ViewDragHelper(二)- 源码及原理解读(进阶篇)

声明:本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

本篇为该系列的第二篇,侧重讲解ViewDragHelper 的实现原理和源码逻辑,以及它所提供的Callback。

目录

ViewDragHelper 的介绍以及初步使用请阅读这篇:
ViewDragHelper (一)- 介绍及简单用例(入门篇)
ViewDragHelper 的源码以及Callback的详情介绍请阅读这篇:
ViewDragHelper (二)- 源码及原理解读(进阶篇)
利用DrageHelper 打造仿陌陌APP视频播放页的demo请阅读这篇:
ViewDragHelper (三)- 打造仿陌陌视频播放页(深入篇)

一、 UML 类图及流程图

1.1 ViewDragHelper的UML类图如下所示:

图1-1. UML类图

在使用ViewDragHelper过程中,主要涉及到如下四个类:
- MyDraggableView
我们自定义的ViewGroup类。
- ViewDragHelper
帮助类,是我们本篇文章主要分析的对象。
- Callback
ViewDragHelper的内部抽象静态类,主要用于事件处理结果的回调及事件监听。
- DraggableViewCallback
继承于Callback,是它的实现类,ViewDragHelper里面处理的事件,我们可以通过该实现类进行监听回调。

1.2 ViewDragHelper的事件流程图如下所示:

图1-2. MotionEvent事件流程图

MotionEvent事件是从上往下传递的,如果其中的一个onInterceptTouchEvent返回了true,则表示该View拦截此事件系列,此后的MOVE,UP都不会再调用onInterceptTouchEvent,而是会直接调用自己的onTouchEvent方法。

第一篇文章里面提及的,我们自定义的ViewGroup控件的 onInterceptTouchEvent 方法,是通过 viewDragHelper.shouldInterceptTouchEvent(ev) 方法的返回值来决定是否拦截,当它返回 true 时,会直接触发该类自己的onTouchEvent方法;在onTouchEvent事件里面通过viewDragHelper 的 processTouchEvent(ev) 方法,将MotionEvent传递给viewDragHelper 内部,让viewDragHelper 对事件进行分析处理。以上就是在使用viewDragHelper时,事件分发的大概流程以及它的处理过程了,接下来将分析我们在onTouch 方法里将事件传递给viewDragHelper之后 ,它内部是如何对事件进行分析处理的。

本文由于篇幅关系,重点讲解的是以下几个部分:
1. 抽象内部静态类 ViewDrageHelper .Callback。
2. ViewDrageHelper 内部部分源码逻辑。
3. VelocityTracker。
4. ScrollerCompat。

二、ViewDragHelper源码

由UML类图我们不难看出,ViewDragHelper 是在我们自定义ViewGroup类的构造方法中初始化的,而Callback 是一个ViewDrageHelper 的内部静态抽象类。在创建ViewDragHelper 对象时,我们需要传入一个继承自Callback 的实现类实例对象进去。下面我们一步一步来剖析它的内部逻辑。

2.1 构造器
 /**
     * Apps should use ViewDragHelper.create() to get a new instance.
     * This will allow VDH to use internal compatibility implementations for different
     * platform versions.
     *
     * @param context Context to initialize config-dependent params from
     * @param forParent Parent view to monitor
     */
    private ViewDragHelper(Context context, ViewGroup forParent, Callback cb)

由以上源码我们看到,它的构造器是私有的,也就是说我们并不能直接在外部通过new ViewDragHelper()的方式来创建对象。那么我们需要如何创建一个新的ViewDragHelper对象呢?不急,我们接着往下看。

2.2 创建对象

我们贴上关于创建对象以及初始化相关的完整源代码,其实,通过构造方法上面的英文注释可以知道,Google提供了两个工厂方法,让开发者去创建一个新的ViewDragHelper对象。如下所示:

  1. create(ViewGroup forParent, Callback cb)
    该方法在return 时,利用构造器创建了一个新的ViewDragHelper实例。

  2. create(ViewGroup forParent, float sensitivity, Callback cb)
    该方法内部,先调用了第一个工厂方法,得到新ViewDragHelper实例,之后又初始化了 mTouchSlop、mMaxVelocity 、mMinVelocity 、mScroller 等数据和对象。

