Google在其support库中为我们提供了DrawerLayout和SlidingPaneLayout两个布局来帮助开发者实现侧边栏滑动的效果。这两个新的布局,大大方便了我们创建自己的滑动布局界面。
然而,这两个功能强大的布局背后,却隐藏着一个鲜为人知却功能强大的类-------ViewDragHelper。通过ViewDargHelper,基本可以实现不同的滑动、拖动需求,因此这个方法也是各种解决方案中的终极绝招。
我们这里就实现一个 QQ滑动侧边栏的布局:
1.初始化ViewDragHelper
首先,进行初始化。ViewDragHelper通常定义在一个ViewGroup的内部,并通过其静态工厂方法进行初始化
ViewDragHelper.create(this, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return false;
}
})
它的第一个参数是要监听的View,通常是一个ViewGroup,第二个参数是一个CallBack回调,这个回调就是ViewDragHelper的逻辑核心。
2.拦截事件
接下来,要重写事件拦截方法,将事件传递给ViewDragHelper进行处理
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mvViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//将触摸事件传递给ViewDragHelper,此操作必不可少
mvViewDragHelper.processTouchEvent(event);
return true;
}
3.处理computeScroll()
使用ViewDragHelper同样需要重写computescroll()方法,因为ViewDragHelper内部也是通过Scroll来实现平滑移动的。
@Override
public void computeScroll() {
if(mViewDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
4.处理回调Cakkback
//侧滑回调
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//何时开始触摸
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView开始检测
return mMainView == child;
}
//处理水平滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
//处理垂直滑动
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
//拖动结束后调用
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起后缓慢的移动到指定位置
if(mMainView.getLeft() <500){
//关闭菜单
mViewDragHelper.smoothSlideViewTo(mMainView,0,0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}else{
//打开菜单
mViewDragHelper.smoothSlideViewTo(mMainView,300,0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
下面是完整实例:
//XML加载组建后回调
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMenuView = getChildAt(0);
mMainView = getChildAt(1);
}
//组件大小改变时回调
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = mMenuView.getMeasuredWidth();
}
/**
* QQ侧滑
* Created by lgl on 16/3/22.
*/
public class DragViewGroup extends FrameLayout{
//侧滑类
private ViewDragHelper mViewDragHelper;
private View mMenuView,mMainView;
private int mWidth;
public DragViewGroup(Context context) {
super(context);
initView();
}
public DragViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public DragViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
//初始化数据
private void initView() {
mViewDragHelper = ViewDragHelper.create(this,callback);
}
//XML加载组建后回调
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMenuView = getChildAt(0);
mMainView = getChildAt(1);
}
//组件大小改变时回调
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = mMenuView.getMeasuredWidth();
}
//事件拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
//触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
//将触摸事件传递给ViewDragHelper
mViewDragHelper.processTouchEvent(event);
return true;
}
//侧滑回调
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//何时开始触摸
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView开始检测
return mMainView == child;
}
//处理水平滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
//处理垂直滑动
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
//拖动结束后调用
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起后缓慢的移动到指定位置
if(mMainView.getLeft() <500){
//关闭菜单
mViewDragHelper.smoothSlideViewTo(mMainView,0,0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}else{
//打开菜单
mViewDragHelper.smoothSlideViewTo(mMainView,300,0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
@Override
public void computeScroll() {
if(mViewDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
}
当然,这里只是非常简单的模拟了一下,在Cakkback中系统给我们提供了很多的方法来监听
onViewCaptured
//用户触摸到view回调
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
onViewDragStateChanged
//拖拽状态改变时,比如idle,dragging
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
}
onViewPositionChanged
//位置发生改变,常用语滑动scale效果
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
}