在众多的App中都用到了SlidingMenu,使用SlidingMenu不仅可以增加显示的内容,也让用户体验更多的舒适,当然现在github上有相应的SlidingMenu的库在其它的博客也有很多的关于SlidingMenu的介绍,也当然此文章也会有很多和别人重复,但是相信坚持总结、坚持学习,总有一天能写出不一样的文章,fighting!!
*/
首先附上github上第三方的库
https://github.com/jfeinstein10/SlidingMenu
然后开始我们的自定义SlidingMenu类之旅,在开始之前我们先明确自定义类继承什么呢?要是使用ViewGroup的话会比较麻烦因为你需要在类里面进行测量,在很多自定义的控件中都会继承其子类来实现相应的功能例如FrameLayout和HorizontalScrollView等等。
Part 1、继承HorizontalScrollView实现
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
int dx = getScrollX();//(1)
if (dx > menuWidth / 2) {
smoothScrollTo(menuWidth, 0);
} else {
smoothScrollTo(0, 0);
}
return true;//(2)
}
return super.onTouchEvent(ev);
}
tips:
这里使用的smoothScrollTo而不是scrollTo,纯粹是为了体验更加的平滑。
(1)、getScrollX() 就是当前view的左上角相对于母视图的左上角的X轴偏移量。
(2)、这里应该返回true来屏蔽掉下面的super.onTouchEvent(ev)方法,不然的话就没有任何的效果
ok,效果~
这样简易的侧滑就搞定了~
如果你想要在添加一些酷炫的动画效果的话需要重写
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float scale = l * 1.0f / menuWidth; // 1 ~ 0
float fraction = 1 - scale;
当滚动发生的时候会不断的去执行,里面也有变化量得到的方法,其实要想实现动画效果此时只需要一个变化量那一切动画so easy!
平移缩放等等
ViewHelper.setTranslationX(menuContent, evaluate(fraction,menuWidth*0.5f, 0));
ViewHelper.setScaleX(menuContent, evaluate(fraction, 0.5f, 1.0f));
ViewHelper.setScaleY(menuContent, evaluate(fraction, 0.5f, 1.0f));
最后在加上颜色渐变的效果
getBackground().setColorFilter((Integer) evaluateColor(fraction, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.DST_OUT);
tips:
1、nineoldandroid库
compile 'com.nineoldandroids:library:2.4.0'
2、Android提供的差值器
如:颜色渐变
/**
* 颜色变化过渡
* @param fraction
* @param startValue
* @param endValue
* @return
*/
public Object evaluateColor(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int) ((startA + (int) (fraction * (endA - startA))) << 24) |
(int) ((startR + (int) (fraction * (endR - startR))) << 16) |
(int) ((startG + (int) (fraction * (endG - startG))) << 8) |
(int) ((startB + (int) (fraction * (endB - startB))));
}
ok,效果~
这样就实现了继承HorizontalScrollView的侧滑菜单~
Part 2 继承FrameLayout实现
当然我们也可以通过继承FrameLayout或者ViewGroup来实现,针对上面的侧滑接下来要说的这个大致是一样的只不过增加了一个ViewDragHelper类
dragHelper = ViewDragHelper.create(this, new DragCallBack());
}
class DragCallBack extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
@Override
public int getViewHorizontalDragRange(View child) {
return range;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
// left = oldLeft + dx;
if (child == mainContent) {
left = fixLeft(left);
}
return left;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
int newLeft = left;
if (changedView == leftContent) {
newLeft = mainContent.getLeft() + dx;
}
newLeft = fixLeft(newLeft);
if (changedView == leftContent) {
leftContent.layout(0, 0, mWidth, mHeight);
mainContent.layout(newLeft, 0, newLeft + mWidth, mHeight);
}
//执行动画
animPerform(newLeft);
// 为了兼容低版本, 每次修改值之后, 进行重绘
invalidate();
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
// View releasedChild 被释放的子View
// float xvel 水平方向的速度, 向右为+
// float yvel 竖直方向的速度, 向下为+
// 判断执行 关闭/开启
if (xvel == 0 && mainContent.getLeft() > range / 2.0f) {
open();
} else if (xvel > 0) {
open();
} else {
close();
}
}
tips:
1、
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
根据返回值来决定当前的child是否可以拖拽,child:当前拖拽的View;poterId:多点触控的id
2、
public void onViewCaptured(View capturedChild, int activePointerId) {
当child被捕获时,基本上没有多大用可以忽略
3、
@Override
public int getViewHorizontalDragRange(View child) {
return range;
}
得到水平拖拽的范围,这个值不会实际起作用,通过查看源码可知只是在滑动速度上有一定的作用
4、
public int clampViewPositionHorizontal(View child, int left, int dx) {
根据建议值修正要移动的位置,left=oldleft+dx; 注意:此时还没有发生移动
5、
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
当View位置发生改变的时候要处理的逻辑(动画、重绘界面等) 注意:此时已经发生移动
6、
public void onViewReleased(View releasedChild, float xvel, float yvel) {
当View被释放的时候执行此方法,可以在这里面执行一些动画
创建了ViewDragHelper之后,只需要在onTouch和onIntercept方法里面将事件授权给ViewDragHelper
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 传递给mDragHelper
return dragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
dragHelper.processTouchEvent(event);
return true;
}
这样也可以进行滑动了,但是通过滑动你会发现效果并不是像上面那样平滑,原因是上面HorizontalScrollView里面有smoothScrollTo方法,而在ViewGroup或FrameLayout里面却没有,为了也实现平滑的移动应使用Scroller类,由于ViewDragHelper类里面有Scroller,所以只需要使用ViewDragHelper提供的方法即可
if (dragHelper.smoothSlideViewTo(mainContent, 0, 0)) {
//ViewCompat.postInvalidateOnAnimation(this);
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (dragHelper.continueSettling(true)) {
// 如果返回true, 动画还需要继续执行
//ViewCompat.postInvalidateOnAnimation(this);
invalidate();
}
}
tips:
public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) {
参数一看便知,这里调用invalidate方法,实际内部是回调了computeScroll()方法,然后通过在computeScroll方法里面调用invalidate实现递归效果从而达到平滑移动的效果。
最后在实现动画效果上和part1是一样的,在onViewPositionChanged方法里面添加即可.
效果~
Part 3 使用android.support.v4.widget.DrawerLayout实现
布局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
tools:context="com.andly.administrator.andlyviewpager.drawer.DrawerActivity">
<include
layout="@layout/hsv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include
layout="@layout/hsv_menu"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_gravity="left"/>
</android.support.v4.widget.DrawerLayout>
为DrawerLayout设置监听事件
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
float fraction = slideOffset;
View mainContent = mDrawerLayout.getChildAt(0);
View menuContent = drawerView;
ViewHelper.setTranslationX(menuContent,evaluate(fraction, menuContent.getMeasuredWidth()/2,0));
ViewHelper.setScaleX(menuContent,evaluate(fraction,0.5f,1.0f));
ViewHelper.setScaleY(menuContent,evaluate(fraction,0.5f,1.0f));
ViewHelper.setScaleY(mainContent,evaluate(fraction,1.0f,0.8f));
ViewHelper.setScaleY(mainContent,evaluate(fraction,1.0f,0.8f));
ViewHelper.setTranslationX(mainContent,evaluate(fraction,0,menuContent.getMeasuredWidth()));
mDrawerLayout.getBackground().setColorFilter((Integer) evaluateColor(fraction,Color.BLACK,Color.TRANSPARENT), PorterDuff.Mode.DST_OUT);
}
@Override
public void onDrawerOpened(View drawerView) {
}
@Override
public void onDrawerClosed(View drawerView) {
//mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
}
@Override
public void onDrawerStateChanged(int newState) {
}
});
tips:
1、
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
关闭向右侧滑的效果
2、
public void onDrawerSlide(View drawerView, float slideOffset) {
当不断滑动的时候将不断的执行此方法,你只需要将需要执行的动画添加到这里即可
效果~