Android--RecyclerView嵌套R(shí)ecyclerView優(yōu)化

我們在使用RecyclerView嵌套R(shí)ecyclerView時(shí)绝淡,會(huì)覺得卡頓,操作不是很順暢
RecyclerView嵌套R(shí)ecyclerView滑動(dòng).gif
在這個(gè)現(xiàn)象中,我們想要水平滑動(dòng)時(shí),卻豎直滑動(dòng)了
我們觀察RecyclerView的源碼续扔,在onInterceptTouchEvent方法中對(duì)事件做了處理
    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        if (mLayoutFrozen) {
            // When layout is frozen,  RV does not intercept the motion event.
            // A child view e.g. a button may still get the click.
            return false;
        }
        if (dispatchOnItemTouchIntercept(e)) {
            cancelTouch();
            return true;
        }

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

        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
        final boolean canScrollVertically = mLayout.canScrollVertically();

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

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

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

                if (mScrollState == SCROLL_STATE_SETTLING) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    setScrollState(SCROLL_STATE_DRAGGING);
                }

                // 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);
                break;

            case MotionEventCompat.ACTION_POINTER_DOWN:
                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);
                if (mScrollState != SCROLL_STATE_DRAGGING) {
                    final int dx = x - mInitialTouchX;
                    final int dy = y - mInitialTouchY;
                    boolean startScroll = false;
                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
                        startScroll = true;
                    }
                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
                        startScroll = true;
                    }
                    if (startScroll) {
                        setScrollState(SCROLL_STATE_DRAGGING);
                    }
                }
            } break;

            case MotionEventCompat.ACTION_POINTER_UP: {
                onPointerUp(e);
            } break;

            case MotionEvent.ACTION_UP: {
                mVelocityTracker.clear();
                stopNestedScroll();
            } break;

            case MotionEvent.ACTION_CANCEL: {
                cancelTouch();
            }
        }
        return mScrollState == SCROLL_STATE_DRAGGING;
    }
主要看MOVE事件攻臀,RecyclerView中只做了距離判斷,只要滑動(dòng)的距離大于系統(tǒng)的默認(rèn)滑動(dòng)距離閾值纱昧,則本身進(jìn)行滑動(dòng)刨啸,這就導(dǎo)致了為什么我們水平滑動(dòng)的時(shí)候,子RecyclerView接收不到Touch事件的原因识脆,我們針對(duì)這點(diǎn)進(jìn)行優(yōu)化
/**
 * 解決嵌套R(shí)ecyclerView滑動(dòng)問題
 */
public class BetterRecyclerView extends RecyclerView {
    private int mTouchSlop;
    private int mScrollPointerId = 0;
    private int mInitialTouchX;
    private int mInitialTouchY;

    public BetterRecyclerView(@NonNull Context context) {
        this(context, null);
    }

    public BetterRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BetterRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        final ViewConfiguration vc = ViewConfiguration.get(context);
        mTouchSlop = vc.getScaledTouchSlop();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        //仿造源碼
        final int action = MotionEventCompat.getActionMasked(e);
        final int actionIndex = MotionEventCompat.getActionIndex(e);

        switch (action) {
            case MotionEvent.ACTION_DOWN://記錄按下的坐標(biāo)
                mScrollPointerId = e.getPointerId(0);
                mInitialTouchX = (int) (e.getX() + 0.5f);
                mInitialTouchY = (int) (e.getY() + 0.5f);
                break;
            case MotionEvent.ACTION_POINTER_DOWN://這邊處理多手指觸摸呜投,優(yōu)先使用后面觸摸的手指
                mScrollPointerId = e.getPointerId(actionIndex);
                mInitialTouchX = (int) (e.getX() + 0.5f);
                mInitialTouchY = (int) (e.getY() + 0.5f);
                break;
            case MotionEvent.ACTION_MOVE://仿造源碼
                final int index = e.findPointerIndex(mScrollPointerId);
                final int x = (int) (e.getX(index) + 0.5f);
                final int y = (int) (e.getY(index) + 0.5f);

                boolean startScroll = false;
                if (getScrollState() != SCROLL_STATE_DRAGGING) {
                    final int dx = x - mInitialTouchX;
                    final int dy = y - mInitialTouchY;

                    //這邊增加攔截的難度,當(dāng)滑動(dòng)的水平距離大于滑動(dòng)的垂直距離時(shí)存璃,才攔截,當(dāng)然了雕拼,如果自身又能豎直滑動(dòng)的情況纵东,就不判斷兩個(gè)滑動(dòng)距離大小
                    if ((getLayoutManager().canScrollHorizontally() && Math.abs(dx) > mTouchSlop)
                            && (getLayoutManager().canScrollVertically() || Math.abs(dx) > Math.abs(dy))) {
                        startScroll = true;
                    }
                    //這里是當(dāng)滑動(dòng)的水平距離小于滑動(dòng)的垂直距離時(shí),才攔截啥寇,如果自身又能水平滑動(dòng)的情況偎球,就不判斷兩個(gè)滑動(dòng)距離大小
                    if ((getLayoutManager().canScrollVertically() && Math.abs(dy) > mTouchSlop)
                            && (getLayoutManager().canScrollHorizontally() || Math.abs(dy) > Math.abs(dx))) {
                        startScroll = true;
                    }
                }

                return startScroll && super.onInterceptTouchEvent(e);
        }

