Android - RecyclerView

RecyclerView可以以列表形式展示大量数据。当列表项滚动出屏幕时,RecyclerView 不会销毁其视图。相反,RecyclerView 会对屏幕上滚动的新列表项重用该视图。这种重用可以显著提高性能,改善应用响应能力并降低功耗。

RecyclerView是一个ViewGroup容器,本身也是一个View。


实现RecyclerView的步骤

  1. 确定列表或网格的外观。

  2. 设计每个元素的外观和行为,依据这些扩展 ViewHolder 类。

    ViewHolder 是 View 的封装容器,且该视图由 RecyclerView 管理。
    列表中每个独立元素都由一个 ViewHolder 对象进行定义。
    创建 ViewHolder 时,它并没有任何关联的数据。创建 ViewHolder 后,RecyclerView 会将其绑定到其数据。可以通过扩展 RecyclerView.ViewHolder来定义 ViewHolder。

  3. 定义将数据与ViewHolder相关联的Adapter。

    通过扩展 RecyclerView.Adapter 来定义 Adapter。

确定布局外观

RecyclerView库提供了三种布局管理器:

  • LinearLayoutManager 将各个项排列在一维列表中。
  • GridLayoutManager 将所有项排列在二维网格中:
    • 如果网格垂直排列,GridLayoutManager 会尽量使每行中所有元素的宽度和高度相同,但不同的行可以有不同的高度。
    • 如果网格水平排列,GridLayoutManager 会尽量使每列中所有元素的宽度和高度相同,但不同的列可以有不同的宽度。
  • StaggeredGridLayoutManagerGridLayoutManager 类似,但不要求同一行中的列表项具有相同的高度(垂直网格有此要求)或同一列中的列表项具有相同的宽度(水平网格有此要求)。其结果是,同一行或同一列中的列表项可能会错落不齐。

实现Adapter和ViewHolder

这两个类配合使用,共同定义数据的显示方式。

  • ViewHolder 是包含列表中各列表项的布局的 View 的封装容器。
  • Adapter 会根据需要创建 ViewHolder 对象,还会为这些视图设置数据。将视图与其数据相关联的过程称为“绑定”。

实现Adapter

定义Adapter时需要重写三个方法:

  • onCreateViewHolder():每当 RecyclerView 需要创建新的 ViewHolder 时,它都会调用此方法。此方法会创建并初始化 ViewHolder 及其关联的 View,但不会填充视图的内容,因为 ViewHolder 此时尚未绑定到具体数据。

  • onBindViewHolder()RecyclerView 调用此方法将 ViewHolder 与数据相关联。此方法会提取适当的数据,并使用该数据填充 ViewHolder 的布局。例如,如果 RecyclerView 显示的是一个名称列表,该方法可能会在列表中查找适当的名称,并填充 ViewHolder 的 TextView 微件。

  • getItemCount():RecyclerView 调用此方法来获取数据集的大小。例如,在通讯簿应用中,这可能是地址总数。RecyclerView 使用此方法来确定什么时候没有更多的列表项可以显示。

三个方法均由LayoutManager调用,不需要自己调用。

Adapter代码示例


public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    private static final String TAG = "CustomAdapter";
    private String[] localDataSet;

    // 自定义的ViewHolder类
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private final TextView textView;

        public ViewHolder(View view) {
            super(view);

            // 为ViewHolder的组件对象定义点击监听器
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d(TAG, "Element " + getAdapterPosition() + " clicked.");
                }
            });

            textView = (TextView) view.findViewById(R.id.textView); // 这一行是干啥的?
        }

        public TextView getTextView() {
            return textView;
        }
    }

    /**
     * 初始化Adapter的数据集
     *
     * @param dataSet String[] 包含了由RecyclerView用来填充View的数据
     */
    public CustomAdapter(String[] dataSet) {
        localDataSet = dataSet;
    }

    // 创建新的视图(View) (由layout manager调用)
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        // 创建新的定义列表元素UI的View
        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.text_row_item, viewGroup, false); // 绑定布局XML

        return new ViewHolder(view);
    }

    // 替换一个View的内容 (由layout manager调用)
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {

        // 获取指定位置的数据集元素,然后用该元素替换View的内容
        viewHolder.getTextView().setText(localDataSet[position]);
    }

    // 返回数据集的大小 (由layout manager调用)
    @Override
    public int getItemCount() {
        return localDataSet.length;
    }
}

对应的布局文件 text_row_item.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/list_item_height"
    android:layout_marginLeft="@dimen/margin_medium"
    android:layout_marginRight="@dimen/margin_medium"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/element_text"/>
</FrameLayout>

在Activity中使用RecyclerView

首先需要实例化RecyclerView对象,为它传入要绑定的RecyclerView组件ID(在Activity的布局XML中定义,方式同普通视图)。之后实例化一个LayoutManager,再为刚才的RecyclerView对象设置LayoutManager。最后实例化自己定义的Adapter,为RecyclerView设置Adapter。代码示例如下:

使用RecyclerView

private List<String> textList = new ArrayList<>(); // 待绑定的数据,此处为字符串列表

@Override
protected void onCreate(Bundle savedInstanceState) {

    // onCreate方法的常规语句
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initData(); // 初始化数据,为textList赋值
    RecyclerView recyclerView = findViewById(R.id.recycler_view); // 初始化RecyclerView并绑定布局XML
    LinearLayoutManager layoutManager = new LinearLayout(this); // 初始化布局管理器
    CustomAdapter adapter = new CustomAdapter(textList); // 初始化Adapter并传入数据
    recyclerView.setLayoutManager(layoutManager); // 为RecyclerView设置LayoutManager
    recyclerView.setAdapter(adapter); // 为RecyclerView设置Adapter
}

推荐阅读更多精彩内容