做爲一名Android開發者,用過ListView或者RecycleView後想必對ViewHolder再熟悉不過了。ViewHolder 一開始並非 Android 原生提供的,而是在ListView中做爲減小頻繁調用findViewById
而引入的,再到後來推出了更好的 RecycleView,直接內置了ViewHolder。不過咱們總歸逃脫不了在寫適配器時寫ViewHolder
或者findViewById
的命運。android
而如今Kotlin的出現彷佛垂手可得地解決了這個問題。你可能還記得,引入Kotlin後,Activity中能夠直接用佈局文件的Id來使用view,原理能夠看下之前寫過的一篇文章Kotlin直接使用控件ID原理解析,本文就是用這個特性來封裝一個極簡地不須要本身建立ViewHolder的通用RecycleView適配器。bash
首先Kotlin上述特性在普通View中默認是關閉,打開app的build.gradle
,啓用實驗性功能:app
android {
...
}
androidExtensions {
experimental = true
}
複製代碼
而後建立一個通用的ViewHolder,很簡單隻有一行代碼:ide
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)
itemView.setOnClickListener { mOnItemClickListener?.invoke(it, viewHolder.adapterPosition) }
itemView.setOnLongClickListener { return@setOnLongClickListener mOnItemLongClickListener.invoke(it, viewHolder.adapterPosition) }
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
:post
<?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>
複製代碼
咱們能夠經過繼承這個適配器來建立:gradle
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的形式直接建立:ui
val adapter = BaseRecyclerAdapter<String>(R.layout.item_textview) {
onBindViewHolder { holder, position ->
holder.textview.text = getItem(position)
}
}
複製代碼