        return super.onInterceptTouchEvent(e);
    }
}
我們修改了攔截方法,只有當(dāng)水平滑動(dòng)距離大于豎直滑動(dòng)距離時(shí)辑甜,才讓RecyclerView攔截事件
RecyclerView嵌套R(shí)ecyclerView滑動(dòng)2.gif
這時(shí)衰絮,發(fā)現(xiàn)了另一個(gè)問題,當(dāng)子RecyclerView快速滑動(dòng)磷醋,還沒有停止?jié)L動(dòng)時(shí)猫牡,我們?nèi)ヘQ直滑動(dòng),發(fā)現(xiàn)父RecyclerView沒法接受到Touch事件邓线,我們找到requestDisallowInterceptTouchEvent方法并改寫了它淌友,解決了這個(gè)問題
    /**
     * 這個(gè)接口是不允許父類打斷這個(gè)onTouch事件,一般被子類調(diào)用getParent.requestDisallowInterceptTouchEvent(true)
     * 比如快速水平滑動(dòng)子view的時(shí)候骇陈,這時(shí)我們想去豎直滑動(dòng)父類震庭,因?yàn)榭焖倩瑒?dòng)有一個(gè)速度,子view還沒停止?jié)L動(dòng)你雌,所以子類禁止父類攔截
     * 想要解決這個(gè)辦法器联,就把這個(gè)方法改為空方法就行了
     *
     * @param disallowIntercept
     */
    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
RecyclerView嵌套R(shí)ecyclerView滑動(dòng)3.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市婿崭,隨后出現(xiàn)的幾起案子拨拓,更是在濱河造成了極大的恐慌,老刑警劉巖氓栈,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件千元,死亡現(xiàn)場離奇詭異,居然都是意外死亡颤绕,警方通過查閱死者的電腦和手機(jī)幸海,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門祟身,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人物独,你說我怎么就攤上這事袜硫。” “怎么了挡篓?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵婉陷,是天一觀的道長。 經(jīng)常有香客問我官研,道長秽澳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任戏羽,我火速辦了婚禮担神,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘始花。我一直安慰自己妄讯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布酷宵。 她就那樣靜靜地躺著亥贸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浇垦。 梳的紋絲不亂的頭發(fā)上炕置,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音男韧,去河邊找鬼讹俊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛煌抒,可吹牛的內(nèi)容都是我干的仍劈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼寡壮,長吁一口氣:“原來是場噩夢啊……” “哼贩疙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起况既,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤这溅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后棒仍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悲靴,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年莫其,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了癞尚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耸三。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖浇揩,靈堂內(nèi)的尸體忽然破棺而出仪壮,到底是詐尸還是另有隱情,我是刑警寧澤胳徽,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布积锅,位于F島的核電站,受9級(jí)特大地震影響养盗,放射性物質(zhì)發(fā)生泄漏缚陷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一往核、第九天 我趴在偏房一處隱蔽的房頂上張望箫爷。 院中可真熱鬧,春花似錦铆铆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碍论,卻和暖如春谅猾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鳍悠。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國打工税娜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人藏研。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓敬矩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蠢挡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弧岳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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