作为一名Android开发者,用过ListView或者RecycleView后想必对ViewHolder再熟悉不过了。ViewHolder 一开始并不是 Android 原生提供的,而是在ListView中作为减少频繁调用findViewById
而引入的,再到后来推出了更好的 RecycleView,直接内置了ViewHolder。不过我们总归逃脱不了在写适配器时写ViewHolder
或者findViewById
的命运。
而现在Kotlin的出现似乎轻而易举地解决了这个问题。你可能还记得,引入Kotlin后,Activity中可以直接用布局文件的Id来使用view,原理可以参考一下这篇文章Kotlin直接使用控件ID原理解析,这篇文章就是用这个特性来封装一个极简地不需要自己创建ViewHolder的通用RecycleView适配器。
通用ViewHolder
首先Kotlin上述特性在普通View中默认是关闭,打开app的build.gradle
,启用实验性功能:
android {
...
}
androidExtensions {
experimental = true
}
然后创建一个通用的ViewHolder,很简单只有一行代码:
class CommonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), LayoutContainer {
override val containerView: View = itemView
}
极简适配器
我们直接上代码:
open class BaseRecyclerAdapter<M>(
@LayoutRes val itemLayoutId: Int, list: Collection<M>? = null,
bind: (BaseRecyclerAdapter<M>.() -> Unit)? = null
) :
RecyclerView.Adapter<BaseRecyclerAdapter.CommonViewHolder>() {
init {
if (bind != null) {
apply(bind)
}
}
private var dataList = mutableListOf<M>()
private var mOnItemClickListener: ((v: View, position: Int) -> Unit)? = null
private var mOnItemLongClickListener: ((v: View, position: Int) -> Boolean) = { _, _ -> false }
private var onBindViewHolder: ((holder: CommonViewHolder, position: Int) -> Unit)? = null
fun onBindViewHolder(onBindViewHolder: ((holder: CommonViewHolder, position: Int) -> Unit)) {
this.onBindViewHolder = onBindViewHolder
}
/**
* 填充数据,此操作会清除原来的数据
*
* @param list 要填充的数据
* @return true:填充成功并调用刷新数据
*/
fun setData(list: Collection<M>?): Boolean {
var result = false
dataList.clear()
if (list != null) {
result = dataList.addAll(list)
}
return result
}
/**
* 根据位置获取一条数据
*
* @param position View的位置
* @return 数据
*/
fun getItem(position: Int) = dataList[position]
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(itemLayoutId, parent, false)
val viewHolder = CommonViewHolder(itemView)
val position = viewHolder.adapterPosition
itemView.setOnClickListener { mOnItemClickListener?.invoke(it, position) }
itemView.setOnLongClickListener { return@setOnLongClickListener mOnItemLongClickListener.invoke(it, position) }
return viewHolder
}
override fun getItemCount() = dataList.size
override fun onBindViewHolder(holder: CommonViewHolder, position: Int) {
if (onBindViewHolder != null) {
onBindViewHolder!!.invoke(holder, position)
} else {
bindData(holder, position)
}
}
open fun bindData(holder: CommonViewHolder, position: Int) {}
class CommonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), LayoutContainer {
override val containerView: View = itemView
}
}
使用
首先创建一个简单的布局item_textview.xml
:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="50dp">
</TextView>
我们可以通过继承这个适配器来创建:
class StringAdapter : BaseRecyclerAdapter<String>(R.layout.item_textview) {
override fun onBindViewHolder(holder: CommonViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
holder.textview.text = getItem(position)
}
}
也可以采用类似DSL的形式直接创建:
val adapter = BaseRecyclerAdapter<String>(R.layout.item_textview) {
onBindViewHolder { holder, position ->
holder.textview.text = getItem(position)
}
}
最后
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
然后文末放上一个技术交流群:Android架构设计(185873940)
群内有许多技术大牛,有任何问题,欢迎广大网友一起来交流,群内还不定期免费分享高阶Android学习视频资料和面试资料包~
再推荐一篇文章,具体的架构视频,面试专题,学习笔记都在这篇文章中:“寒冬未过”,阿里P9架构分享Android必备技术点,让你offer拿到手软!
偷偷说一句:群里高手如云,欢迎大家加群和大佬们一起交流讨论啊!
网友评论