通用回收站视图或如何不编写样板代码

我们都编写应用程序,并且都有列表。 最明显的解决方案是RecyclerView。 实现本身并不复杂,并且不再需要在RecyclerView上编写指南。 但是有一件事。 每当我们需要一个列表时,我们都会创建一个类,在其中指定模板方法,并创建模板类。 当我们有2-3个列表时,那没有什么错。 但是,如果其中有10个或更多,则您不再需要这样做。

面对一个问题,我开始寻找。 在Kotlin上找到了一个非常有趣的实现。 我喜欢它,但是缺少一些元素。 花了几个小时后,我得以完成它,现在适配器的实现需要几行。 在这里,我想与您分享。

我们需要做的第一件事是创建一个适配器。

abstract class GenericAdapter<T> : RecyclerView.Adapter<RecyclerView.ViewHolder> { private var itemList = mutableListOf<T>() constructor() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return getViewHolder( LayoutInflater.from(parent.context) .inflate(viewType, parent, false) , viewType ) } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { (holder as Binder<T>).bind(itemList[position], itemClickListener) } override fun getItemCount(): Int = itemList.size override fun getItemViewType(position: Int): Int = getLayoutId(position, itemList[position]) fun update(items: List<T>) { itemList = items.toMutableList() notifyDataSetChanged() } protected abstract fun getLayoutId(position: Int, obj: T): Int protected open fun getViewHolder(view: View, viewType: Int): RecyclerView.ViewHolder { return ViewHolderFactory.create(view, viewType) } internal interface Binder<T> { fun bind(data: T) } } 

这是怎么回事 我们创建一个参数化适配器,并在其中重新定义基本模板方法。 我们为接口创建一个参数化的Binder接口,我们的ViewHolder必须实现该接口。 在抽象的getLayoutId()方法中,我们将设置布局。

在为ViewHolder创建Factory之后。

 object ViewHolderFactory { fun create(view: View, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { R.layout.item_data -> DataViewHolder(view) R.layout.item_other_data -> OtherDataViewHolder(view) else -> throw Exception("Wrong view type") } } class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), GenericAdapter.Binder<Data> { override fun bind(data: Data) { itemView.apply { dateTextView.text = data.dateTitle } } class OtherDataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), GenericAdapter.Binder<OtherData> { override fun bind(data: OtherData) { itemView.apply { dateTextView.text = data.dateTitle } } } 

这就是片段中此适配器的实现的样子。

 private lateinit var adapter GenericAdapter<Data> protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); adapter = dataAdapter } private val dataAdapter = object : GenericAdapter<Data>() { override fun getLayoutId(position: Int, obj: Data): Int = R.layout.item_data } 

一切都很酷,方便,快捷。 我大致以这种形式找到了这种实现。 但是后来我想到了,可点击元素呢? 这是我的决定。

首先,创建一个界面

 interface OnItemClickListener<T> { fun onClickItem(data: T) } 

并将其传递到我们的资料夹界面
 internal interface Binder<T> { fun bind(data: T, listener: OnItemClickListener<T>?) } 

并在适配器中,创建一个附加的构造函数:

  private var itemClickListener: OnItemClickListener<T>? = null constructor(listener: OnItemClickListener<T>) { itemClickListener = listener } class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), GenericAdapter.Binder<Data> { override fun bind(data: Data, listener: OnItemClickListener<Data>?) { itemView.apply { dateTextView.text = data.dateTitle setOnClickListener { listener?.onClickItem(data) } } } 

最终,我们有了一个3行创建的适配器和一个适用于所有类型元素的通用接口。 如果我们不需要处理点击,则只需将侦听器传递给构造函数即可。 但这还不是全部。

如果我们想将DiffUtils.Callback绑定到适配器怎么办。

 class GenericDiffUtil<T>( private val oldItems: List<T>, private val newItems: List<T>, private val itemDiff: GenericItemDiff<T> ) : DiffUtil.Callback() { override fun getOldListSize() = oldItems.size override fun getNewListSize() = newItems.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = itemDiff.isSame(oldItems, newItems, oldItemPosition, newItemPosition) override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = itemDiff.isSameContent(oldItems, newItems, oldItemPosition, newItemPosition) } interface GenericItemDiff<T> { fun isSame( oldItems: List<T>, newItems: List<T>, oldItemPosition: Int, newItemPosition: Int ): Boolean fun isSameContent( oldItems: List<T>, newItems: List<T>, oldItemPosition: Int, newItemPosition: Int ): Boolean } 

这就是DiffUtils的基类。 向我们的适配器添加方法

 private var diffUtil: GenericItemDiff<T>? = null fun setDiffUtilCallback(diffUtilImpl: GenericItemDiff<T>) { diffUtil = diffUtilImpl } 

并略微修改适配器的方法更新()
  fun update(items: List<T>) { if (diffUtil != null) { val result = DiffUtil.calculateDiff(GenericDiffUtil(itemList, items, diffUtil!!)) itemList.clear() itemList.addAll(items) result.dispatchUpdatesTo(this) } else { itemList = items.toMutableList() notifyDataSetChanged() } } 

因此,我们实现了DiffUtils
 adapter.setDiffUtilCallback(dataDiffUtil) private val dataDiffUtil = object : GenericItemDiff<Data> { override fun isSame( oldItems: List<Data>, newItems: List<Data>, oldItemPosition: Int, newItemPosition: Int ): Boolean { val oldData = oldItems[oldItemPosition] val newData = newItems[newItemPosition] return oldData.id == newData.id } override fun isSameContent( oldItems: List<Data>, newItems: List<Data>, oldItemPosition: Int, newItemPosition: Int ): Boolean { val oldData = oldItems[oldItemPosition] val newData = newItems[newItemPosition] return oldData.name == newData.name && oldData.content == newData.content } 

结果,我们有了一个简单而相当灵活的模板代码实现。 带有多个ViewHolders的适配器的便捷实现。 将逻辑集中在一处。

在这里您可以看到源代码

在这里您可以看到原始版本

Source: https://habr.com/ru/post/zh-CN469557/


All Articles