RecyclerView 源码分析(二)RecyclerView的滑动机制

转载自琼珶和予

RecyclerView的滑动机制

RecyclerView作为一个列表View,天生就可以滑动。作为一个使用者,我们可以不去了解它是怎么进行滑动,但是我们作为一个学习源码的人,必须得知道RecyclerView的滑动机制,所以,我们今天来看看RecyclerView滑动部分的代码。
  
本文参考资料:
Android 源码分析 - 嵌套滑动机制的实现原理
深入 RecyclerView 源码探究三:绘制和滑动
  
同时,从RecyclerView的类结构上来看,我们知道RecyclerView实现了NestedScrollingChild接口,所以RecyclerView也是一个可以产生滑动事件的View。我相信大家都有用过CoordinatorLayout和RecyclerView这个组合,这其中原理的也是嵌套滑动。本文在介绍普通滑动中,可能会涉及到嵌套滑动的知识,所以在阅读本文时,需要大家掌握嵌套滑动的机制,具体可以参考我上面的文章:Android 源码分析 - 嵌套滑动机制的实现原理,此文专门从RecyclerView的角度上来理解嵌套滑动的机制。
  
本文打算从如下几个方面来分析RecyclerView:

  1. 正常的TouchEvent
  2. 嵌套滑动(穿插着文章各个地方,不会专门的讲解)
  3. 多指滑动
  4. fling滑动

1. 传统事件

现在,我们正式分析源码,首先我们来看看onTouchEvent方法,来看看它为我们做了那些事情:

@Override
public boolean onTouchEvent(MotionEvent e) {
   
    // ······
    if (dispatchOnItemTouch(e)) {
   
        cancelTouch();
        return true;
    }
    // ······
    switch (action) {
   
        case MotionEvent.ACTION_DOWN: {
   
            // ······
        } break;

        case MotionEvent.ACTION_POINTER_DOWN: {
   
            // ······
        } break;

        case MotionEvent.ACTION_MOVE: {
   
            // ······
        } break;

        case MotionEvent.ACTION_POINTER_UP: {
   
            // ······
        } break;

        case MotionEvent.ACTION_UP: {
   
            // ······
        } break;

        case MotionEvent.ACTION_CANCEL: {
   
            cancelTouch();
        } break;
    }
    // ······
    return true;
}

如上就是RecyclerView的onTouchEvent方法,我大量的简化了这个方法,先让大家对它的结构有一个了解。
  
其中ACTION_DOWN、ACTION_MOVE、ACTION_UP和ACTION_CANCEL这几个事件,我相信各位同学都比较熟悉,这是View最基本的事件。
  
可能有人对ACTION_POINTER_DOWN和ACTION_POINTER_UP事件比较陌生,这两个事件就跟多指滑动有关,也是本文重点分析之一。

好了,我们现在开始正式分析源码。在分析源码之前,我先将上面的代码做一个简单的概述。

  1. 如果当前的mActiveOnItemTouchListener需要消耗当前事件,那么优先交给它处理
  2. 如果mActiveOnItemTouchListener不消耗当前事件,那么就走正常的事件分发机制。这里面有很多的细节,稍后我会详细的介绍。
      
    关于第一步,这里不用我来解释,它就是一个Listener的回调,非常的简单,我们重点的在于分析第二步。

(1) Down 事件

我们先来看看这部分的代码吧。

case MotionEvent.ACTION_DOWN: {
   
    mScrollPointerId = e.getPointerId(0);
    mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
    mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);

    int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
    if (canScrollHorizontally) {
   
        nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
    }
    if (canScrollVertically) {
   
        nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
    }
    startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
} break;

这里主要是做了两件事。

  1. 记录下Down事件的x、y坐标
  2. 调用startNestedScroll方法,询问父View是否处理事件

Down事件还是比较简单,通常来说就一些初始化的事情。
  
接下来,我们来看看重头戏–move事件

(2) Move事件

我们先来看看这部分的代码:

case MotionEvent.ACTION_MOVE: {
   
    final int index = e.findPointerIndex(mScrollPointerId);
    if (index < 0) {
   
        Log.e(TAG, "Error processing scroll; pointer index for id "
                + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
        return false;
    }

    final int x = (int) (e.getX(index) + 0.5f);
    final int y = (int) (e.getY(index) + 0.5f);
    int dx = mLastTouchX - x;
    int dy = mLastTouchY - y;

    if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
   
        dx -= mScrollConsumed[0];
        dy -= mScrollConsumed[1];
        vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
        // Updated the nested offsets
        mNestedOffsets[0] += mScrollOffset[0];
        mNestedOffsets[1] += mScrollOffset[1];
    }

    if (mScrollState != SCROLL_STATE_DRAGGING) {
   
        boolean startScroll = false;
        if 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值