RecyclerView重写LinearLayoutManager实现无限循环

1,503 阅读1分钟

LooperLayoutManage

new 一个类 LooperLayoutManager继承LinearLayoutManager

用来调整间距 private int mMargin;

import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class LooperLayoutManager extends LinearLayoutManager {
    private static final String TAG = "LooperLayoutManager";

    public LooperLayoutManager(Context context) {
        super(context);
    }

    private int mMargin=36;
    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }
    @Override
    public boolean canScrollHorizontally() {
        return true;
    }
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getItemCount() <= 0) {
            return;
        }
        //标注1.如果当前时准备状态,直接返回
        if (state.isPreLayout()) {
            return;
        }
        //标注2.将视图分离放入scrap缓存中,以准备重新对view进行排版
        detachAndScrapAttachedViews(recycler);

        int autualWidth = 0;

        for (int i = 0; i < getItemCount(); i++) {
            //标注3.初始化,将在屏幕内的view填充
            View itemView = recycler.getViewForPosition(i);
            addView(itemView);
            //标注4.测量itemView的宽高
            measureChildWithMargins(itemView, 0, 0);

            int width = getDecoratedMeasuredWidth(itemView);
            int height = getDecoratedMeasuredHeight(itemView);
            //标注5.根据itemView的宽高进行布局
            layoutDecorated(itemView, autualWidth, 0, autualWidth + width, height);

            autualWidth += width+mMargin;
//            //标注6.如果当前布局过的itemView的宽度总和大于RecyclerView的宽,则不再进行布局
//            if (autualWidth > getWidth()) {
//                break;
//            }
        }
    }
    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        //标注1.横向滑动的时候,对左右两边按顺序填充itemView
        int travl = fill(dx, recycler, state);
        if (travl == 0) {
            return 0;
        }

        //2.滑动
        offsetChildrenHorizontal(-travl);
//        offsetChildrenHorizontal(travl * -1);
        //3.回收已经不可见的itemView
        recyclerHideView(dx, recycler, state);
        return travl;
    }
    /**
     * 左右滑动的时候,填充
     */
    private RecyclerView.Recycler mRecycle;
    public int fill(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (dx > 0) {
            //标注1.向左滚动
            View lastView = getChildAt(getChildCount() - 1);
            if (lastView == null) {
                return 0;
            }
            int lastPos = getPosition(lastView);
            //标注2.可见的最后一个itemView完全滑进来了,需要补充新的
            if (lastView.getRight() < getWidth()) {
                View scrap = null;
                //标注3.判断可见的最后一个itemView的索引,
                // 如果是最后一个,则将下一个itemView设置为第一个,否则设置为当前索引的下一个
                if (lastPos == getItemCount() - 1) {
                    if (looperEnable) {
                        scrap = recycler.getViewForPosition(0);
                    } else {
                        dx = 0;
                    }
                } else {
                    scrap = recycler.getViewForPosition(lastPos + 1);
                }
                if (scrap == null) {
                    return dx;
                }
                //标注4.将新的itemViewadd进来并对其测量和布局
                addView(scrap);
                measureChildWithMargins(scrap, 0, 0);
                int width = getDecoratedMeasuredWidth(scrap);
                int height = getDecoratedMeasuredHeight(scrap);



                layoutDecorated(scrap,lastView.getRight()+mMargin, 0,
                        lastView.getRight() + width+mMargin, height);
                return dx;
            }
        } else {
            //向右滚动
            View firstView = getChildAt(0);
            if (firstView == null) {
                return 0;
            }
            int firstPos = getPosition(firstView);

            if (firstView.getLeft() >= 0) {
                View scrap = null;
                if (firstPos == 0) {
                    if (looperEnable) {
                        scrap = recycler.getViewForPosition(getItemCount() - 1);
                    } else {
                        dx = 0;
                    }
                } else {
                    scrap = recycler.getViewForPosition(firstPos - 1);
                }
                if (scrap == null) {
                    return 0;
                }
                addView(scrap, 0);
                measureChildWithMargins(scrap,0,0);
                int width = getDecoratedMeasuredWidth(scrap);
                int height = getDecoratedMeasuredHeight(scrap);
                layoutDecorated(scrap, firstView.getLeft() - width-mMargin, 0,
                        firstView.getLeft() -mMargin, height);
            }
        }

        return dx;
    }
    /**
     * 回收界面不可见的view
     */
    private void recyclerHideView(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            if (view == null) {
                continue;
            }
            if (dx > 0) {
                //标注1.向左滚动,移除左边不在内容里的view
                if (view.getRight() < 0) {
                    removeAndRecycleView(view, recycler);
                    Log.d(TAG, "循环: 移除 一个view  childCount=" + getChildCount());
                }
            } else {
                //标注2.向右滚动,移除右边不在内容里的view
                if (view.getLeft() > getWidth()) {
                    removeAndRecycleView(view, recycler);
                    Log.d(TAG, "循环: 移除 一个view  childCount=" + getChildCount());
                }
            }
        }
    }

    private boolean looperEnable;
    public void setLooperEnable(boolean b) {
        this.looperEnable = b;
    }
    public void setMargin(int margin){
        this.mMargin = margin;
    }
}

原文

MyAdapter

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.VH> {

    private List<String> list = new ArrayList<>();
    private Context context;

    public MyAdapter(Context context, List<String> lists) {
        this.context = context;
        list.clear();
        this.list.addAll(lists);
    }

    @NonNull
    @Override
    public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_home, parent, false);
        return new VH(view);
    }

    @Override
    public void onBindViewHolder(@NonNull VH holder, int position) {

        holder.mTitleTv.setText(list.get(position));
    }

    //1、设置item数量无限多
    @Override
    public int getItemCount() {
        return list.size();
    }


    class VH extends RecyclerView.ViewHolder {
        TextView mTitleTv;

        public VH(@NonNull View itemView) {
            super(itemView);
            mTitleTv = itemView.findViewById(R.id.title);
        }
    }
}

我在考虑item的布局这么简单写还是不写呢,想想还是写吧

item_home.xml

<?xml version="1.0" encoding="utf-8"?>

<com.google.android.material.card.MaterialCardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/item"
    android:layout_width="150dp"
    android:layout_height="240dp"
    app:cardCornerRadius="16dp">

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/teal_200">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/white"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">
    </TextView>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

使用方法

mRecyclerView.setAdapter(new MyAdapter(getContext(),list));
mLayoutManager = new LooperLayoutManager(getContext());
mLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mLayoutManager.setLooperEnable(true);
//设置间距
mLayoutManager.setMargin(66);
mRecyclerView.setLayoutManager(mLayoutManager);