美文网首页
RecyclerView fling事件

RecyclerView fling事件

作者: Today_Now | 来源:发表于2019-12-16 20:14 被阅读0次

本文旨在分析RecyclerView 中的fling 事件,并提出一些注意事项:
事件拦截处理,只有recyclerView scrollState 是停止的时候,事件才会向下分发

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                          .....
                if (mScrollState == SCROLL_STATE_SETTLING) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    setScrollState(SCROLL_STATE_DRAGGING);
                }
                break;
                         .....
        }
       //这个地方需要注意
       //结合上下文可以看到,只有recyclerView 的state==SCROLL_STATE_IDLE,也就是非滑动状态,事件才不被拦截
        return mScrollState == SCROLL_STATE_DRAGGING;
    }

fling 事件的开始,UP 手指抬起的时候,开始fling

  @Override
    public boolean onTouchEvent(MotionEvent e) {
            case MotionEvent.ACTION_UP: {
                mVelocityTracker.addMovement(vtev);
                eventAddedToVelocityTracker = true;
                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
                final float xvel = canScrollHorizontally
                        ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
                final float yvel = canScrollVertically
                        ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
            //here 回调fling api
                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
                    setScrollState(SCROLL_STATE_IDLE);
                }
                resetTouch();
            } break;
        return true;
    }

fling 一系列事件,从这个api 开始处理

public boolean fling(int velocityX, int velocityY) {
         //嵌套滑动事件中,onNestedPreFling api 被调用
        if (!dispatchNestedPreFling(velocityX, velocityY)) {
            final boolean canScroll = canScrollHorizontal || canScrollVertical;
          //嵌套滑动,onNestedFling api 被调用
            dispatchNestedFling(velocityX, velocityY, canScroll);
            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
                return true;
            }

            if (canScroll) {
                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
                if (canScrollHorizontal) {
                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
                }
                if (canScrollVertical) {
                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
                }
           //嵌套滑动,startNestedScroll api 被调用
                startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);

                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));

            //ViewFlinger 是RecyclerView 中的类,implements Runnable ,用来处理fling事件
                mViewFlinger.fling(velocityX, velocityY);
                return true;
            }
        }
        return false;
    }

ViewFlinger implements Runnable

 public void fling(int velocityX, int velocityY) {
          //设置当前scroll 状态
            setScrollState(SCROLL_STATE_SETTLING);
            mLastFlingX = mLastFlingY = 0;
            mScroller.fling(0, 0, velocityX, velocityY,
                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
            postOnAnimation();
        }
 void postOnAnimation() {
            if (mEatRunOnAnimationRequest) {
                mReSchedulePostAnimationCallback = true;
            } else {
                removeCallbacks(this);
          //回调run()方法
                ViewCompat.postOnAnimation(RecyclerView.this, this);
            }
        }

这里有一个问题,RecyclerView 滑动到底部是怎么停止滑动事件的?
通过以下代码拆解,可以看出,如果一次事件中,有滑动距离未被处理,则认为滑动应该停止,stopScroll()。

以下代码中,也表现出了,fling 事件通过post ,实现了分批回调onNestedPrescroll、onNestedScroll,从而可以在嵌套滑动事件中,优雅的处理fling操作。

public void run() {            
          //scroll 未停止滑动
            if (scroller.computeScrollOffset()) {
                final int[] scrollConsumed = mScrollConsumed;
                final int x = scroller.getCurrX();
                final int y = scroller.getCurrY();//scroller 当前的y 值
                int dx = x - mLastFlingX;
                //dy 应该滑动的距离
                int dy = y - mLastFlingY;
                int hresult = 0;
                int vresult = 0;
                mLastFlingX = x;
                mLastFlingY = y;
                //未被消费的距离
                int overscrollX = 0, overscrollY = 0;

                if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
                    dx -= scrollConsumed[0];
                    dy -= scrollConsumed[1];
                }

                if (mAdapter != null) {
                    eatRequestLayout();
                    onEnterLayoutOrScroll();
                    TraceCompat.beginSection(TRACE_SCROLL_TAG);
                    fillRemainingScrollValues(mState);
                    if (dx != 0) {
                    //实际滑动了多少x
                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
                        overscrollX = dx - hresult;
                    }
                    if (dy != 0) {
                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
                    //实际滑动了多少y
                        overscrollY = dy - vresult;
                    }
                                         .....
                  //嵌套滑动事件,如果有parent ,则不处理代码块中的代码逻辑
                  //嵌套滑动事件  onNestedScroll api 被调用,此时child 已经滑动完成
                  //代码块中的逻辑,处理了滑动到底部事件,停止滑动的操作
                if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
                        TYPE_NON_TOUCH)
                        && (overscrollX != 0 || overscrollY != 0)) {
                    final int vel = (int) scroller.getCurrVelocity();

                    int velX = 0;
                    if (overscrollX != x) {
                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
                    }
                    int velY = 0;
                    if (overscrollY != y) {
                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
                    }
                  // 如果有剩余未被处理的滑动距离,则 abortAnimation
                  //scroller 中的isFinish=true
                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
                        scroller.abortAnimation();
                    }
                }

                //scrollListener api 被调用
                if (hresult != 0 || vresult != 0) {
                    dispatchOnScrolled(hresult, vresult);
                }
                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
                        && vresult == dy;
                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
                        && hresult == dx;
                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
                        || fullyConsumedVertical;

            //如果scroller  中isFinish=true,stopScroll,停止滑动事件
                if (scroller.isFinished() || (!fullyConsumedAny
                        && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
                    // setting state to idle will stop this.
                    setScrollState(SCROLL_STATE_IDLE);
                    if (ALLOW_THREAD_GAP_WORK) {
                        mPrefetchRegistry.clearPrefetchPositions();
                    }
                    stopNestedScroll(TYPE_NON_TOUCH);
                } else {
                  //如果未停止,则继续回调run 方法,fling事件变成了多次回调run 的操作,从而解决了嵌套滑动中能逐步处理fling事件
                    postOnAnimation();
                }
            }
        }

相关文章

网友评论

      本文标题:RecyclerView fling事件

      本文链接:https://www.haomeiwen.com/subject/oqxprqtx.html