RecyclerView 的简单使用 -- (横向/纵向)列表、网格、瀑布流的实现

代码实现

RecyclerViewActivity.java

public class RecyclerViewActivity extends BaseActivity {

    private RecyclerView recyclerView;
    private MyRecyclerViewAdapter adapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_recyclerview);

        recyclerView = findViewById(R.id.recyclerview);

        initRecyclerView();
    }

    /**
     * 初始化 RecyclerView
     */
    private void initRecyclerView() {
        initLayoutManager();

        initItemDivider();

        initAdapter();

        initItemClickListener();
    }

    /**
     * 设置布局管理器
     */
    private void initLayoutManager() {
        /**
         * 用于实现列表布局
         */
        LinearLayoutManager manager = new LinearLayoutManager(this);
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(manager);

        /**
         * 用于实现网格布局
         */
//        GridLayoutManager manager = new GridLayoutManager(this, 3);
//        manager.setOrientation(GridLayoutManager.VERTICAL);
//        recyclerView.setLayoutManager(manager);

        /**
         * 用于实现瀑布流
         * 第一个参数表示布局的列数/行数
         * 第二个参数表示布局的方向
         *  StaggeredGridLayoutManager.VERTICAL -- 表示布局纵向排列
         *  StaggeredGridLayoutManager.HORIZONTAL -- 表示布局横向排列
         */
//        StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
//        manager.setOrientation(StaggeredGridLayoutManager.HORIZONTAL);
//        recyclerView.setLayoutManager(manager);
    }

    /**
     * 设置 adapter
     */
    private void initAdapter() {
        adapter = new MyRecyclerViewAdapter(this, initBookList());
        recyclerView.setAdapter(adapter);
    }

    /**
     * 设置 点击或长按事件监听
     */
    private void initItemClickListener() {
        OnRecyclerViewItemClickListener itemClickListener = new OnRecyclerViewItemClickListener() {
            /**
             * item 点击事件
             *
             * @param position
             */
            @Override
            public void onItemClick(int position) {
                Toast.makeText(RecyclerViewActivity.this, position + "被点击", Toast.LENGTH_SHORT).show();
            }

            /**
             * item 长按事件
             * @param position
             */
            @Override
            public boolean onItemLongClick(int position) {
                Toast.makeText(RecyclerViewActivity.this, position + "被长按", Toast.LENGTH_SHORT).show();
                return true;
            }
        };
        adapter.setOnRecyclerViewItemClickListener(itemClickListener);
    }

    /**
     * 给 item 添加分割线
     * 注意:只适用于列表布局(LinearLayout)的 RecyclerView
     *       网格布局(GridLayout)和流式布局(StaggeredGridLayout)的 RecyclerView 就不适用了。
     */
       private void initItemDivider() {
        //添加 Android 自带的分割线
//        DividerItemDecoration divider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        //添加 分割线,但最后一个 item 不添加分割线的效果
        CustomDividerItemDecoration divider = new CustomDividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        //添加 自定义图片/ 图形 的分割线
        divider.setDrawable(getResources().getDrawable(R.drawable.line_gradient));
        recyclerView.addItemDecoration(divider);
    }

    /**
     * 初始化 集合 数据
     *
     * @return mBookList
     */
    private List<Book> initBookList() {

        List<Book> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            list.add(new Book("book" + i, "author" + i));
        }
        return list;
    }

    public class MyRecyclerViewAdapter extends RecyclerView.Adapter<BookViewHolder> implements View.OnClickListener, View.OnLongClickListener {

        Context context;
        List<Book> list;
        private OnRecyclerViewItemClickListener itemClickListener = null;

        /**
         * 设置 item 点击监听器
         * @param itemClickListener
         */
        public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener itemClickListener) {
            this.itemClickListener = itemClickListener;
        }

        public MyRecyclerViewAdapter(Context context, List<Book> list) {
            this.context = context;
            this.list = list;
        }

        @Override
        public BookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(context).inflate(R.layout.viewholder_recyclerview, parent, false);
            BookViewHolder viewHolder = new BookViewHolder(view);

            view.setOnClickListener(this);
            view.setOnLongClickListener(this);
            return viewHolder;
        }

        @Override
        public void onBindViewHolder(BookViewHolder holder, int position) {
            holder.textview.setText(list.get(position).toString());

            if (itemClickListener == null) {
                return;
            }

            //被点击的 item 设置 tag 标签,用于 item 被点击时获取 position 索引值
            holder.itemView.setTag(position);
        }

        @Override
        public int getItemCount() {
            return list.size();
        }

        /**
         * 当 item 被点击时,调用 item 点击监听,执行点击回调
         * @param v
         */
        @Override
        public void onClick(View v) {
            if (itemClickListener == null) {
                return;
            }

            //被点击的 item 获取 tag 标签,即 position 索引值
            itemClickListener.onItemClick((Integer) v.getTag());
        }

        @Override
        public boolean onLongClick(View v) {
            return itemClickListener.onItemLongClick((Integer) v.getTag());
        }
    }

    /**
     * RecyclerView 的 ViewHolder 实现类
     */
    public class BookViewHolder extends RecyclerView.ViewHolder {

        TextView textview;

        public BookViewHolder(View itemView) {
            super(itemView);
            textview = itemView.findViewById(R.id.textview);
        }
    }
}

