美文网首页学习笔记
双RecyclerView 不一样的联动方式

双RecyclerView 不一样的联动方式

作者: lovesosoi | 来源:发表于2020-03-24 12:17 被阅读0次

    导读

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

    问题描述

    数据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

    相关文章

      网友评论

        本文标题:双RecyclerView 不一样的联动方式

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