美文网首页
自定义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