参照 DividerItemDecoration 自定义 item 的分割线

/**
 * 最后一个 item 不添加分割线
 * 只能用于 LinearLayoutManager 列表布局,不能用于网格布局(GridLayoutManager)和瀑布流(StaggeredGridLayoutManager)
 */

public class CustomDividerItemDecoration extends RecyclerView.ItemDecoration {

    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    public static final int VERTICAL = LinearLayout.VERTICAL;

    private static final String TAG = "CustomDividerItem";
    private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };

    private Drawable mDivider;

    /**
     * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
     */
    private int mOrientation;

    private final Rect mBounds = new Rect();

    /**
     * 只能用于 LinearLayoutManager 列表布局,不能用于网格布局(GridLayoutManager)和瀑布流(StaggeredGridLayoutManager)
     *
     * @param context Current context, it will be used to access resources.
     * @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
     */
    public CustomDividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        if (mDivider == null) {
            Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "
                    + "DividerItemDecoration. Please set that attribute all call setDrawable()");
        }
        a.recycle();
        setOrientation(orientation);
    }

    /**
     * Sets the orientation for this divider. This should be called if
     * {@link RecyclerView.LayoutManager} changes orientation.
     *
     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
     */
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException(
                    "Invalid orientation. It should be either HORIZONTAL or VERTICAL");
        }
        mOrientation = orientation;
    }

    /**
     * Sets the {@link Drawable} for this divider.
     *
     * @param drawable Drawable that should be used as a divider.
     */
    public void setDrawable(@NonNull Drawable drawable) {
        if (drawable == null) {
            throw new IllegalArgumentException("Drawable cannot be null.");
        }
        mDivider = drawable;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (parent.getLayoutManager() == null || mDivider == null) {
            return;
        }
        if (mOrientation == VERTICAL) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    private void drawVertical(Canvas canvas, RecyclerView parent) {
        canvas.save();
        final int left;
        final int right;
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        if (parent.getClipToPadding()) {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            canvas.clipRect(left, parent.getPaddingTop(), right,
                    parent.getHeight() - parent.getPaddingBottom());
        } else {
            left = 0;
            right = parent.getWidth();
        }

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            final View child = parent.getChildAt(i);
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
            final int top = bottom - mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        canvas.save();
        final int top;
        final int bottom;
        //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
        if (parent.getClipToPadding()) {
            top = parent.getPaddingTop();
            bottom = parent.getHeight() - parent.getPaddingBottom();
            canvas.clipRect(parent.getPaddingLeft(), top,
                    parent.getWidth() - parent.getPaddingRight(), bottom);
        } else {
            top = 0;
            bottom = parent.getHeight();
        }

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            final View child = parent.getChildAt(i);
            parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
            final int right = mBounds.right + Math.round(child.getTranslationX());
            final int left = right - mDivider.getIntrinsicWidth();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                               RecyclerView.State state) {
        if (mDivider == null) {
            outRect.set(0, 0, 0, 0);
            return;
        }
        if (mOrientation == VERTICAL) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}

item 点击事件监听

OnRecyclerViewItemClickListener.java

public interface OnRecyclerViewItemClickListener {
    /**
     * 点击事件
     * @param position
     */
    void onItemClick(int position);

    /**
     * 长按事件
     * @param position
     */
    void onItemLongClick(int position);
}

实体 Bean

Book.java

public class Book {

    private String name;
    private String author;

    public Book(String name, String author){
        this.name = name;
        this.author = author;
    }

    @Override
    public String toString() {
        return "name: " + name + "\n" + "author: " + author;
    }
}

布局文件

activity_recyclerview.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:text="recyclervie"
        android:layout_marginBottom="15dp"
        android:layout_height="wrap_content" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

viewholder_recyclerview.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/textview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:drawableRight="@drawable/mechanic"
        android:padding="15dp"
        android:text="item" />

</LinearLayout>

line_gradient.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <gradient
        android:startColor="#FFFFFFFF"
        android:centerColor="#FF0000FF"
        android:endColor="#FFFFFFFF"
        android:type="linear"/>

    <!--尝试这个颜色搭配的效果-->
    <!--<gradient-->
        <!--android:startColor="#FFFF0000"-->
        <!--android:centerColor="#FF00FF00"-->
        <!--android:endColor="#FF0000FF"-->
        <!--android:type="linear" />-->

    <!--DividerItemDecoration.HORIZONTAL 时设置-->
    <!--<size android:width="3dp" />-->

    <!--DividerItemDecoration.VERTICAL 时设置-->
    <size android:height="3dp" />

</shape>

RecyclerView 的 item 的点击事件

RecyclerView 竟然找不到 setOnItemClickListener() 方法,网上的写法都是自定义接口来实现
本例中,已经添加了 item 的点击事件和长按事件监听

/**
 * item 点击事件监听
 */
public interface OnItemClickListener{
    /**
     * 点击事件
     * @param position
     */
    void onItemClick(int position);

    /**
     * 长按事件
     * @param position
     */
    void onItemLongClick(int position);
}

这里还是有几点需要注意的地方

1.RecyclerView 需要用到 布局管理器,如未添加,则 item 没有显示出来

这里写图片描述

/**
 * 用于实现列表布局
 */
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
如果需要改成横向的列表,只需改变一行代码即可
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
RecyclerView 除了 LinearLayoutManager(列表布局)之外
还提供了 GridlayoutManager(网格布局) 和 StaggeredGridlayoutManager(瀑布流)
/**
 * 用于实现网格布局
 */
GridLayoutManager manager = new GridLayoutManager(this, 3);
manager.setOrientation(GridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
如果需要改成横向的网格,只需改变一行代码即可
manager.setOrientation(GridLayoutManager.HORIZONTAL);
/**
 * 用于实现瀑布流
 * 第一个参数表示布局的列数/行数
 * 第二个参数表示布局的方向
 *  StaggeredGridLayoutManager.VERTICAL -- 表示布局纵向排列
 *  StaggeredGridLayoutManager.HORIZONTAL -- 表示布局横向排列
 */
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
如果需要改成横向的瀑布流,只需添加/改变一行代码即可
manager.setOrientation(StaggeredGridLayoutManager.HORIZONTAL);

2.RecyclerView 有自己的 Adapter (即,RecyclerView.Adapter)

这里写图片描述

这里的 Adapter 带了泛型,泛型我们可以写 RecyclerView.ViewHolder,因为 BookViewHolder 就是其子类,但之所以这里没有写 RecyclerView.ViewHolder,是因为这里有个小问题需要注意。

根据快捷键自动实现父类方法,代码如下

这里写图片描述

存在两个问题

1.创建 RecyclerView.ViewHolder 对象时报错,提示 RecyclerView 里的 ViewHolder 类是抽象类(abstract)

这里写图片描述

2.在 onBindViewHolder 回调中,找不到我们自己写的 item 布局里的控件

这里写图片描述

所以,建议在泛型里写我们自己定义的 BookViewHolder 而不是直接写 RecyclerView.ViewHolder,这样在实现父类方法时,直接生成了对应的泛型,而不需要我们在一个一个的去修改(如果忘记修改了,就会需要我们一个一个小问题的去排除),当然只是建议。

这里写图片描述

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<BookViewHolder> {

        Context context;
        List<Book> list;

        public MyRecyclerViewAdapter(Context context, List<Book> list){
            this.context = context;
            this.list = list;
        }

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

        @Override
        public void onBindViewHolder(BookViewHolder holder, int position) {
            holder.textview.setText(list.get(position).toString());
        }

        @Override
        public int getItemCount() {
            return list.size();
        }
    }

3.在 Adapter 的 onCreateViewHolder 方法中,进行加载 item 布局时,建议用下面两种方式

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

@Override
        public BookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = View.inflate(context, R.layout.viewholder_recyclerview, null);
            BookViewHolder viewHolder = new BookViewHolder(view);
            return viewHolder;
        }

先看一下报错的情况

这里写图片描述

这里写图片描述

上面两种情况,会出现 IllegalStateException,并且没有提示错误位置

这里写图片描述

我们看一下 inflate 的源码

这里写图片描述

这里写图片描述

我们可以看到,如果不传 boolean attachToRoot 参数则默认传的是 root != null
而上述两种情况中,ViewGroup 对象并不是 null

猜你喜欢

转载自blog.csdn.net/qq_34801506/article/details/80538944