使用 RecyclerView 创建动态列表 Android Jetpack 的一部分。
RecyclerView 使高效显示大量数据变得容易。您提供数据并定义每个项目的显示方式,RecyclerView 库会在需要时动态创建元素。
顾名思义,RecyclerView 会回收这些单个元素。当某个项目滚动出屏幕时,RecyclerView 不会销毁其视图。相反,RecyclerView 会重用该视图以用于已滚动到屏幕上的新项目。RecyclerView 提高了性能和应用的响应速度,并降低了功耗。
关键类
几个类协同工作以构建您的动态列表。
RecyclerView
是包含与您的数据对应的视图的ViewGroup
。它本身就是一个视图,因此您可以像添加任何其他 UI 元素一样将RecyclerView
添加到您的布局中。列表中的每个单个元素都由一个视图持有者对象定义。创建视图持有者时,它没有任何与其关联的数据。创建视图持有者后,
RecyclerView
会将其绑定到其数据。您可以通过扩展RecyclerView.ViewHolder
来定义视图持有者。通过调用适配器中的方法,
RecyclerView
请求视图并将视图绑定到其数据。您可以通过扩展RecyclerView.Adapter
来定义适配器。布局管理器排列列表中的各个元素。您可以使用 RecyclerView 库提供的布局管理器之一,也可以定义自己的布局管理器。布局管理器都基于库的
LayoutManager
抽象类。
您可以在 RecyclerView 示例应用(Kotlin) 或 RecyclerView 示例应用(Java) 中了解所有部分如何组合在一起。
实现 RecyclerView 的步骤
如果您要使用 RecyclerView,则需要执行以下几件事。在以下部分中将详细解释它们。
确定列表或网格的外观。通常,您可以使用 RecyclerView 库的标准布局管理器之一。
设计列表中每个元素的外观和行为。根据此设计,扩展
ViewHolder
类。您的ViewHolder
版本提供了列表项的所有功能。您的视图持有者是View
的包装器,该视图由RecyclerView
管理。定义将您的数据与
ViewHolder
视图关联的Adapter
。
还有 高级自定义选项,让您可以根据自己的确切需求调整 RecyclerView。
规划您的布局
RecyclerView 中的项目由 LayoutManager
类排列。RecyclerView 库提供了三个布局管理器,它们处理最常见的布局情况
LinearLayoutManager
将项目排列在一维列表中。GridLayoutManager
将项目排列在二维网格中- 如果网格垂直排列,
GridLayoutManager
会尝试使每行中的所有元素具有相同的宽度和高度,但不同的行可以具有不同的高度。 - 如果网格水平排列,
GridLayoutManager
会尝试使每列中的所有元素具有相同的宽度和高度,但不同的列可以具有不同的宽度。
- 如果网格垂直排列,
StaggeredGridLayoutManager
类似于GridLayoutManager
,但它不要求一行中的项目具有相同的高度(对于垂直网格)或同一列中的项目具有相同的宽度(对于水平网格)。结果是,一行或一列中的项目最终可能会彼此偏移。
您还需要设计单个项目的外观。在下一节中所述的设计视图持有者时,需要此布局。
实现您的适配器和视图持有者
确定布局后,您需要实现您的 Adapter
和 ViewHolder
。这两个类协同工作以定义如何显示您的数据。ViewHolder
是 View
的包装器,其中包含列表中单个项目的布局。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 上进行测试的更多信息,请参阅以下资源。