Android应用ViewDragHelper详解及部分源码浅析,kotlin版权

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我

3 ViewDragHelper局部源码浅析

==========================

上面的例子中我们可以知道,使用ViewDragHelper的第一步就是通过他提供的静态工厂方法create获取实例,因为ViewDragHelper的构造方法是私有的。既然这样那我们先看下这些静态工厂方法,如下:

public class ViewDragHelper {

public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) {

final ViewDragHelper helper = create(forParent, cb);

helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));

return helper;

}

public static ViewDragHelper create(ViewGroup forParent, Callback cb) {

return new ViewDragHelper(forParent.getContext(), forParent, cb);

}

}

可以看见,三个参数的create方法实质调运的还是两个参数的create。其中forParent一般是我们自定义的ViewGroup,cb是控制子View相关状态的回调抽象类实现对象,sensitivity是用来调节mTouchSlop的,至于mTouchSlop是啥以及sensitivity的作用下面会有解释。接着可以发现两个参数的create实质是调运了ViewDragHelper的构造函数,那我们就来分析一下这个构造函数,如下源码:

private ViewDragHelper(Context context, ViewGroup forParent, Callback cb) {

//对参数进行赋值

mParentView = forParent;

mCallback = cb;

//通过ViewConfiguration等将dp转px得到mEdgeSize

final ViewConfiguration vc = ViewConfiguration.get(context);

final float density = context.getResources().getDisplayMetrics().density;

mEdgeSize = (int) (EDGE_SIZE * density + 0.5f);

//通过ViewConfiguration获取TouchSlop,默认为8

mTouchSlop = vc.getScaledTouchSlop();

//获得允许执行一个fling手势动作的最大速度值

mMaxVelocity = vc.getScaledMaximumFlingVelocity();

//获得允许执行一个fling手势动作的最小速度值

mMinVelocity = vc.getScaledMinimumFlingVelocity();

//通过兼容包的ScrollerCompat实例化Scroller,动画插值器为sInterpolator

mScroller = ScrollerCompat.create(context, sInterpolator);

}

可以看见,构造函数其实没有做啥特别的事情,主要就是一些参数的实例化,最主要的就是实例化了一个Scroller的内部成员,而且还在ViewDragHelper中重写了插值器,如下:

private static final Interpolator sInterpolator = new Interpolator() {

public float getInterpolation(float t) {

t -= 1.0f;

return t * t * t * t * t + 1.0f;

}

};

关于动画插值器这里不再说了,之前博文有讲过。我们还是把视线回到上面实例部分,可以看见,在获取ViewDragHelper实例之后我们接着重写了ViewGroup的onInterceptTouchEvent和onTouchEvent方法,在其中触发了ViewDragHelper的shouldInterceptTouchEvent和processTouchEvent方法。所以下面我们就来分析这两个方法,首先我们看下shouldInterceptTouchEvent方法,如下:

//这玩意返回值的作用在前面博客中有分析,我们先来看下ACTION_DOWN事件

public boolean shouldInterceptTouchEvent(MotionEvent ev) {

final int action = MotionEventCompat.getActionMasked(ev);

final int actionIndex = MotionEventCompat.getActionIndex(ev);

if (action == MotionEvent.ACTION_DOWN) {

//每次ACTION_DOWN都会调用cancel(),该方法中mVelocityTracker被清空,故mVelocityTracker记录的是本次ACTION_DOWN到ACTION_UP的触摸信息

cancel();

}

//获取VelocityTracker实例,记录下各个触摸点信息用来计算本次滑动速率等

if (mVelocityTracker == null) {

mVelocityTracker = VelocityTracker.obtain();

}

mVelocityTracker.addMovement(ev);

switch (action) {

case MotionEvent.ACTION_DOWN: {

final float x = ev.getX();

final float y = ev.getY();

final int pointerId = MotionEventCompat.getPointerId(ev, 0);

//Step 1

saveInitialMotion(x, y, pointerId);

//Step 2

final View toCapture = findTopChildUnder((int) x, (int) y);

//Step 3

if (toCapture == mCapturedView && mDragState == STATE_SETTLING) {

tryCaptureViewForDrag(toCapture, pointerId);

}

//Step 4

final int edgesTouched = mInitialEdgesTouched[pointerId];

if ((edgesTouched & mTrackingEdges) != 0) {

mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);

}

break;

}

//暂时忽略

}

//Step 5

return mDragState == STATE_DRAGGING;

}

可以看见,上面代码我们只列出了shouldInterceptTouchEvent关于ACTION_DOWN部分的代码,在注释里我将他分为了5步来叙述。我们先来看当ACTION_DOWN触发时Step 1的代码,他通过saveInitialMotion(x, y, pointerId)保存了事件的初始信息,如下是saveInitialMotion方法代码:

private void saveInitialMotion(float x, float y, int pointerId) {

ensureMotionHistorySizeForId(pointerId);

mInitialMotionX[pointerId] = mLastMotionX[pointerId] = x;

mInitialMotionY[pointerId] = mLastMotionY[pointerId] = y;

//getEdgesTouched就是通过mEdgeSize去判断触摸边沿方向是否OK

mInitialEdgesTouched[pointerId] = getEdgesTouched((int) x, (int) y);

mPointersDown |= 1 << pointerId;

}

接着目光再回到shouldInterceptTouchEvent方法的Step 2,可以发现他尝试通过findTopChildUnder()方法来获取当前触摸点下最顶层的子View,我们可以看下这个方法的源码,如下:

public View findTopChildUnder(int x, int y) {

//获取mParentView中子View个数

final int childCount = mParentView.getChildCount();

//倒序遍历整个子View,因为最上面的子View最后插入

for (int i = childCount - 1; i >= 0; i–) {

//遍历拿到最靠上且获得触摸焦点的那个子View

final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));

//判断当前DOWN的触摸点是否在该子View范围,也就是说是不是摸上了该子View

if (x >= child.getLeft() && x < child.getRight() &&

y >= child.getTop() && y < child.getBottom()) {

return child;

}

}

return null;

}

这个方法你不觉的奇怪么&#x

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值