基于RecyclerView.Adapter构建该通用适配器,以后其他项目直接拿来用就行~
思考1:
adapter功能纯粹,如 '构建复用条目布局' 和 '绑定条目数据' 等。
基类应具备的能力:构建条目布局、绑定数据、设置数据源、数据源的增删改查等。
思考2:需继承RecyclerView.ViewHolder抽象类
多布局场景的数据源,应具备区分ItemType能力,所以需定义对应接口
思考3:泛型相关RecyclerView.ViewHolder的子类:泛型-参数View (更方便获取控件对象,否则只配苦逼地findViewById)
单布局Adapter基类:泛型-数据源类型、泛型-条目布局ViewBinding
多布局Adapter基类:泛型-数据源类型
废话不多说,直接上菜~
1.模块下build.gradle.kts中配置ViewBinding
ini
android{
...
buildFeatures {
...
viewBinding = true
}
}
2.构建BaseViewHolder (继承RecyclerView.ViewHolder)
kotlin
class BaseViewHolder<VB : ViewBinding>(var viewBinding: VB) : RecyclerView.ViewHolder(viewBinding.root)
★3. 构建单布局适配器BaseRvAdapter
kotlin
/**
* @author yyf
* 单布局-基类适配器
*/
abstract class BaseRvAdapter<D,VB: ViewBinding> (var mContext: Context) : RecyclerView.Adapter<BaseViewHolder<VB>>() {
private val mList = mutableListOf<D>()
override fun getItemCount(): Int = mList.size
var listener: ((View, Int) -> Unit)? = null
fun setOnItemClickListener(listener: ((View, Int) -> Unit)? = null) {
this.listener = listener
}
/*数据设置*/
fun setData(list: MutableList<D>?) {
list?.let {
mList.clear()
mList.addAll(it)
notifyDataSetChanged()
}
}
/*数据获取*/
fun getData(): MutableList<D> {
return mList
}
/*增*/
fun addData(list: MutableList<D>?) {
if (list?.isNotEmpty() == true) {
var start = mList.size
mList.addAll(list)
var end = mList.size - 1
notifyItemRangeChanged(start, end)
}
}
/*删*/
fun removeData(position: Int) {
if (position >= 0 && position < mList.size) {
mList.removeAt(position)
notifyItemRemoved(position)
}
}
/*改*/
fun updateData(position: Int, bean: D) {
if (position >= 0 && position < mList.size) {
mList[position] = bean
notifyItemChanged(position)
}
}
/*查*/
fun getData(position: Int): D? {
if (position >= 0 && position < mList.size) {
return mList[position]
}
return null
}
}
4.定义MultiItem接口(数据bean实现该接口,使其具备区分ItemType能力)
kotlin
interface MultiItem {
fun getItemType(): Int
}
5.★ 构建多布局适配器BaseRvMultiAdapter
kotlin
/**
* @author yyf
* 多布局-基类适配器
*/
abstract class BaseRvMultiAdapter<D : MultiItem> (var mContext: Context) : RecyclerView.Adapter<BaseViewHolder<ViewBinding>>() {
private val mList = mutableListOf<D>()
override fun getItemCount(): Int = mList.size
var listener: ((View, Int) -> Unit)? = null
fun setOnItemClickListener(listener: ((View, Int) -> Unit)? = null) {
this.listener = listener
}
/*数据设置*/
fun setData(list: MutableList<D>?) {
list?.let {
mList.clear()
mList.addAll(it)
notifyDataSetChanged()
}
}
/*数据获取*/
fun getData(): MutableList<D> {
return mList
}
/*增*/
fun addData(list: MutableList<D>?) {
if (list?.isNotEmpty() == true) {
var start = mList.size
mList.addAll(list)
var end = mList.size - 1
notifyItemRangeChanged(start, end)
}
}
/*删*/
fun removeData(position: Int) {
if (position >= 0 && position < mList.size) {
mList.removeAt(position)
notifyItemRemoved(position)
}
}
/*改*/
fun updateData(position: Int, bean: D) {
if (position >= 0 && position < mList.size) {
mList[position] = bean
notifyItemChanged(position)
}
}
/*查*/
fun getData(position: Int): D? {
if (position >= 0 && position < mList.size) {
return mList[position]
}
return null
}
override fun getItemViewType(position: Int): Int {
return mList[position].getItemType()
}
}
干货上齐了,大家亲自尝尝看吧~
--------------------------------------------------- 完 ---------------------------------------------------
以下使用示例可忽略了,没啥看的!
举个栗子:
UI布局 activity_main.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mListRv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
条目布局 item_test.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:background="#80000000"
android:orientation="horizontal">
<TextView
android:id="@+id/mName2Tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="name" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="-" />
<TextView
android:id="@+id/mValue2Tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="value" />
</LinearLayout>
条目布局 item2_test.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/mNameTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="name" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="-" />
<TextView
android:id="@+id/mValueTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="value" />
</LinearLayout>
单布局BaseRvAdapter的使用
kotlin
/**
* @author yyf
* 单布局尝试
*/
class TestNewAdapter(context: Context) : BaseRvAdapter<TestBean, ItemTestBinding>(context) {
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int
): BaseViewHolder<ItemTestBinding> {
return BaseViewHolder<ItemTestBinding>(ItemTestBinding.inflate(LayoutInflater.from(mContext), parent, false))
}
override fun onBindViewHolder(
holder: BaseViewHolder<ItemTestBinding>,
position: Int
) {
getData()[position].let {
holder.viewBinding.mNameTv.text = it.name
holder.viewBinding.mValueTv.text = it.value
holder.viewBinding.root.setOnClickListener {
listener?.invoke(it, position)
}
}
}
}
多布局BaseRvMultiAdapter的使用
kotlin
/**
* @author yyf
* 多布局尝试
*/
class TestNewMultiAdapter(context: Context) : BaseRvMultiAdapter<TestBean>(context) {
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int
): BaseViewHolder<ViewBinding> {
when (viewType) {
0 -> {
return BaseViewHolder<ViewBinding>(ItemTestBinding.inflate(LayoutInflater.from(mContext), parent, false))
}
1 -> {
return BaseViewHolder<ViewBinding>(Item2TestBinding.inflate(LayoutInflater.from(mContext), parent, false))
}
}
return BaseViewHolder<ViewBinding>(ItemTestBinding.inflate(LayoutInflater.from(mContext), parent, false))
}
override fun onBindViewHolder(
holder: BaseViewHolder<ViewBinding>,
position: Int
) {
getData()[position].let { bean->
when (getItemViewType(position)) {
0 -> {
(holder.viewBinding as ItemTestBinding).mNameTv.text = bean.name
(holder.viewBinding as ItemTestBinding).mValueTv.text = bean.value
}
1 -> {
(holder.viewBinding as Item2TestBinding).mName2Tv.text = bean.name
(holder.viewBinding as Item2TestBinding).mValue2Tv.text = bean.value
}
}
holder.viewBinding.root.setOnClickListener {
listener?.invoke(it, position)
}
}
}
}
UI层调用
kotlin
class MainActivity : ComponentActivity() {
val viewBinding by lazy { ActivityMainBinding.inflate(LayoutInflater.from(this@MainActivity)) }
// val mAdapter by lazy { TestNewAdapter(this@MainActivity) } //单布局适配器
val mAdapter by lazy { TestNewMultiAdapter(this@MainActivity) } //多布局适配器
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(viewBinding.root)
viewBinding.mListRv.layoutManager = LinearLayoutManager(this@MainActivity)
viewBinding.mListRv.adapter = mAdapter
mAdapter.setOnItemClickListener { view, position ->
Toast.makeText(this@MainActivity, "点击了 position=$position", Toast.LENGTH_SHORT).show()
}
requestData()
}
private fun requestData() {
var list = mutableListOf<TestBean>()
for (index in 0 until 30) {
var bean = TestBean()
bean.name = "name $index"
bean.value = "value $index"
bean.type = index % 2
list.add(bean)
}
mAdapter.setData(list)
}
}