使用 RecyclerView 创建动态列表 Android Jetpack 的一部分。
RecyclerView 可以让您轻松高效地显示大量数据。您提供数据并定义每个列表项的外观,而 RecyclerView 库会根据需要动态创建元素。
顾名思义,RecyclerView 会回收这些单个的元素。当列表项滚动出屏幕时,RecyclerView 不会销毁其视图。相反,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。
规划布局
RecyclerView 中的列表项由 LayoutManager 类负责排列。RecyclerView 库提供了三种布局管理器,用于处理最常见的布局情况:
LinearLayoutManager将各个项排列在一维列表中。GridLayoutManager将各个项排列在二维网格中:- 如果网格垂直排列,
GridLayoutManager会尽量使每行中所有元素的宽度和高度相同,但不同的行可以有不同的高度。 - 如果网格水平排列,
GridLayoutManager会尽量使每列中所有元素的宽度和高度相同,但不同的列可以有不同的宽度。
- 如果网格垂直排列,
StaggeredGridLayoutManager与GridLayoutManager类似,但不要求同一行中的列表项具有相同的高度(垂直网格有此要求)或同一列中的列表项具有相同的宽度(水平网格有此要求)。其结果是,同一行或同一列中的列表项可能会错落不齐。
您还需要设计各个列表项的布局。在设计 ViewHolder 时,您需要使用此布局,如下一部分所述。
实现适配器和 ViewHolder
确定布局后,您需要实现 Adapter 和 ViewHolder。这两个类配合使用,共同定义数据的显示方式。ViewHolder 是包含列表中各列表项的布局的 View 的封装容器。Adapter 会根据需要创建 ViewHolder 对象,还会为这些视图设置数据。将视图与其数据相关联的过程称为“绑定”。
定义适配器时,您需要替换三个关键方法:
onCreateViewHolder():每当RecyclerView需要创建新的ViewHolder时,它都会调用此方法。此方法会创建并初始化ViewHolder及其关联的View,但不会填充视图的内容,因为ViewHolder此时尚未绑定到具体数据。onBindViewHolder():RecyclerView调用此方法将ViewHolder与数据相关联。此方法会提取适当的数据,并使用该数据填充 ViewHolder 的布局。例如,如果RecyclerView显示的是一个名称列表,该方法可能会在列表中查找适当的名称,并填充 ViewHolder 的TextViewwidget。getItemCount():RecyclerView调用此方法来获取数据集的大小。例如,在通讯簿应用中,这可能是地址总数。 RecyclerView 使用此方法来确定什么时候没有更多的列表项可以显示。
下面是一个典型的简单 Adapter 示例,该 Adapter 包含一个显示数据列表的嵌套 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 自定义。
启用无边框显示
如需为 RecyclerView 启用无边框显示,请按以下步骤操作:
- 通过调用
enableEdgeToEdge()设置向后兼容的无边框显示。 - 如果列表项最初与系统栏重叠,请在
RecyclerView上应用边衬区。您可以通过将android:fitsSystemWindows设置为true或使用ViewCompat.setOnApplyWindowInsetsListener来实现此目的。 - 通过在
RecyclerView上将android:clipToPadding设置为false,允许列表项在滚动时绘制在系统栏下方。
以下视频显示了停用(左侧)和启用(右侧)全屏显示的 RecyclerView:
插页代码示例:
Kotlin
ViewCompat.setOnApplyWindowInsetsListener( findViewById(R.id.my_recycler_view) ) { v, insets -> val innerPadding = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() // If using EditText, also add // "or WindowInsetsCompat.Type.ime()" to // maintain focus when opening the IME ) v.setPadding( innerPadding.left, innerPadding.top, innerPadding.right, innerPadding.bottom) insets }
Java
ViewCompat.setOnApplyWindowInsetsListener( activity.findViewById(R.id.my_recycler_view), (v, insets) -> { Insets innerPadding = insets.getInsets( WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() // If using EditText, also add // "| WindowInsetsCompat.Type.ime()" to // maintain focus when opening the IME ); v.setPadding( innerPadding.left, innerPadding.top, innerPadding.right, innerPadding.bottom ); return insets; } );
RecyclerView XML:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />
其他资源
如需详细了解如何在 Android 平台上进行测试,请参阅以下资源。