孟老板 ListAdapter封装, 告别Adapter代码 (上)

ListAdapter封装 (上) - SimpleAdapter 

前言:

上一篇文章已经讲解 ListAdapter 的基本使用;  这次 我们不再关心 实体类型、Item事件回调,  编写统一 DiffCallback,  布局ID传入. 

 

使用:

1. 首先新建 BaseItem: 列表实体都会实现此接口.  它是 BaseAdapter 的默认实体类型

interface BaseItem {
    /**
     *  条目更新标记, 用于 DiffUtil areContentsTheSame
     */
    var hasChanged: Boolean

    /**
     *  Adapter 中的 ItemType.
     * 单类型布局可以不用关心 该返回值
     * 多类型布局中;  需直接返回布局Id;  例如: R.layout.item_test
     */
    fun getMItemType(): Int = 0
}

 

2. 再创建实体类,  实现 BaseItem 接口;  除了  hasChanged,  只有一个 title 参数;

class TestEntity(
    var title: String? = null,
    override var hasChanged: Boolean = false
) : BaseItem

 

3.编写通用的 DiffCallback;  实体类型就用 BaseItem 

areItemsTheSame(): 我们选用比较内存地址的方式; 

areContentsTheSame(): 我们选择状态标记方式;

class DiffCallback: DiffUtil.ItemCallback<BaseItem>() {
    /**
     * 比较两个条目对象  是否为同一个Item
     */
    override fun areItemsTheSame(oldItem: BaseItem, newItem: BaseItem): Boolean {
        return oldItem === newItem
    }

    /**
     * 再确定为同一条目的情况下;  再去比较 item 的内容是否发生变化;
     * 我们使用 状态标识方式判断;
     * @return true: 代表无变化;  false: 有变化;
     */
    override fun areContentsTheSame(oldItem: BaseItem, newItem: BaseItem): Boolean {
        return !oldItem.hasChanged
    }
}

 

4.接下来是 ViewHolder:  用于 item 绑定数据,及缓存控件.   我们加入事件处理 handler; 以及重置状态标记;

open class NewViewHolder(val binding: ViewDataBinding, private val handler: BaseHandler?) : RecyclerView.ViewHolder(binding.root){
    open fun bind(item: BaseItem?) {
        //重置 状态标记
        item?.hasChanged = false
        binding.setVariable(BR.item, item)
        binding.setVariable(BR.handler, handler)
        binding.executePendingBindings()
    }
}

 

5. Handler : MVVM 模式中 Item事件响应处理类;   

基类: BaseHandler   它几乎只出现在 Adapter相关基类中,省的我们再写泛型;   要使用还得用它的子类

子类: Handler  提供具体实体泛型.  供布局文件使用

/**
 * item 事件响应基类, 这类什么都不用写
 */
open class BaseHandler


/**
 * item 事件响应基类, 需提供具体的实体类泛型;
 */
abstract class Handler<T: BaseItem> : BaseHandler() {
    abstract fun onClick(view: View, info: T)
}

 

6.下面是重头戏了:  BaseAdapter

重头戏?但它却如此简单  [捂脸] (自带转义);   只需要传入 BaseHandler 对象;  并重写 onBindViewHolder 即可;  

我还重写了 submitList();  因为ListAdapter 提交数据时,会判断前后数据集合是否为同一内存地址;  我们让它必定以新数据集对象传入; (虽然个人感觉这样不对, 但博主还就踩上这坑了 [机智])

abstract class BaseAdapter(
    protected val handler: BaseHandler? = null) :
    ListAdapter<BaseItem, NewViewHolder>(DiffCallback()) {

    override fun onBindViewHolder(holder: NewViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    /**
     * 重写 提交数据方法, 让它必定以新数据集合对象传入
     */
    override fun submitList(list: MutableList<out BaseItem>?) {
        val newData = mutableListOf<BaseItem>()
        if (list != null) {
            newData.addAll(list)
        }
        super.submitList(newData)
    }
}

 

7. 重头戏中的重头来了 SimpleAdapter: 

好吧, 它更简单.  继承 BaseAdapter 并传入 layoutId, 再重写 onCreateViewHolder 即可;

open class SimpleAdapter(
    /**
     * 布局id;
     */
    private val layout: Int,
    handler: BaseHandler? = null
) :
    BaseAdapter(handler) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder {
        return NewViewHolder(
            DataBindingUtil.inflate(
                LayoutInflater.from(parent.context), layout, parent, false
            ), handler
        )
    }
}

 

8. 好了封装完事.  接下来我们要开始用了!  使用也非常简单

//只需要传入  布局id 及 事件响应 Handler
mAdapter = SimpleAdapter(R.layout.item_test_mvvm, object : Handler<TestEntity>(){
    override fun onClick(view: View, info: TestEntity) {
        Toast.makeText(mActivity, "点了条目", Toast.LENGTH_SHORT).show()
    }
})
mDataBind.rvRecycle.let {
    it.layoutManager = LinearLayoutManager(mActivity)
    it.adapter = mAdapter
}
val data = mutableListOf(TestEntity("第一条"), TestEntity("第一条"), TestEntity("第一条"), TestEntity("第一条"), TestEntity("第一条"))
mAdapter.submitList(data)

 

9. 好吧,再贴出我的 布局文件: 

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="item"
            type="com.example.kotlinmvpframe.network.entity.TestEntity" />
        <variable
            name="handler"
            type="com.example.kotlinmvpframe.test.testtwo.Handler" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:onClick="@{(view) -> handler.onClick(view, item)}"
        android:padding="20dp">
        <TextView
            android:id="@+id/btn2"
            style="@style/tv_base_16_dark"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{item.title}" />
    </LinearLayout>
</layout>

 

好的! over

下一篇再讲  多条目类型, 嵌套RecycleView, 封装头尾 等等! 敬请期待

回到顶部

 

posted @ 2021-06-10 18:56  孟老板  阅读(121)  评论(0编辑  收藏  举报