美文网首页
自定义LayoutManager使RecyclerView无限循

自定义LayoutManager使RecyclerView无限循

作者: jeffrey要努力 | 来源:发表于2018-11-23 15:48 被阅读276次

    昨天学习了下LayoutManager,发现真的是很牛逼,既然滑动和绘制都是LayoutManager控制,那么自定义一个LayoutManager来实现界面无限循环滚动不是一件很简单的事么,来试试吧

    Android LayoutManager学习 https://blog.csdn.net/zxt0601/article/details/52956504

    回顾

    之前学习的时候,简单版的LinearLayoutManager的流程是

    1. 在onLayoutChildren填充子View(根据界面的大小确定填充数量)
    2. 滑动时
      1. 在滑动方向填充新的子View,如果到达了头和尾就不填充了
      2. 执行滑动操作,如果滑动到了头和尾就滑动停止
      3. 回收已经离开可视界面的子View

    思路

    上面的操作需要判断边界,要实现无限循环就是在把上面的边界拿掉

    1. 在滑动方向填充新的子View,如果到达了头和尾就继续填充
    2. 执行滑动操作,没有头尾,可以不停的往一个方向滑动
    3. 回收已经离开可视界面的子View,这个还是要继续回收的,不然会爆炸的

    滑动

    现在的滑动就没有边界了,直接就是填充,滚动,回收

     @Override
        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
            Log.d("feifeifei","getChildCount() " + getChildCount() + " recycler.getScrapList().size() " + recycler.getScrapList().size());
    
            //界面向下滚动的时候,dy为正,向上滚动的时候dy为负
    
            //填充
            fill(dy,recycler,state);
            //滚动
            offsetChildrenVertical(dy*-1);
    
            //回收已经离开界面的
            recycleOut(dy,recycler,state);
    
            return dy;
        }
    

    填充

    只看向下滚动.假设我们adapter的count是10

    1. 可视界面最下面的子View的adapter的位置如果是7,我们就获取第8位添加到尾部.之后一个一个往下加
    2. 如果界面最下方的子View已经是最后一个View,就把把位置为0的子View添加到尾部,这样往下滚动的时候就可以一直循环了,添加完了后最下面的就是adapter位置为0的子view,再往下滑动就是需要添加位置1的了

    向上滚动就是-1,到达位置0就添加位置10,这样向上滚动也可以不停的循环

    private void fill(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){
            //向下滚动
            if (dy > 0){
                //先在底部填充
                View  lastView = getChildAt(getChildCount() -1);
                int lastPos = getPosition(lastView);
                if (lastView.getBottom() -  dy < getHeight()){
                    View scrap;
                    if (lastPos == getItemCount() -1){
                        scrap = recycler.getViewForPosition(0);
                    }else {
                        scrap = recycler.getViewForPosition(lastPos+1);
                    }
                    addView(scrap);
                    measureChildWithMargins(scrap,0,0);
                    int width = getDecoratedMeasuredWidth(scrap);
                    int height = getDecoratedMeasuredHeight(scrap);
                    layoutDecorated(scrap,0,lastView.getBottom(),width,lastView.getBottom()+height);
                }
            }else {
                //向上滚动
                //现在顶部填充
                View  firstView = getChildAt(0);
                int layoutPostion = getPosition(firstView);
    
                if (firstView.getTop() >= 0 ){
                    View scrap ;
                    if (layoutPostion == 0){
                        scrap = recycler.getViewForPosition(getItemCount()-1);
                    }else {
                        scrap = recycler.getViewForPosition(layoutPostion -1);
                    }
                    addView(scrap,0);
                    measureChildWithMargins(scrap,0,0);
                    int width = getDecoratedMeasuredWidth(scrap);
                    int height = getDecoratedMeasuredHeight(scrap);
                    layoutDecorated(scrap,0,firstView.getTop() - height,width,firstView.getTop());
                }
            }
        }
    

    回收

    回收还是和之前的代码一样,超出了可视界面就回收

    可以爽快的往一个方向死命的滑动了,看看效果


    device-2018-11-23-15[00-00-00--00-00-18].gif

    完整代码

    
    /**
     * 界面无线循环的LayoutManager
     */
    public class CustomLayoutManagerInfinite extends RecyclerView.LayoutManager {
        private final String TAG = CustomLayoutManagerInfinite.class.getSimpleName();
    
    
        @Override
        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        }
    
    //    1 在RecyclerView初始化时,会被调用两次。
    //    2 在调用adapter.notifyDataSetChanged()时,会被调用。
    //    3 在调用setAdapter替换Adapter时,会被调用。
    //    4 在RecyclerView执行动画时,它也会被调用。
        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            Log.d(TAG,"onLayoutChildren ");
            if (getItemCount() == 0){
                detachAndScrapAttachedViews(recycler);
                return;
            }
            //state.isPreLayout()是支持动画的
            if (getItemCount() == 0 && state.isPreLayout()){
                return;
            }
            //将当前Recycler中的view全部移除并放到报废缓存里,之后优先重用缓存里的view
            detachAndScrapAttachedViews(recycler);
    
            int actualHeight = 0;
            for (int i = 0 ;i < getItemCount() ; i++){
                View scrap = recycler.getViewForPosition(i);
                addView(scrap);
                measureChildWithMargins(scrap,0,0);
                int width = getDecoratedMeasuredWidth(scrap);
                int height = getDecoratedMeasuredHeight(scrap);
                layoutDecorated(scrap,0,actualHeight,width,actualHeight+height);
                actualHeight+=height;
                //超出界面的就不画了,也不add了
                if (actualHeight > getHeight()){
                    break;
                }
            }
        }
    
        @Override
        public boolean canScrollVertically() {
            return true;
        }
    
    
        @Override
        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
            Log.d("feifeifei","getChildCount() " + getChildCount() + " recycler.getScrapList().size() " + recycler.getScrapList().size());
    
            //界面向下滚动的时候,dy为正,向上滚动的时候dy为负
    
            //填充
            fill(dy,recycler,state);
            //滚动
            offsetChildrenVertical(dy*-1);
    
            //回收已经离开界面的
            recycleOut(dy,recycler,state);
    
            return dy;
        }
    
    
    
    
        private void fill(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){
            //向下滚动
            if (dy > 0){
                //先在底部填充
                View  lastView = getChildAt(getChildCount() -1);
                int lastPos = getPosition(lastView);
                if (lastView.getBottom() -  dy < getHeight()){
                    View scrap;
                    if (lastPos == getItemCount() -1){
                        scrap = recycler.getViewForPosition(0);
                    }else {
                        scrap = recycler.getViewForPosition(lastPos+1);
                    }
                    addView(scrap);
                    measureChildWithMargins(scrap,0,0);
                    int width = getDecoratedMeasuredWidth(scrap);
                    int height = getDecoratedMeasuredHeight(scrap);
                    layoutDecorated(scrap,0,lastView.getBottom(),width,lastView.getBottom()+height);
                }
            }else {
                //向上滚动
                //现在顶部填充
                View  firstView = getChildAt(0);
                int layoutPostion = getPosition(firstView);
    
                if (firstView.getTop() >= 0 ){
                    View scrap ;
                    if (layoutPostion == 0){
                        scrap = recycler.getViewForPosition(getItemCount()-1);
                    }else {
                        scrap = recycler.getViewForPosition(layoutPostion -1);
                    }
                    addView(scrap,0);
                    measureChildWithMargins(scrap,0,0);
                    int width = getDecoratedMeasuredWidth(scrap);
                    int height = getDecoratedMeasuredHeight(scrap);
                    layoutDecorated(scrap,0,firstView.getTop() - height,width,firstView.getTop());
                }
            }
        }
    
            private void recycleOut(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){
                for (int i = 0 ; i <getChildCount() ;i++){
                    View view = getChildAt(i);
                    if (dy >0){
                        if (view.getBottom()-dy <0){
                            Log.d("feifeifei","recycleOut " + i);
                            removeAndRecycleView(view,recycler);
                        }
                    }else {
                        if (view.getTop()-dy > getHeight()){
                            Log.d("feifeifei","recycleOut " + i);
                            removeAndRecycleView(view,recycler);
                        }
                    }
                }
            }
    }
    

    相关文章

      网友评论

          本文标题:自定义LayoutManager使RecyclerView无限循

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