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)
    }
}
相关推荐
Jomurphys22 分钟前
Compose 适配 - 通过 UiMediaScope 获取设备信息
android·compose
阿巴斯甜29 分钟前
必看12
android
阿巴斯甜1 小时前
必看11
android
solo_991 小时前
Perftto 使用命令添加标签
android
阿巴斯甜1 小时前
必看10
android
阿巴斯甜1 小时前
必看9
android
阿巴斯甜1 小时前
必看6
android
angerdream2 小时前
Android手把手编写儿童手机远程监控App之SQLite详解
android
阿巴斯甜2 小时前
必看5
android
雪铃儿2 小时前
Shorebird 之外,Flutter Android 热更新还有什么选择
android·前端