雙RecyclerView 不一樣的聯(lián)動(dòng)方式

導(dǎo)讀

  • 問題描述
  • 代碼分析及修改
  • 性能測(cè)試

問題描述

數(shù)據(jù)2000多條,雙Recycleview聯(lián)動(dòng)滑動(dòng),左右各一個(gè),滑動(dòng)左邊的右邊也動(dòng),滑動(dòng)右邊左邊也動(dòng);
先滑動(dòng)左邊的Recycleview-rcvLeft,兩個(gè)列表滑動(dòng)過程中(重點(diǎn)),去滑動(dòng)右邊的Recycleview-rcvRight
這時(shí)就會(huì)報(bào)錯(cuò),堆內(nèi)存溢出8M (StackOverflowError),同理先滑右過程中去滑左也是崩潰

首先看下下需求,設(shè)計(jì)稿如下圖

image

紅色部分左右滑動(dòng),藍(lán)色部分上下滑動(dòng),所以紅色部分使用了HorizontalScrollView+RecyclerView;綠色為兩個(gè)RecyclerView:

實(shí)際效果

image

代碼梳理

網(wǎng)上方式
mLeftScrollListener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
        
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
                    rcvRight.scrollBy(dx, dy);
                }
            }
        };
        rcvLeft.addOnScrollListener(mLeftScrollListener);

        mRightScrollListener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
              
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
            
                    rcvLeft.scrollBy(dx, dy);//報(bào)StackOverflowError
                }
            }
        };
        rcvRight.addOnScrollListener(mRightScrollListener);

網(wǎng)上上下聯(lián)動(dòng)都是這套代碼,可能數(shù)據(jù)量比較小,滑動(dòng)時(shí)都沒發(fā)現(xiàn)StackOverflowError這個(gè)異常;
解決辦法:在滑動(dòng)rcvLeft時(shí)讓rcvRight的父布局去攔截rcvRight的觸摸事件,在滑動(dòng)rcvRight時(shí)讓rcvLeft的父布局去攔截rcvLeft的觸摸事件;
所以重寫了右邊的HorizontalScrollView和左邊父布局RelativeLayout;

TouchHorizontalScrollView.java:
public class TouchHorizontalScrollView extends HorizontalScrollView {
    public TouchHorizontalScrollView(Context context) {
        super(context);
    }

    public TouchHorizontalScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TouchHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (intercept) {
            return true;
        }
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 是否攔截子View
     */
    private boolean intercept=false;

    public boolean isIntercept() {
        return intercept;
    }

    public void setIntercept(boolean intercept) {
        this.intercept = intercept;
    }
TouchRelativeLayout.java :
public class TouchRelativeLayout extends RelativeLayout {
    public TouchRelativeLayout(Context context) {
        super(context);
    }

    public TouchRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TouchRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (intercept) {
            return true;
        }
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 是否攔截子View
     */
    private boolean intercept=false;

    public boolean isIntercept() {
        return intercept;
    }

    public void setIntercept(boolean intercept) {
        this.intercept = intercept;
    }
}

處理后代碼:

mLeftScrollListener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                //newState 0:停止,1、2表示滑動(dòng),再滑動(dòng)時(shí)去攔截右側(cè)RecyclerView 的觸摸事件
               
                horizontalScrollview.setIntercept(newState != 0);
              
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
                    rcvRight.scrollBy(dx, dy);
                }
            }
        };
        rcvLeft.addOnScrollListener(mLeftScrollListener);

        mRightScrollListener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                    //newState 0:停止,1、2表示滑動(dòng),在滑動(dòng)時(shí)去攔截左側(cè)RecyclerView 的觸摸事件
               
                    rl_left.setIntercept(newState != 0);
              
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
                
                    rcvLeft.scrollBy(dx, dy);//報(bào)StackOverflowError
                }
            }
        };
        rcvRight.addOnScrollListener(mRightScrollListener);

這樣處理完之后稍微有點(diǎn)小瑕疵:在滑動(dòng)左側(cè)后,滑動(dòng)右側(cè)是不生效的(因?yàn)橛|摸事件被攔截了),但是總比報(bào)StackOverflowError 崩潰強(qiáng);

記得在onDestroy時(shí)移除滑動(dòng)事件監(jiān)聽,不然在滑動(dòng)時(shí),關(guān)閉頁面會(huì)報(bào)空指針

@Override
    protected void onDestroy() {
        try {
            if (rcvLeft != null) {
                rcvLeft.removeOnScrollListener(mLeftScrollListener);
            }
            if (rcvRight != null) {
                rcvRight.removeOnScrollListener(mRightScrollListener);
            }
            mLeftScrollListener=null;
            mRightScrollListener=null;
        }catch (Exception e){
           e.printStackTrace();
        }

        super.onDestroy();
    }

最后測(cè)試下這個(gè)滑動(dòng)的性能,看看有沒有掉幀的現(xiàn)象

打開https://perfdog.qq.com/,進(jìn)行測(cè)試,通過windows 客戶端 鏈接手機(jī)進(jìn)行數(shù)據(jù)收集,兩次之前的代碼測(cè)試,一次修改的代碼測(cè)試,選擇好進(jìn)行對(duì)比

image.png

藍(lán)色的線是我們修復(fù)代碼后的曲線
FPS


image

JANK


image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容