1、RecyclerView关键类介绍
在Android开发中,只要有列表页,基本上就是RecyclerView。RecyclerView非常强大、非常灵活且非常重要。在RecyclerView出现之前其实已经有ListView和GridView等列表控件了。但是由于它们的使用相对复杂,对实现动画等各项定制难度较大,不利于实现业务需求,RecyclerView诞生了。
关于RecyclerView有以下几个关键的类:
- Adapter:适配器模式中的适配器,开发者需要自定义一个Adapter的子类并设置给RecyclerView。它主要负责ViewHolder的创建和数据的绑定。
- ViewHolder:顾名思义就是View的持有者。
- LayoutManager:布局管理器,负责列表项的测量、布局等操作。
- ItemDecoration:Item的装饰器,可以实现列表分割线等操作。
- RecyclerView:RecyclerView本身负责以上几个类的调度、滑动事件的处理等。
2、最简单的使用
RecyclerView的使用步骤:
- 获取RecyclerView,通过findViewById或者new的方式
- 创建Adapter类,并给RecyclerView设置Adapter实例
- 设置LayoutManager
- 设置ItemDecoration,可选
class SimplestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val contentView = RecyclerView(this)
// 必须设置LayoutManager
contentView.layoutManager = LinearLayoutManager(this)
// 必须设置Adapter
contentView.adapter = TextAdapter()
// ItemDecoration可以设置,也可以不设置,也可以设置多个
contentView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
setContentView(contentView, ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
))
}
private class TextAdapter: RecyclerView.Adapter<TextViewHolder>() {
private val datas = ArrayList<String>()
init {
// 模拟数据
for (i in 1 .. 100) {
datas.add("Item $i")
}
}
/**
* 加载布局并创建ViewHolder
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextViewHolder {
val inflater = LayoutInflater.from(parent.context)
val itemView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false)
return TextViewHolder(itemView)
}
/**
* 绑定数据
*/
override fun onBindViewHolder(holder: TextViewHolder, position: Int) {
holder.bind(datas[position])
}
override fun getItemCount() = datas.size
}
private class TextViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
var data: String? = null
val textView = itemView as TextView
init {
itemView.setOnClickListener(this)
}
fun bind(data: String) {
this.data = data
textView.text = data
}
override fun onClick(v: View) {
Toast.makeText(v.context, "$data was clicked", Toast.LENGTH_SHORT).show()
}
}
}
效果:
需要注意的是,必须给RecyclerView设置Adapter和LayoutManager,缺少前者,RecyclerView不知道该展示什么内容,缺少后者,RecyclerView不知道该怎么展示内容。这两者不设置的时候,我们会在Logcat中看到如下日志:
E/RecyclerView: No adapter attached; skipping layout
E/RecyclerView: No layout manager attached; skipping layout
接下来,展示一下网格布局的列表,只需要在以上代码的基础上稍作修改即可:
// 必须设置LayoutManager
contentView.layoutManager = GridLayoutManager(this, /*spanCount*/ 3)
// 必须设置Adapter
contentView.adapter = TextAdapter()
// ItemDecoration可以设置,也可以不设置,也可以设置多个
contentView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
contentView.addItemDecoration(DividerItemDecoration(this, RecyclerView.HORIZONTAL))
效果:
其中spanCount参数代表一行中有几个列。可以看到,我们只需要指定不同的LayoutManager就可以实现不同的布局样式了而不需要像以前一样把ListView替换成GridView。在实际业务开发中,我们可以根据主题设置动态地设置RecyclerView的LayoutManager实现线性列表和网格风格的切换。另外,如果Item本身在列表和网格的情况下的布局并不一样的情况下,可以针对两种情况实现不同的Adapter和ViewHolder。
除了LinearlayoutManager和GridLayoutManager外,Android还提供了一个StaggeredGridLayoutManager用于实现瀑布流布局。这里只展示代码中不同的部分:
class SimplestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
....
// 必须设置LayoutManager
contentView.layoutManager = StaggeredGridLayoutManager(3, RecyclerView.VERTICAL)
// 必须设置Adapter
contentView.adapter = TextAdapter()
// 为了看得清楚,这里ItemDecoration设置成左右各有一部分空白
contentView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
outRect.left = 25
outRect.right = 25
outRect.top = 25
outRect.bottom = 25
}
})
...
}
private class TextAdapter: RecyclerView.Adapter<TextViewHolder>() {
...
/**
* 加载布局并创建ViewHolder
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextViewHolder {
val itemView = TextView(parent.context)
itemView.gravity = Gravity.CENTER
itemView.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
return TextViewHolder(itemView)
}
...
}
private class TextViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
...
fun bind(data: String) {
this.data = data
textView.text = data
val layoutParams = itemView.layoutParams
val even = adapterPosition % 2 == 0
layoutParams.height = (if(even) 0 else 50) + 300
itemView.layoutParams = layoutParams
itemView.setBackgroundColor(Color.parseColor(if(even) "#FFAAAA" else "#AAFFFF"))
}
...
}
}
效果:
这三个LayoutManager都可以指定滚动的方向,不指定的话默认纵向滚动。
待续…