使用 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 上进行测试的更多信息,请参阅以下资源。

示例应用