从源码角度分析ViewDragHelper

来源:互联网 发布:代谢综合征基因组数据 编辑:程序博客网 时间:2024/05/20 09:26

最近群里的小伙伴都在说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 0
原创粉丝点击