使用 RecyclerView 创建动态列表   Android Jetpack 的一部分。

尝试 Compose 方法
Jetpack Compose 是推荐的 Android UI 工具包。了解如何在 Compose 中使用布局。

RecyclerView 使高效显示大型数据集变得容易。您提供数据并定义每个项目的外观,RecyclerView 库会在需要时动态创建元素。

顾名思义,RecyclerView *回收* 这些单个元素。当项目从屏幕上滚动消失时,RecyclerView 不会销毁其视图。相反,RecyclerView 会将视图重用于已滚动到屏幕上的新项目。RecyclerView 提高了性能和应用程序的响应速度,并降低了功耗。

关键类

几个类协同工作以构建您的动态列表。

  • RecyclerView 是一个 ViewGroup,它包含与您的数据相对应的视图。它本身就是一个视图,因此您可以像添加任何其他 UI 元素一样将 RecyclerView 添加到您的布局中。

  • 列表中的每个单个元素都由一个 *视图持有者* 对象定义。创建视图持有者时,它没有任何与其关联的数据。创建视图持有者后,RecyclerView 会将其 *绑定* 到其数据。您可以通过扩展 RecyclerView.ViewHolder 来定义视图持有者。

  • RecyclerView 通过调用 *适配器* 中的方法来请求视图并将视图绑定到其数据。您可以通过扩展 RecyclerView.Adapter 来定义适配器。

  • *布局管理器* 安排列表中的各个元素。您可以使用 RecyclerView 库提供的布局管理器之一,也可以定义您自己的布局管理器。布局管理器都基于库的 LayoutManager 抽象类。

您可以在 RecyclerView 示例应用(Kotlin)RecyclerView 示例应用(Java) 中看到所有部分是如何组合在一起的。

实现 RecyclerView 的步骤

如果您要使用 RecyclerView,则需要执行以下几件事。它们将在以下各节中详细解释。

  1. 确定列表或网格的外观。通常,您可以使用 RecyclerView 库的标准布局管理器之一。

  2. 设计列表中每个元素的外观和行为。根据此设计,扩展 ViewHolder 类。您的 ViewHolder 版本提供了列表项的所有功能。您的视图持有者是 View 的包装器,该视图由 RecyclerView 管理。

  3. 定义将您的数据与 ViewHolder 视图关联的 Adapter

还有 高级自定义选项,让您可以根据您的确切需求定制您的 RecyclerView。

规划您的布局

RecyclerView 中的项目由 LayoutManager 类排列。RecyclerView 库提供了三个布局管理器,它们处理最常见的布局情况

  • LinearLayoutManager 将项目排列在一维列表中。
  • GridLayoutManager 将项目排列在二维网格中
    • 如果网格垂直排列,GridLayoutManager 会尝试使每一行中的所有元素具有相同的宽度和高度,但不同的行可以具有不同的高度。
    • 如果网格水平排列,GridLayoutManager 会尝试使每一列中的所有元素具有相同的宽度和高度,但不同的列可以具有不同的宽度。
  • StaggeredGridLayoutManager 类似于 GridLayoutManager,但它不需要一行中的项目具有相同的高度(对于垂直网格)或同一列中的项目具有相同的宽度(对于水平网格)。结果是同一行或列中的项目最终可能会彼此偏移。

您还需要设计各个项目布局。在下一节中所述设计视图持有者时,您需要此布局。

实现您的适配器和视图持有者

确定布局后,您需要实现您的 AdapterViewHolder。这两个类协同工作以定义如何显示您的数据。ViewHolderView 的包装器,其中包含列表中单个项目的布局。Adapter 根据需要创建 ViewHolder 对象,并设置这些视图的数据。将视图与其数据关联的过程称为 *绑定*。

定义适配器时,您将重写三个关键方法

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

  • onBindViewHolder()RecyclerView 调用此方法将 ViewHolder 与数据关联。该方法获取相应的数据,并使用这些数据填充视图持有者的布局。例如,如果 RecyclerView 显示名称列表,则该方法可能会在列表中找到相应的名称,并填充视图持有者的 TextView 小部件。

  • getItemCount()RecyclerView 调用此方法以获取数据集的大小。例如,在通讯录应用程序中,这可能是地址的总数。RecyclerView 使用此信息来确定不再有可以显示的项目。

这是一个简单的适配器示例,它包含一个嵌套的ViewHolder,用于显示数据列表。在本例中,RecyclerView 显示一个简单的文本元素列表。适配器接收一个包含ViewHolder元素文本的字符串数组。

Kotlin

class CustomAdapter(private val dataSet: Array<String>) :
        RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder)
     */
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView

        init {
            // Define click listener for the ViewHolder's View
            textView = view.findViewById(R.id.textView)
        }
    }

    // Create new views (invoked by the layout manager)
    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        // Create a new view, which defines the UI of the list item
        val view = LayoutInflater.from(viewGroup.context)
                .inflate(R.layout.text_row_item, viewGroup, false)

        return ViewHolder(view)
    }

    // Replace the contents of a view (invoked by the layout manager)
    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {

        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        viewHolder.textView.text = dataSet[position]
    }

    // Return the size of your dataset (invoked by the layout manager)
    override fun getItemCount() = dataSet.size

}

Java

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>

后续步骤

以下代码片段展示了如何使用RecyclerView

Kotlin

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val dataset = arrayOf("January", "February", "March")
        val customAdapter = CustomAdapter(dataset)

        val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = customAdapter

    }

}

Java

RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.layoutManager = new LinearLayoutManager(this)
recyclerView.setAdapter(customAdapter);

该库还提供了许多自定义实现的方法。更多信息,请参阅高级 RecyclerView 自定义

更多资源

有关 Android 测试的更多信息,请参阅以下资源。

示例应用