ListAdapter封装, 告别Adapter代码 (二)

ListAdapter封装 (二)- SimpleAdapter


前言

上一篇文章已经讲解 ListAdapter 的基本使用; 这次我们封装简单条目 SimpleAdapter;
我们使用MVVM模式, 且不再关心实体类型、用Handler 做Item事件回调, 编写统一 DiffCallback


推荐文章


提示:以下是本篇文章正文内容,下面案例可供参考

一、实体类

1.BaseItem

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

interface BaseItem {
    /**
     *  条目更新标记, 用于 DiffUtil areContentsTheSame
     */
    var hasChanged: Boolean
	
	/**
     *  如果 DiffCallback areItemsTheSame() 使用 ID来比较. 则函数返回实体主键.
     *  例如: return oldItem.getItemId() == newItem.getItemId()
     */
	// fun getItemId(): String
	
    /**
     *  Adapter 中的 ItemType.
     * 单类型布局可以不用重写它
     * 多类型布局中;  需直接返回布局Id;  例如: R.layout.item_test
     */
    fun getMItemType(): Int = 0
}

2.TestEntity

实体类需要实现 BaseItem; 本类 除了 hasChanged, 就只有一个 title 参数;

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

二、DiffCallback

实体类型就用 BaseItem;

class DiffCallback<T : BaseItem>: DiffUtil.ItemCallback<T>() {
    /**
     * 比较两个条目对象  是否为同一个Item
     * 如果是 Paging + Room, 则需要用 oldItem.getItemId() == newItem.getItemId() 方式
     */
    override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
        return oldItem === newItem
    }

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

三、ViewHolder, Handler

1.ViewHolder

ViewHolder 在 Adapter 中起到了 为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()
    }
}

2.Handler

  • 基类: BaseHandler 它只出现在 Adapter基类中,省的我们写泛型; 要用还得用它的子类
  • 子类: Handler 提供具体实体泛型. 供布局文件使用
/**
 * item 事件响应基类, 这类什么都不用写
 */
open class BaseHandler

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

四、Adapter

1. BaseAdapter

重头戏: BaseAdapter; 但它却如此简单.
只需要传入 BaseHandler 对象; 并重写 onBindViewHolder 即可;
我还重写了 submitList(); (虽然个人感觉这样不对, 但博主还就踩上这坑了)
[机智]

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

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

    /**
     * 重写 提交数据方法, 让它必定以新数据集合对象传入;
     * 这种方式 只是写法简单. 有时会导致不必要的集合重装. 
     */
    override fun submitList(list: MutableList<out T>?) {
        val newData = mutableListOf<T>()
        if (list != null) {
            newData.addAll(list)
        }
        super.submitList(newData)
    }
}

2.SimpleAdapter

重头戏中的重头来了 SimpleAdapter:
好吧, 它更简单. 继承 BaseAdapter 并传入 layoutId, 再重写 onCreateViewHolder 即可;

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

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

五、开始使用

封装如此简单, 那我们就开始画页面了。

1.Activity

只需要初始化 Adapter, RecycleView, 并设置数据即可;

//只需要传入  布局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()
    }
})
// DataBinding 懂得都懂 -.-
mDataBind.rvRecycle.let {
    it.layoutManager = LinearLayoutManager(mActivity)
    it.adapter = mAdapter
}
val data = mutableListOf(TestEntity("第一条"), TestEntity("第一条"), TestEntity("第一条"), TestEntity("第一条"), TestEntity("第一条"))
mAdapter.submitList(data)

2.Layout

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

<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>

总结

  • 简单列表的 Adapter 就用 SimpleAdapter 即可,
  • 使用MVVM模式 ViewModel 也不用单独定义
  • 我们只需要关心布局写法即可.

上一篇: ListAdapter封装, 告别Adapter代码 (一)- ListAdapter 入门
下一篇: ListAdapter封装, 告别Adapter代码 (三)- 头尾,多类型,嵌套,单多选

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值