Android-封装个好用、轻量和通用的原生Adapter基类

基于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)
    }
}
相关推荐
学习使我健康3 小时前
Android App 启动原理
android·android studio
TechMix4 小时前
【性能工具】atrace、systrace、perfetto抓取的trace文件有何不同?
android·性能优化
张小潇4 小时前
AOSP15 WMS/AMS系统开发 - 窗口层级源码分析
android·前端
努力努力再努力wz7 小时前
【MySQL入门系列】掌握表数据的 CRUD:DML 核心语法与执行逻辑解析
android·开发语言·数据结构·数据库·c++·b树·mysql
zh_xuan9 小时前
Android gradle任务
android·gradle构建
Grackers9 小时前
Android Perfetto 系列 10:Binder 调度与锁竞争
android·binder
李白你好10 小时前
Android 自动化渗透测试指令生成
android·自动化
CeshirenTester11 小时前
Claude Code 不只是会写代码:这 10 个 Skills,才是效率分水岭
android·开发语言·kotlin
朝星12 小时前
Android开发[2]:Flow
android·kotlin
zzb158012 小时前
Android Activity 与 Intent 学习笔记
android·笔记·学习