:smlie: 官方文档
使用 RecyclerView
创建动态列表
RecyclerView
可以让您轻松高效地显示大量数据。您提供数据并定义每个列表项的外观,而 RecyclerView
库会根据需要动态创建元素。
顾名思义,RecyclerView
会回收这些单个的元素。当列表项滚动出屏幕时,RecyclerView
不会销毁其视图。相反,RecyclerView
会对屏幕上滚动的新列表项重用该视图。这种重用可以显著提高性能,改善应用响应能力并降低功耗。
⚠️注意:RecyclerView 除了是类的名称,也是库的名称。在本页中,采用 code font 字体的 RecyclerView 始终表示 RecyclerView 库中的类。
关键类
将多个不同的类搭配使用,可构建动态列表。
- RecyclerView 是包含与您的数据对应的视图的 ViewGroup。它本身就是视图,因此,将 RecyclerView 添加到布局中的方式与添加任何其他界面元素相同。
- 列表中的每个独立元素都由一个 ViewHolder 对象进行定义。创建 ViewHolder 时,它并没有任何关联的数据。创建 ViewHolder 后,RecyclerView 会将其绑定到其数据。您可以通过扩展 RecyclerView.ViewHolder 来定义 ViewHolder。
- RecyclerView 会请求这些视图,并通过在 Adapter 中调用方法,将视图绑定到其数据。您可以通过扩展 RecyclerView.Adapter 来定义 Adapter。
- 布局管理器负责排列列表中的各个元素。您可以使用 RecyclerView 库提供的某个布局管理器,也可以定义自己的布局管理器。布局管理器均基于库的 LayoutManager 抽象类。
您可以在 RecyclerView 示例应用 (Kotlin) 或 RecyclerView 示例应用 (Java) 中查看各部分如何组合在一起。
实现 RecyclerView 的步骤
如果您打算使用 RecyclerView,那么您需要完成几项工作。下面几部分对这些工作进行了详细介绍。
- 首先,确定列表或网格的外观。一般来说,您可以使用 RecyclerView 库的某个标准布局管理器。
- 设计列表中每个元素的外观和行为。根据此设计,扩展 ViewHolder 类。您的 ViewHolder 版本提供了列表项的所有功能。您的 ViewHolder 是 View 的封装容器,且该视图由 RecyclerView 管理。
- 定义用于将您的数据与 ViewHolder 视图相关联的 Adapter。
规划布局
RecyclerView 中的列表项由 LayoutManager 类负责排列。RecyclerView 库提供了三种布局管理器,用于处理最常见的布局情况:
- LinearLayoutManager 将各个项排列在一维列表中。
- GridLayoutManager 将所有项排列在二维网格中:
- 如果网格垂直排列,GridLayoutManager 会尽量使每行中所有元素的宽度和高度相同,但不同的行可以有不同的高度。
- 如果网格水平排列,GridLayoutManager 会尽量使每列中所有元素的宽度和高度相同,但不同的列可以有不同的宽度。
- StaggeredGridLayoutManager 与 GridLayoutManager 类似,但不要求同一行中的列表项具有相同的高度(垂直网格有此要求)或同一列中的列表项具有相同的宽度(水平网格有此要求)。其结果是,同一行或同一列中的列表项可能会错落不齐。
您还需要设计各个列表项的布局。在设计 ViewHolder 时,您需要使用此布局,如下一部分所述。
实现Adapter和ViewHolder
确定布局后,您需要实现 Adapter 和 ViewHolder。这两个类配合使用,共同定义数据的显示方式。ViewHolder 是包含列表中各列表项的布局的 View 的封装容器。Adapter 会根据需要创建 ViewHolder 对象,还会为这些视图设置数据。将视图与其数据相关联的过程称为“绑定”。
定义 Adapter 时,您需要替换三个关键方法:
- onCreateViewHolder():每当 RecyclerView 需要创建新的 ViewHolder 时,它都会调用此方法。此方法会创建并初始化 ViewHolder 及其关联的 View,但不会填充视图的内容,因为 ViewHolder 此时尚未绑定到具体数据。
- onBindViewHolder():RecyclerView 调用此方法将 ViewHolder 与数据相关联。此方法会提取适当的数据,并使用该数据填充 ViewHolder 的布局。例如,如果 RecyclerView 显示的是一个名称列表,该方法可能会在列表中查找适当的名称,并填充 ViewHolder 的 TextView widget。
- getItemCount():RecyclerView 调用此方法来获取数据集的大小。例如,在通讯簿应用中,这可能是地址总数。RecyclerView 使用此方法来确定什么时候没有更多的列表项可以显示。
下面是一个典型的简单 Adapter 示例,该 Adapter 包含一个显示数据列表的嵌套 ViewHolder。在本例中,RecyclerView 显示了一个简单的文本元素列表。系统会向 Adapter 传递一个字符串数组,该数组包含了 ViewHolder 元素的文本。
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private String[] localDataSet;
/**
* Provide a reference to the type of views that you are using
* (custom ViewHolder).
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public ViewHolder(View view) {
super(view);
// Define click listener for the ViewHolder's View
textView = (TextView) view.findViewById(R.id.textView);
}
public TextView getTextView() {
return textView;
}
}
/**
* Initialize the dataset of the Adapter.
*
* @param dataSet String[] containing the data to populate views to be used
* by RecyclerView.
*/
public CustomAdapter(String[] dataSet) {
localDataSet = dataSet;
}
// Create new views (invoked by the layout manager)
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view, which defines the UI of the list item
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.text_row_item, viewGroup, false);
return new ViewHolder(view);
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the
// contents of the view with that element
viewHolder.getTextView().setText(localDataSet[position]);
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
return localDataSet.length;
}
}
每个视图项的布局照例在 XML 布局文件中定义。在本例中,应用包含一个 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>