从源码角度分析ViewDragHelper

最近群里的小伙伴都在说ViewDragHelper这玩意,我就感觉好像很牛逼的样子。然后稍微看了下,不是很难,在此先做个笔记。因为之前他们说scroller的时候,我都不知道是啥。然后今天发现我去年写的demo中还用到了。原谅我猪一般的记性!!
先来个测试demo的效果图。
这里写图片描述
下面直接上代码:


/**
 * Created by Angel on 2016/11/26.
 */
public class ViewDragHelperLayout extends LinearLayout {

    private ViewDragHelper helper;

    public ViewDragHelperLayout(Context context) {
        super(context);
        inital();
    }

    public ViewDragHelperLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        inital();
    }

    private void inital() {
        helper = ViewDragHelper.create(this, 1.0f, new ViewDragCallback());
    }

    private class ViewDragCallback extends ViewDragHelper.Callback {

        public boolean tryCaptureView(View view, int id) {
            return true;
        }

        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
        }

        public void onViewCaptured(View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
        }

        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //让我们的视图不越界
            int paddingleft = getPaddingLeft();
            int view_width = child.getWidth();
            int view_left = getWidth() - view_width - paddingleft;
            int new_left = Math.min(Math.max(paddingleft, left), view_left);
            return new_left;
        }

        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;
        }
    }

    public boolean onInterceptTouchEvent(MotionEvent event) {
        return helper.shouldInterceptTouchEvent(event);
    }

    public boolean onTouchEvent(MotionEvent event) {
        helper.processTouchEvent(event);
        return true;
    }
}

好了,回归整体,从源码角度开始分析,接下来要放大招了~~~
ViewDragHelper并不强大,强大的是他有一个回调函数
我们点开ViewDragHelper的源码,找到他的回调:

 public abstract static class Callback {
        public Callback() {
        }

        public void onViewDragStateChanged(int state) {
        }

        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
        }

        public void onViewCaptured(View capturedChild, int activePointerId) {
        }

        public void onViewReleased(View releasedChild, float xvel, float yvel) {
        }

        public void onEdgeTouched(int edgeFlags, int pointerId) {
        }

        public boolean onEdgeLock(int edgeFlags) {
            return false;
        }

        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
        }

        public int getOrderedChildIndex(int index) {
            return index;
        }

        public int getViewHorizontalDragRange(View child) {
            return 0;
        }

        public int getViewVerticalDragRange(View child) {
            return 0;
        }

        public abstract boolean tryCaptureView(View var1, int var2);

        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return 0;
        }

        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;
        }
    }

接下来,我们分析一下他们分别是什么时候才会调用的。

onViewDragStateChanged

当ViewDragHelper状态变更时回调该方法

onViewPositionChanged

当捕获view由于拖曳或者设定而发生位置变更时回调

onViewCaptured

当子view被由于拖曳而被捕获时回调的方法.

onViewReleased

手指释放的时候回调

onEdgeTouched

当触摸到边界时回调。

onEdgeLock

true的时候会锁住当前的边界,false则unLock。

onEdgeDragStarted

在边界拖动时回调

getOrderedChildIndex

改变同一个坐标(x,y)去寻找captureView位置的方法。

getViewHorizontalDragRange
getViewVerticalDragRange

这两个的返回值大于0时才可捕获

tryCaptureView

是否捕捉该view的滚动,id代表捕捉某一个view的滚动

clampViewPositionHorizontal

左右滑动时调用

clampViewPositionVertical

上下滑动时调用

到这边,应该所有的方法都介绍完了。好了,现在我们来聊聊,ViewDragHelper的方法。
创建一个ViewDragHelper对象

 helper.create(forParent, cb);
 helper.create(forParent, sensitivity, cb);

至于有小伙伴对sensitivity这个属性有疑问,那么我们点开源码来了解下他是干嘛的。

public static ViewDragHelper create(ViewGroup forParent, float sensitivity, ViewDragHelper.Callback cb) {
        ViewDragHelper helper = create(forParent, cb);
        helper.mTouchSlop = (int)((float)helper.mTouchSlop * (1.0F / sensitivity));
        return helper;
    }

我们传入的值越大,他的值越小。但mTochSlop是怎么计算的呢,我们继续看源码:

 private ViewDragHelper(Context context, ViewGroup forParent, ViewDragHelper.Callback cb) {
        if(forParent == null) {
            throw new IllegalArgumentException("Parent view may not be null");
        } else if(cb == null) {
            throw new IllegalArgumentException("Callback may not be null");
        } else {
            this.mParentView = forParent;
            this.mCallback = cb;
            ViewConfiguration vc = ViewConfiguration.get(context);
            float density = context.getResources().getDisplayMetrics().density;
            this.mEdgeSize = (int)(20.0F * density + 0.5F);
            this.mTouchSlop = vc.getScaledTouchSlop();
            this.mMaxVelocity = (float)vc.getScaledMaximumFlingVelocity();
            this.mMinVelocity = (float)vc.getScaledMinimumFlingVelocity();
            this.mScroller = ScrollerCompat.create(context, sInterpolator);
        }
    }

我们定位到了这个:vc.getScaledTouchSlop();这个属性是用来干嘛的呢。我查了下。解释如下:
getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件。
现在不要我多说了把。就是你一次最少滑动的距离要大于这个距离,否则,视图是不会移动了。
好了,现在看我们之前的demo还用到了什么。

helper.shouldInterceptTouchEvent(event);

继续看源码:

public boolean shouldInterceptTouchEvent(MotionEvent ev) {      
    ...
    ...
    ...
  return mDragState == 1;
}

等于1是什么鬼 ?找方法啊。经过翻山越岭我们终于找到了。

public void captureChildView(View childView, int activePointerId) {
        if(childView.getParent() != this.mParentView) {
            throw new IllegalArgumentException("captureChildView: parameter must be a descendant of the ViewDragHelper\'s tracked parent view (" + this.mParentView + ")");
        } else {
            this.mCapturedView = childView;
            this.mActivePointerId = activePointerId;
            this.mCallback.onViewCaptured(childView, activePointerId);
            this.setDragState(1);
        }
    }

哎呀呀,这是什么吊东西。说白了。就是它的父view是同一个的时候执行。否则直接抛异常咯。
接下来就是:

 helper.processTouchEvent(event);

经过我几般周折。终于了解了一丢丢。这是加工从父view中获取的触摸事件。这个方法将分发callback回调事件。父view的触摸事件实现中应该调用该方法。说白了,这是处理ontouch事件的~~~
好了。稍微介绍下ViewDragHelper的原理和几个重点:
1.ViewDragHelper用于监听整个拖拽事件的开始到结束,过程分三步:休息,开始拖动,结束拖动。
2.ViewDragHelper的拖拽是通过Scoller实现的。
3.ViewDragHelper是有保存历史记录的。例如我从(0,0)滚动到(100,0),那么下次滚动肯定是从后者继续滚动,而不是前者了。
4。 有历史记录,当然也有清空记录的功能,ViewDragHelper提供了cancel()方法,类似onTouch的ACTION_UP事件。当拖曳结束,可能系统还认为过程还在,因此就需要提供的cancel()或abort()方法去终止这个过程,同时也自动调用clearMotionHistory()方法,置空历史记录。确保下次触摸拖曳事件是”新的开始”。
5.ViewDragHelper的拖拽事件是根据父view的最顶层的子view才会响应事件。所以该类提供了public View findTopChildUnder(int x, int y)方法捕获父view中最顶层的子view对象。
好了。应该没什么要说了的把?如果还有什么疑问,可以提出来,一起讨论。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值