您的位置:首页 > 其它

从源码角度分析ViewDragHelper

2016-11-26 16:25 513 查看
最近群里的小伙伴都在说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对象。

好了。应该没什么要说了的把?如果还有什么疑问,可以提出来,一起讨论。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: