RecyclerView fling事件

本文旨在分析RecyclerView 中的fling 事件团赁,并提出一些注意事項(xiàng):
事件攔截處理,只有recyclerView scrollState 是停止的時(shí)候娜氏,事件才會(huì)向下分發(fā)

    @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;
                         .....
        }
       //這個(gè)地方需要注意
       //結(jié)合上下文可以看到璃诀,只有recyclerView 的state==SCROLL_STATE_IDLE,也就是非滑動(dòng)狀態(tài)阳谍,事件才不被攔截
        return mScrollState == SCROLL_STATE_DRAGGING;
    }

fling 事件的開(kāi)始,UP 手指抬起的時(shí)候韧拒,開(kāi)始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 回調(diào)fling api
                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
                    setScrollState(SCROLL_STATE_IDLE);
                }
                resetTouch();
            } break;
        return true;
    }

fling 一系列事件自脯,從這個(gè)api 開(kāi)始處理

public boolean fling(int velocityX, int velocityY) {
         //嵌套滑動(dòng)事件中,onNestedPreFling api 被調(diào)用
        if (!dispatchNestedPreFling(velocityX, velocityY)) {
            final boolean canScroll = canScrollHorizontal || canScrollVertical;
          //嵌套滑動(dòng)铲汪,onNestedFling api 被調(diào)用
            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;
                }
           //嵌套滑動(dòng)熊尉,startNestedScroll api 被調(diào)用
                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 桥状,用來(lái)處理fling事件
                mViewFlinger.fling(velocityX, velocityY);
                return true;
            }
        }
        return false;
    }

ViewFlinger implements Runnable

 public void fling(int velocityX, int velocityY) {
          //設(shè)置當(dāng)前scroll 狀態(tài)
            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);
          //回調(diào)run()方法
                ViewCompat.postOnAnimation(RecyclerView.this, this);
            }
        }

這里有一個(gè)問(wèn)題帽揪,RecyclerView 滑動(dòng)到底部是怎么停止滑動(dòng)事件的?
通過(guò)以下代碼拆解辅斟,可以看出转晰,如果一次事件中,有滑動(dòng)距離未被處理士飒,則認(rèn)為滑動(dòng)應(yīng)該停止查邢,stopScroll()。

以下代碼中酵幕,也表現(xiàn)出了扰藕,fling 事件通過(guò)post ,實(shí)現(xiàn)了分批回調(diào)onNestedPrescroll芳撒、onNestedScroll邓深,從而可以在嵌套滑動(dòng)事件中未桥,優(yōu)雅的處理fling操作。

public void run() {            
          //scroll 未停止滑動(dòng)
            if (scroller.computeScrollOffset()) {
                final int[] scrollConsumed = mScrollConsumed;
                final int x = scroller.getCurrX();
                final int y = scroller.getCurrY();//scroller 當(dāng)前的y 值
                int dx = x - mLastFlingX;
                //dy 應(yīng)該滑動(dòng)的距離
                int dy = y - mLastFlingY;
                int hresult = 0;
                int vresult = 0;
                mLastFlingX = x;
                mLastFlingY = y;
                //未被消費(fèi)的距離
                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) {
                    //實(shí)際滑動(dòng)了多少x
                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
                        overscrollX = dx - hresult;
                    }
                    if (dy != 0) {
                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
                    //實(shí)際滑動(dòng)了多少y
                        overscrollY = dy - vresult;
                    }
                                         .....
                  //嵌套滑動(dòng)事件芥备,如果有parent 冬耿,則不處理代碼塊中的代碼邏輯
                  //嵌套滑動(dòng)事件  onNestedScroll api 被調(diào)用,此時(shí)child 已經(jīng)滑動(dòng)完成
                  //代碼塊中的邏輯萌壳,處理了滑動(dòng)到底部事件亦镶,停止滑動(dòng)的操作
                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;
                    }
                  // 如果有剩余未被處理的滑動(dòng)距離,則 abortAnimation
                  //scroller 中的isFinish=true
                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
                        scroller.abortAnimation();
                    }
                }

                //scrollListener api 被調(diào)用
                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,停止滑動(dòng)事件
                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 {
                  //如果未停止袱瓮,則繼續(xù)回調(diào)run 方法缤骨,fling事件變成了多次回調(diào)run 的操作,從而解決了嵌套滑動(dòng)中能逐步處理fling事件
                    postOnAnimation();
                }
            }
        }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尺借,一起剝皮案震驚了整個(gè)濱河市绊起,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌褐望,老刑警劉巖勒庄,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瘫里,居然都是意外死亡实蔽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門谨读,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)局装,“玉大人,你說(shuō)我怎么就攤上這事劳殖☆砩校” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵哆姻,是天一觀的道長(zhǎng)宣增。 經(jīng)常有香客問(wèn)我,道長(zhǎng)矛缨,這世上最難降的妖魔是什么爹脾? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮箕昭,結(jié)果婚禮上灵妨,老公的妹妹穿的比我還像新娘。我一直安慰自己落竹,他們只是感情好泌霍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著述召,像睡著了一般朱转。 火紅的嫁衣襯著肌膚如雪蟹地。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天藤为,我揣著相機(jī)與錄音锈津,去河邊找鬼。 笑死凉蜂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的性誉。 我是一名探鬼主播窿吩,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼错览!你這毒婦竟也來(lái)了纫雁?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤倾哺,失蹤者是張志新(化名)和其女友劉穎轧邪,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體羞海,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忌愚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了却邓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硕糊。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖腊徙,靈堂內(nèi)的尸體忽然破棺而出简十,到底是詐尸還是另有隱情,我是刑警寧澤撬腾,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布螟蝙,位于F島的核電站,受9級(jí)特大地震影響民傻,放射性物質(zhì)發(fā)生泄漏胰默。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一饰潜、第九天 我趴在偏房一處隱蔽的房頂上張望初坠。 院中可真熱鬧,春花似錦彭雾、人聲如沸碟刺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)半沽。三九已至爽柒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間者填,已是汗流浹背浩村。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留占哟,地道東北人心墅。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像榨乎,于是被迫代替她去往敵國(guó)和親怎燥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容