孟老板 ListAdapter封装, 告别Adapter代码 (二)
- BaseAdapter系列
- ListAdapter封装, 告别Adapter代码 (一)
- ListAdapter封装, 告别Adapter代码 (二)
- ListAdapter封装, 告别Adapter代码 (三)
- ListAdapter封装, 告别Adapter代码 (四)
- Paging3 系列
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, 封装头尾 等等! 敬请期待