AndroidX RecyclerView总结-滑动处理

概述

RecyclerView作为一个灵活的在有限窗口显示大量数据集的视图组件,继承自ViewGroup,需要处理触摸事件产生时子View的滚动。同时RecyclerView实现了NestedScrollingChild接口,也支持嵌套在支持Nested的父容器中。

这里结合LinearLayoutManager,以垂直方向滑动为例,从源码浅析RecyclerView是如何进行滑动事件处理的。

源码探究

文中源码基于 ‘androidx.recyclerview:recyclerview:1.1.0’

RecyclerView中的处理

RecyclerView和常规事件处理方式一样,重写了onInterceptTouchEventonTouchEvent。RecyclerView也实现了NestedScrollingChild接口,在关键事件节点也会通知实现了NestedScrollingParent接口的父容器。

关于NestedScrollingChild和NestedScrollingParent的简要用法和说明,可参考《关于NestedScrollingParent2、NestedScrollingChild2接口》

onInterceptTouchEvent

[RecyclerView#onInterceptTouchEvent]

public boolean onInterceptTouchEvent(MotionEvent e) {
   
    // 判断是否抑制布局滚动,可通过suppressLayout方法设置为true,当重新设置Adapter或托管item动画时不拦截。
    if (mLayoutSuppressed) {
   
        // When layout is suppressed,  RV does not intercept the motion event.
        // A child view e.g. a button may still get the click.
        return false;
    }

    // 省略OnItemTouchListener部分,设置FastScroller或ItemTouchHelper时涉及 ···

    if (mLayout == null) {
   
        return false;
    }

    // 获取支持滚动的方向。以垂直排列的LinearLayoutManager为例,canScrollVertically为true。
    final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
    final boolean canScrollVertically = mLayout.canScrollVertically();

    if (mVelocityTracker == null) {
   
        mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(e);

    final int action = e.getActionMasked();
    final int actionIndex = e.getActionIndex();

    switch (action) {
   
        case MotionEvent.ACTION_DOWN:
            // mIgnoreMotionEventTillDown默认为false,调用suppressLayout抑制布局滚动时会将其置为true
            if (mIgnoreMotionEventTillDown) {
   
                mIgnoreMotionEventTillDown = false;
            }
            // 获取第一个触摸点的ID
            mScrollPointerId = e.getPointerId(0);
            // 保存DOWN时X、Y坐标,用于计算滑动偏移量
            mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
            mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);

            // 判断当前滑动状态是否是惯性滑动或其他非用户触摸滑动,mScrollState默认为SCROLL_STATE_IDLE
            if (mScrollState == SCROLL_STATE_SETTLING) {
   
                // 请求父布局不拦截事件
                getParent().requestDisallowInterceptTouchEvent(true);
                // 更新滑动状态为SCROLL_STATE_DRAGGING
                setScrollState(SCROLL_STATE_DRAGGING);
                // 通知父布局停止滑动,类型为TYPE_NON_TOUCH
                stopNestedScroll(TYPE_NON_TOUCH);
            }

            // Clear the nested offsets
            mNestedOffsets[0] = mNestedOffsets[1] = 0;

            // 获取当前支持的滑动方向
            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;

        case MotionEvent.ACTION_POINTER_DOWN:
            // 有新的触摸点,更新触摸点ID和初始X、Y坐标以新的为准
            mScrollPointerId = e.getPointerId(actionIndex);
            mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
            mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
            break;

        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);
            // 判断当前滑动状态是否是SCROLL_STATE_DRAGGING
            if (mScrollState != SCROLL_STATE_DRAGGING) {
   
                // 计算滑动偏移量
                final int dx = x - mInitialTouchX;
                final int dy = y - mInitialTouchY;
                // 标记是否有任一方向可以滑动
                boolean startScroll = false;
                // 判断是否构成滑动,mTouchSlop为最小滑动距离
                if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   
                    // 保存刚开始滑动时的坐标
                    mLastTouchX = x;
                    startScroll = 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Android开发中,我们可以使用RecyclerView来实现网格布局,并且可以通过指定的方式滑动到指定的position。 首先,我们需确保已经在项目的build.gradle文件中添加了RecyclerView的依赖项。 在布局文件中,我们将RecyclerView添加到指定的位置。例如,如果我们希望将RecyclerView添加到activity_main.xml文件的某个LinearLayout中,可以使用以下代码: ```xml <LinearLayout ... android:id="@+id/linear_layout" ...> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" ... /> </LinearLayout> ``` 在Activity或Fragment中,我们需要将布局文件中的RecyclerView与代码中的RecyclerView关联起来,并为RecyclerView设置LayoutManager和Adapter。LayoutManager决定了RecyclerView的布局方式,可以选择GridLayoutManager来实现网格布局。Adapter负责为RecyclerView提供数据,并控制每个item的显示。 ```java LinearLayout linearLayout = findViewById(R.id.linear_layout); RecyclerView recyclerView = findViewById(R.id.recycler_view); GridLayoutManager layoutManager = new GridLayoutManager(this, 2); // 2代表每行显示2个item recyclerView.setLayoutManager(layoutManager); CustomAdapter adapter = new CustomAdapter(dataList); // 自定义Adapter,提供数据 recyclerView.setAdapter(adapter); ``` 接下来,我们可以通过RecyclerView的smoothScrollToPosition()方法来平滑地滑动到指定的position。例如,如果我们想要滑动到第10个item,可以使用以下代码: ```java recyclerView.smoothScrollToPosition(9); // RecyclerView中position从0开始计数 ``` 这个方法会使RecyclerView平稳地滑动到指定位置,并且会自动滑动到该项的前台,以便用户能够看到该项。 总结来说,我们可以通过在布局文件中添加RecyclerView并设置LayoutManager和Adapter来实现网格布局。然后,通过RecyclerView的smoothScrollToPosition()方法,我们可以滑动到指定的position。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值