不难看出含有sensitivity 这个参数的create方法,内部也是调用了create(forParent, cb)方法,只是它对mTouchSlop做了一下处理,传入的灵敏度(sensitivity值)越大,mTouchSlop的值越小。假设当前手机的系统mTouchSlop 大小为24dp, 若我们传入的sensitivity = 3.0f ,则mTouchSlop = 8 dp,即单次滑动距离超过8dp,就会触发系统的 MOVE事件。它的源码如下:


    /**
     * Factory method to create a new ViewDragHelper.
     *
     * @param forParent Parent view to monitor
     * @param cb Callback to provide information and receive events
     * @return a new ViewDragHelper instance
     */
    public static ViewDragHelper create(ViewGroup forParent, Callback cb) {
        return new ViewDragHelper(forParent.getContext(), forParent, cb);
    }

    /**
     * Factory method to create a new ViewDragHelper.
     *
     * @param forParent Parent view to monitor
     * @param sensitivity Multiplier for how sensitive the helper should be about detecting
     *                    the start of a drag. Larger values are more sensitive. 1.0f is normal.
     * @param cb Callback to provide information and receive events
     * @return a new ViewDragHelper instance
     */
    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;
    }

    /**
     * Apps should use ViewDragHelper.create() to get a new instance.
     * This will allow VDH to use internal compatibility implementations for different
     * platform versions.
     *
     * @param context Context to initialize config-dependent params from
     * @param forParent Parent view to monitor
     */
    private ViewDragHelper(Context context, ViewGroup forParent, Callback cb) {
        if (forParent == null) {
            throw new IllegalArgumentException("Parent view may not be null");
        }
        if (cb == null) {
            throw new IllegalArgumentException("Callback may not be null");
        }

        mParentView = forParent;
        mCallback = cb;

        final ViewConfiguration vc = ViewConfiguration.get(context);
        final float density = context.getResources().getDisplayMetrics().density;
        mEdgeSize = (int) (EDGE_SIZE * density + 0.5f);

        mTouchSlop = vc.getScaledTouchSlop();
        mMaxVelocity = vc.getScaledMaximumFlingVelocity();
        mMinVelocity = vc.getScaledMinimumFlingVelocity();
        mScroller = ScrollerCompat.create(context, sInterpolator);
    }
2.3 滑动相关
smoothSlideViewTo方法

该方法用于平顺地滑动控件到指定位置。 child代表子控件对象, finalLeft代表滑动结束时,子控件左边所处的位置, finalTop 代表子控件顶部的位置。

那么,smoothSlideViewTo方法内部做了哪些操作呢?下面我们来看一看源代码:

    public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) {
        mCapturedView = child;
        mActivePointerId = INVALID_POINTER;

        boolean continueSliding = forceSettleCapturedViewAt(finalLeft, finalTop, 0, 0);
        if (!continueSliding && mDragState == STATE_IDLE && mCapturedView != null) {
            mCapturedView = null;
        }

        return continueSliding;
    }

我们可以看到,它是一个布尔型的方法,如果此方法返回 true,则我们应该调用continueSettling方法让它继续滑动,直到返回false,这次滑动才算完成。

settleCapturedViewAt方法

该方法是以松手前的滑动速度为初值,让捕获到的子View自动滑动到指定位置,它只能在Callback的onViewReleased()中使用,若mReleaseInProgress不为True,则会抛出IllegalStateException异常。传递的两个参数分别是结束时子控件的位置,其内部最终调用的是forceSettleCapturedViewAt 方法。

    public boolean settleCapturedViewAt(int finalLeft, int finalTop) {
        if (!mReleaseInProgress) {
            throw 
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值