双RecyclerView 不一样的联动方式

导读

  • 问题描述
  • 代码分析及修改
  • 性能测试

问题描述

数据2000多条,双Recycleview联动滑动,左右各一个,滑动左边的右边也动,滑动右边左边也动;
先滑动左边的Recycleview-rcvLeft,两个列表滑动过程中(重点),去滑动右边的Recycleview-rcvRight
这时就会报错,堆内存溢出8M (StackOverflowError),同理先滑右过程中去滑左也是崩溃

首先看下下需求,设计稿如下图

image

红色部分左右滑动,蓝色部分上下滑动,所以红色部分使用了HorizontalScrollView+RecyclerView;绿色为两个RecyclerView:

实际效果

image

代码梳理

网上方式
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);//报StackOverflowError
                }
            }
        };
        rcvRight.addOnScrollListener(mRightScrollListener);

网上上下联动都是这套代码,可能数据量比较小,滑动时都没发现StackOverflowError这个异常;
解决办法:在滑动rcvLeft时让rcvRight的父布局去拦截rcvRight的触摸事件,在滑动rcvRight时让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表示滑动,再滑动时去拦截右侧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表示滑动,在滑动时去拦截左侧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);//报StackOverflowError
                }
            }
        };
        rcvRight.addOnScrollListener(mRightScrollListener);

这样处理完之后稍微有点小瑕疵:在滑动左侧后,滑动右侧是不生效的(因为触摸事件被拦截了),但是总比报StackOverflowError 崩溃强;

记得在onDestroy时移除滑动事件监听,不然在滑动时,关闭页面会报空指针

@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();
    }

最后测试下这个滑动的性能,看看有没有掉帧的现象

打开https://perfdog.qq.com/,进行测试,通过windows 客户端 链接手机进行数据收集,两次之前的代码测试,一次修改的代码测试,选择好进行对比

image.png

蓝色的线是我们修复代码后的曲线
FPS


image

JANK


image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,306评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,657评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,928评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,688评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,105评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,024评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,159评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,937评论 0 212
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,689评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,851评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,325评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,651评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,364评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,192评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,985评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,154评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,955评论 2 279

推荐阅读更多精彩内容