实战 | 认识 RecyclerView
RecyclerView
https://developer.android.google.cn/reference/kotlin/androidx/recyclerview/widget/RecyclerView
RecyclerView 是 "何方神圣"?为什么选择它呢?
粉红色的方格表示屏幕上正在显示的表项,黄色的方格表示屏幕可视范围之外的表项是如何被回收并转为新的视图
RecyclerView 使用 ViewHolder 模式,这样做可以提高性能,因为它无需频繁调用 findViewById() 方法即可访问表项的视图;
RecyclerView 使用 LayoutManager,它支持纵向滑动的列表和横向滑动的列表,以及交错布局的列表和网格布局的列表。您还可以创建自定义的 LayoutManager;
RecyclerView 提供默认的表项动画以及自定义动画的入口。
LayoutManager
https://developer.android.google.cn/reference/androidx/recyclerview/widget/RecyclerView.LayoutManager
实现 RecyclerView
本文会为大家展示如何实现一个简单的 RecyclerView,用它来显示不同种类花的名称。下面的代码会使用 Kotlin 语言,但是 RecyclerView 也可以在 Java 语言中使用。
首先在 Android Studio 里创建一个工程,并且使用 Empty Activity 模板。设置项目名称,并且选择 Kotlin 作为项目所用的语言。
接下来在 app 级的 build.gradle 文件里引入最新版本的 RecyclerView 依赖。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
// 在 https://developer.android.google.cn/jetpack/androidx/releases/recyclerview 获得最新版本号
def recyclerview_version = "1.1.0"
implementation 'androidx.recyclerview:recyclerview:$recyclerview_version
最新版本
https://developer.android.google.cn/jetpack/androidx/releases/recyclerview?hl=zh-cn
RecyclerView 数据
RecyclerView 最重要的组成部分之一就是需要显示的数据。对于比较复杂的应用来说,数据可能是来自数据库或者来自于网络,不过这里我们简单使用字符串资源文件作为应用的数据源。
在 strings.xml 文件中,创建一个字符串数组来存放花的名称。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<resources>
<string name="app_name">RecyclerSample</string>
<string-array name="flower_array">
<item>Lily</item>
<item>Poppy</item>
<item>Sunflower</item>
<item>Freesia</item>
<item>Daisy</item>
<item>Rose</item>
<item>Daffodil</item>
<item>Lavender</item>
<item>Peony</item>
<item>Lilac</item>
<item>Dahlia</item>
<item>Tulip</item>
<item>Dandelion</item>
<item>Geranium</item>
<item>Gardenia</item>
<item>Rhododendron</item>
<item>Azalea</item>
</string-array>
</resources>
然后,创建一个类,名字为 Datasource,并且可以接收一个 Context 类型的参数。创建一个叫做 getFlowerList() 的函数,它负责返回花的名称列表。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class Datasource(val context: Context) {
fun getFlowerList(): Array<String> {
return context.resources.getStringArray(R.array.flower_array)
}
}
在 MainActivity.onCreate() 中,创建一个变量叫做 flowerList,然后将 getFlowerList() 的返回结果赋给它。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flowerList = Datasource(this).getFlowerList()
}
RecyclerView 布局
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="LinearLayoutManager"/>
</FrameLayout>
表项布局
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/flower_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="32sp" />
</FrameLayout>
拆分 Adapter 类
接下来是 RecyclerView 的重头戏了,也就是 ViewHolder 和 Adapter 类。ViewHolder 负责存储 RecyclerView 中每一个单独的表项所需要显示的信息。RecyclerView 仅需要创建当前所显示的表项数量的 ViewHolder 外加缓存中的几个 ViewHolder 即可。随着用户滑动屏幕,ViewHolder 会被回收 (使用新数据进行填充),已有的表项会在一端消失,并且在另一端显示一个新的表项。Adapter 类从数据源获得数据,并且将数据传递给正在更新其所持视图的 ViewHolder。下图显示了 RecyclerView、Adapter、ViewHolder 和数据之间的协作关系。
ViewHolder
https://developer.android.google.cn/reference/androidx/recyclerview/widget/RecyclerView.ViewHolderAdapter
https://developer.android.google.cn/reference/androidx/recyclerview/widget/RecyclerView.Adapter
创建 Adapter
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class FlowerAdapter(val flowerList: Array<String>) {
}
创建 ViewHolder
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class FlowerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
private val flowerTextView:TextView = itemView.findViewById(R.id.flower_text)
fun bind(word: String){
flowerTextView.text = word
}
}
继承 RecyclerView.Adapter
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class FlowerAdapter(val flowerList: Array<String>) :
RecyclerView.Adapter<FlowerAdapter.FlowerViewHolder>() {
}
重写 onCreateViewHolder()
当 ViewHolder 创建的时候会调用该方法。在该方法里进行初始化和填充 RecyclerView 中的表项视图。该视图使用前面我们创建的用于显示文本的布局。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FlowerViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.flower_item, parent, false)
return FlowerViewHolder(view)
}
重写 onBindViewHolder()
onBindViewHolder() 被调用的时候,会传入参数 ViewHolder 和一个位置 (position),它表示在 flowerList 中所绑定的表项的位置。该位置可以用于提取表项所需的数据,并且将数据传递给 ViewHolder 来使数据绑定到对应的 UI。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
override fun onBindViewHolder(holder: FlowerViewHolder, position: Int) {
holder.bind(flowerList[position])
}
重写 getItemCount()
RecyclerView 显示一个列表,所以它需要知道列表里共有多少项。由于 flowerList 就是数据源,所以直接返回它的长度即可。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
override fun getItemCount(): Int {
return flowerList.size
}
完成 Adapter 代码
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class FlowerAdapter(val flowerList: Array<String>) :
RecyclerView.Adapter<FlowerAdapter.FlowerViewHolder>() {
// 描述表项视图并且将它放在 RecyclerView 中
class FlowerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val flowerTextView: TextView = itemView.findViewById(R.id.flower_text)
fun bind(word: String) {
flowerTextView.text = word
}
}
// 返回一个新的 ViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FlowerViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.flower_item, parent, false)
return FlowerViewHolder(view)
}
// 返回数据列表的长度
override fun getItemCount(): Int {
return flowerList.size
}
// 显示一个指定位置的数据
override fun onBindViewHolder(holder: FlowerViewHolder, position: Int) {
holder.bind(flowerList[position])
}
}
连接到 MainActivity
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val flowerList = Datasource(this).getFlowerList()
val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
recyclerView.adapter = FlowerAdapter(flowerList)
}
}
下一步
https://github.com/android/views-widgets-samples/tree/master/RecyclerViewSimple
更多资源,请参阅:
RecyclerView Sample — Kotlin https://github.com/android/views-widgets-samples/tree/master/RecyclerViewKotlinc RecyclerView Sample — Java https://github.com/android/views-widgets-samples/tree/master/RecyclerView RecyclerView Documentation https://developer.android.google.cn/reference/kotlin/androidx/recyclerview/widget/RecyclerView Create a List with RecyclerView https://developer.android.google.cn/guide/topics/ui/layout/recyclerview
推荐阅读