文档解释
ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.
也就是用来简化自定义ViewGroup拖拽操作的工具类,一些系统提供的Widget如SlidingPaneLayout、DrawerLayout都用到了ViewDragHelper处理拖动。
三个View均在屏幕范围内滑动,第一个是常规拖动,第二个只有在拖动左侧屏幕边缘时才滑动,第三个展示的功能是拖动停止后的滑动。
自定义的DragLinearLayout代码如下:
public classDragLinearLayoutextendsLinearLayout{ private ViewDragHelper mDragHelper; private View mDrawerNormalView; private View mDrawerEdgeView; private View mDrawerReleaseView; private Point mOriginPoint; publicDragLinearLayout(Context context, AttributeSet attrs,intdefStyle){ super(context, attrs, defStyle); //实例化ViewDragHelper:1.0f为拖拽敏感系数,值越大拖拽灵敏度越高 mDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelperCallBack()); //设置屏幕左侧边缘支持拖拽 mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); mOriginPoint = new Point(); } public classViewDragHelperCallBackextendsViewDragHelper.Callback{ @Override publicbooleantryCaptureView(View child,intpointerId){ //如果想让某个view支持拖拽,在此处对应的child处返回true return mDrawerNormalView == child || child == mDrawerReleaseView; } @Override publicintclampViewPositionVertical(View child,inttop,intdy){ //子View在y轴方向上应该移动到的位置,三个参数:要拖拽的子View实例、期望的移动后位置子View的top值、移动的距离。 //返回值为子View在最终位置时的top值 int topLimit = Math.max(0, top); return Math.min(getHeight() - child.getHeight(), topLimit); } @Override publicintclampViewPositionHorizontal(View child,intleft,intdx){ int leftLimit = Math.max(0, left); return Math.min(getWidth() - child.getWidth(), leftLimit); } @Override publicvoidonEdgeTouched(intedgeFlags,intpointerId){ //滑动左侧边缘时,主动通过captureChildView进行捕获,虽然tryCaptureView并未对mDrawerEdgeView返回true,但依旧能捕获 if (edgeFlags == ViewDragHelper.EDGE_LEFT) { mDragHelper.captureChildView(mDrawerEdgeView, pointerId); } } @Override publicvoidonViewReleased(View releasedChild,floatxvel,floatyvel){ //拖动mDrawerReleaseView手指释放时,让其回到原来位置 // 注意:settleCapturedViewAt内部是Scroller机制,所以要通过invalidate() + computeScroll方法才能实现释放后的滑动。 // 在computeScroll方法中判断滑动是否结束调用mDragHelper.continueSettling(true),此方法内部调用了mScroller.computeScrollOffset() if (releasedChild == mDrawerReleaseView) { mDragHelper.settleCapturedViewAt(mOriginPoint.x, mOriginPoint.y); invalidate(); } } } @Override publicvoidcomputeScroll(){ //响应settleCapturedViewAt() if (mDragHelper.continueSettling(true)) { invalidate(); } } @Override publicbooleanonInterceptTouchEvent(MotionEvent ev){ //交给DragHelper处理 return mDragHelper.shouldInterceptTouchEvent(ev); } @Override publicbooleanonTouchEvent(MotionEvent event){ //交给DragHelper处理 mDragHelper.processTouchEvent(event); return true; } @Override protectedvoidonFinishInflate(){ super.onFinishInflate(); mDrawerNormalView = findViewById(R.id.drawer_normal); mDrawerEdgeView = findViewById(R.id.drawer_edge); mDrawerReleaseView = findViewById(R.id.drawer_release); } @Override protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){ super.onLayout(changed, l, t, r, b); mOriginPoint.x = mDrawerReleaseView.getLeft(); mOriginPoint.y = mDrawerReleaseView.getTop(); } publicDragLinearLayout(Context context){ this(context, null); } publicDragLinearLayout(Context context, AttributeSet attrs){ this(context, attrs, 0); } }
代码中注释比较详细,就不再解释了。另外,DragHelper还有对应fling的方法flingCapturedView(),其它smoothSlideViewTo()方法等。
如果子view是Button或者具有Clickable的其它widget,就无法再次拖动了,因为此时子view抢去了事件的处理。简单的处理可以在覆盖CallBack中的如下方法:
@Override publicintgetViewHorizontalDragRange(View child){ return 1; } @Override publicintgetViewVerticalDragRange(View child){ return 1; }
返回大于0即可,当子view抢去事件处理后,ViewDragHelper会进一步通过这两个方法的返回值来capture view。
如果子view是类似ScrollView的控件,可以通过重写ViewGroup的onInterceptTouchEvent来处理抢事件问题。
@Override publicbooleanonInterceptTouchEvent(MotionEvent ev){ //交给DragHelper处理 boolean helper = mDragHelper.shouldInterceptTouchEvent(ev); boolean result = false; switch (MotionEventCompat.getActionMasked(ev)) { case MotionEvent.ACTION_DOWN: mInitXpos = ev.getX(); mInitYpos = ev.getY(); break; case MotionEvent.ACTION_MOVE: if (Math.abs(ev.getX() - mInitXpos) - Math.abs(ev.getY() - mInitYpos) > ViewConfiguration.getTouchSlop()) { result = true; mDragHelper.captureChildView(mDrawerScrollView, ev.getPointerId(0)); } break; } return helper || result; }
这里只是做了简单的使用说明,原理还需要查看源码的实现~
详细代码: https://github.com/edreamoon/FLibrary/blob/develop/edreamoon/src/main/java/com/edreamoon/customview/DragLinearLayout.java