使用RecyclerView显示文字列表
1. 创建和使用RecyclerView
在刚接触RecyclerView的时候,我寻思着这不就是一个和ListView一样的UI组件吗,直接再MainActivety里操作还不行吗。看我暴力拖动:
RecyclerView的确出现了,但是还差不少意思,因为我想要显示的文字列表并不非那莪简单就能实现
这就体现了代码的重要性了,抛去想当然,如果想要使RecylerView成功显示,需要搞清四个部分
列表项:哪些数据需要我们去显示?
Adapter:如何获取这些数据?(适配器)
ViewHolder:把这些数据放哪儿(一个个视图池)
RecyclerView:最终显示给用户的样子
接着,我们就逐个击破,在击破之前,还需要切换一下布局。我们常用的布局是ConstraintLayout,在放置多个子视图的时候用它再合适不过了,但是我们只有一个RecyclerView子视图,用它似乎就有些冗余,不如使用简单的FrameLayout FrameLayout的代码块如下,将它复制到activity_main.xml中
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</FrameLayout>
2.列表项
这一部分很容易理解,就是准备数据 首先在res>values>strings中添加列表数据:
创建一个DataSource.kt,在里面调用你的数据列表
package com.example.affirmations.data
import com.example.affirmations.R
import com.example.affirmations.model.Affirmation
class Datasource {
fun loadAffirmations(): List<Affirmation>{
return listOf<Affirmation>(
Affirmation(R.string.affirmation1),
Affirmation(R.string.affirmation2),
Affirmation(R.string.affirmation3),
Affirmation(R.string.affirmation4),
Affirmation(R.string.affirmation5),
Affirmation(R.string.affirmation6),
Affirmation(R.string.affirmation7),
Affirmation(R.string.affirmation8),
Affirmation(R.string.affirmation9),
Affirmation(R.string.affirmation10)
)
}
}
3.RecyclerView
这是最容易击破的了,就是开局的暴力拖动,当然也可以直接在FrameLayout中添加代码
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutManager="LinearLayoutManager"
android:scrollbars="vertical"
/>
4.Adapter以及ViewHolder
这一部分就不太容易了,慢慢来。 首先,RecyclerView中的每一个列表项都有自己的布局,为了定义每一个布局,我们可以在单独的布局文件中定义这些布局。 所以先在 res > layout 中,新建名为 list_item.xml 的空 File;在 Code 视图中打开 list_item.xml;添加 id 为 item_title 的 TextView;针对 layout_width 和 layout_height 添加 wrap_content,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
接着,创建一个ItemAdapter类:
ItemAdapter需要获取文字列表以及有关如何解析字符串资源信息,文字列表好办,向里面传一个列表就完事儿了,那么解析字符串资源的信息从哪里来呢?其实这些信息和有关·应用的信息都被存储在了context中,所以context也要被传进去。如下:
class ItemAdapter(private val context: Context, private val dataset: List<Affirmation>) {
}
紧接着,需要创建一个ViewHolder RecyclerView不会直接和列表项试图交互,而是处理ViewHolder。啥是VeiwHolder?一个ViewHolder代表RecyclerView中的一个列表项视图,可以根据需要重复使用。 So,继续在ItemAdapter中创建一个嵌套类(不强制嵌套类哈~这里代表ItemViewHOlder仅由ItemAdapter使用,有助于别人了解你的程序结构):
class ItemAdapter(
private val context: Context,
private val dataset: List<Affirmation>
) : RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {
class ItemViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
val textView: TextView = view.findViewById(R.id.item_title)
}
}
代码仍然会报错,因为ItemAdapter这里继承了一个父类,这个父类是一个抽象类,所以我们需要完成这个函数的所有功能 将鼠标转移到ItemAdapter上,按ctrl+l,有三个方法出现了: getItemCount():列表中有多少个元素要显示?即后续要多少个ViewHolder onCreateViewHolder():创建ViewHolder onBindViewHolder():
全部OK一下,会出现以下代码:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
TODO("Not yet implemented")
}
override fun getItemCount(): Int {
TODO("Not yet implemented")
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
TODO("Not yet implemented")
}
所以,我们仍然需要逐个击破一下。 首先解决getItemCount()
//得知数据集的大小
override fun getItemCount(): Int {
return dataset.size
}
然后实现onCreateViewHolder(),这一个方法将被布局管理器调用,为RecyclerView创建新的ViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
// 创建一个新视图
val adapterLayout = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return ItemViewHolder(adapterLayout)
}
最后一个了,实现以下onBindViewHolder(),这个方法也是由布局管理器调用,用来替换列表项视图里的内容。
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = dataset[position]
holder.textView.text = context.resources.getString(item.stringResourceId)
}
最终的ItemAdapter.kt代码如下:
package com.example.affirmations.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.affirmations.R
import com.example.affirmations.model.Affirmation
class ItemAdapter(private val context: Context, private val dataset: List<Affirmation>): RecyclerView.Adapter<ItemAdapter.ItemViewHolder>(){
class ItemViewHolder(private val view:View): RecyclerView.ViewHolder(view){
val textView: TextView = view.findViewById(R.id.item_title)
}//嵌套类
//由布局管理器调用
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val adapterLayout = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false) //存储着对视图的引用
return ItemViewHolder(adapterLayout)
}
//返回数据集的大小
//override fun getItemCount() = dataset.size
override fun getItemCount(): Int {
return dataset.size
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.textView.text = context.getString(dataset[position].stringResourceId)
}
}
5.修改MainActivity以使用RecyclerView
最后,就需要用MainActivity去调用你刚刚创建的类和方法了。 具体代码如下:
package com.example.affirmations
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.affirmations.adapter.ItemAdapter
import com.example.affirmations.data.Datasource
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//将Datasource实例化,调用其中的loadAffirmations方法
val myDataset = Datasource().loadAffirmations()
var recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.adapter = ItemAdapter(this,myDataset)
recyclerView.setHasFixedSize(true)
}
}
最终的效果如下:
详细代码已上传