文档解释
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