一个资深的Android是不是应该学会自己做一个超级的RecyclerView-Adapter?

  • ItemViewType 需要自己维护一套常量控制
  • onBindViewHolder 随着业务的复杂,变得越来越臃肿

从零开始,我们自己写一个舒服的Adapter

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以前我们的实现,要分别实现Adapter、ViewHolder、Model,而且它们之间耦合严重,遇到一个复杂的列表真是苦不堪言。

现在我们要实现如下目标

  • 通用ViewHolder,不再重写ViewHolder
  • 通用Adapter,不再重写Adapter
  • 只关注实现ViewModel,并实现View根据ViewModel的变化而变化,自动做到局部刷新(不是简单粗暴的NotifyDataSetChange)

通用的ViewHolder

class DefaultViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
/**

  • views缓存
    */
    private val views: SparseArray = SparseArray()
    val mContext: Context = view.context

fun getView(@IdRes viewId: Int): T? {
return retrieveView(viewId)
}

private fun retrieveView(@IdRes viewId: Int): T? {
var view = views[viewId]
if (view == null) {
view = itemView.findViewById(viewId)
if (view == null) return null
views.put(viewId, view)
}
return view as T
}
}

ViewHolder职责很单一,就是负责持有View的引用,辅助你用对的View做对的事,这里优化了一点就是用到SparseArray缓存,其实就是做了简单的优化,防止再次findViewById造成不必要的损耗。BaseRecyclerViewAdapterHelper的ViewHolder也是用的这个实现,这是大家公认的比较靠谱的写法。

ViewModel抽象

ViewModel这层很关键,它负责View数据的绑定逻辑和负责加载哪个Layout,来直接看代码

public abstract class ViewModel<M, VH extends RecyclerView.ViewHolder> {

public M model;
public VH viewHolder;

public abstract void onBindView(RecyclerView.Adapter<?> adapter);

int getItemViewType() {
return getLayoutRes();
}

@LayoutRes
public abstract int getLayoutRes();

}

  • M 数据源的抽象,负责提供什么样子的数据
  • VH 默认是DefaultViewHolder,当然也可以有其他的扩展,这里给扩展留有余地
  • onBindView 负责将M绑定到VH的逻辑,这里回传Adapter是为了以后不同的ViewModel有数据交互的情况,这里就可以通过Adapter拿到关联的值,并且可以通过它去刷新其他Item,是不是很聪明。
  • getItemViewType 这个大家应该知道,这里是RecyclerView适配不同布局Layout的关键参数,默认是getLayoutRes,因为不同的布局=不同的LayoutRes,当然你也可以扩展变更逻辑,但目前来看没必要变。
  • getLayoutRes 也就是R.layout.item_layout,获取布局的引用。

这么设计最大的亮点就是少了ItemViewType的维护,让你看看别人的设计,下面是别人的代码,维护ItemViewType,吓人不,如果以后再多一种EMPTY_VIEW,那我是不是得扩展一个EMPTY_VIEW2啊,而且还要修改这里的逻辑,这么设计不科学啊,应该永远或者说尽量不要动底层逻辑才对,因为你动了底层逻辑就要面临的全面测试。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在我看来,最好的设计是永远不要关心ItemViewType的逻辑,而所谓的头部View和底部View只是你维护在List顶端和低端的数据,最终根据List的排序绑定到ItemView上,而不是通过ItemViewType去控制,这点你细细品味。而EmptyView更像是一个压在RecyclerView上面的栈,或者你把List改成一个Empty的ViewModel并全屏展示的RecyclerView上,当有真实数据的时候将其移除掉,总之我们操作的就是ViewModel的去和留,保持Adapter底层逻辑的简洁。

通用Adapter

史上最简单的通用Adapter就要出现了,鼓掌把朋友

abstract class ListAdapter<VM : ViewModel<*, *>> : RecyclerView.Adapter() {

protected val layouts: SparseIntArray by lazy(LazyThreadSafetyMode.NONE) { SparseIntArray() }

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DefaultViewHolder {
return DefaultViewHolder(LayoutInflater.from(parent.context).inflate(layouts[viewType], parent, false))
}

override fun getItemViewType(position: Int): Int {
val item = getItem(position)
layouts.append(item.itemViewType, item.layoutRes)
return item.itemViewType
}

override fun onBindViewHolder(holder: DefaultViewHolder, position: Int) {
val item = getItem(position)
item.onBindView(this)
}

abstract fun getItem(position: Int): VM

}

class ArrayListAdapter : ListAdapter<ArrayListViewModel>() {

private val observableDataList = ObservableArrayList<ArrayListViewModel>()

init {

observableDataList.addOnListChangedCallback(object : OnListChangedCallback<ObservableArrayList<ArrayListViewModel>>() {

override fun onChanged(sender: ObservableArrayList<ArrayListViewModel>) {
notifyDataSetChanged()
}

override fun onItemRangeChanged(sender: ObservableArrayList<ArrayListViewModel>, positionStart: Int, itemCount: Int) {
notifyItemRangeChanged(positionStart, itemCount)
}

override fun onItemRangeInserted(sender: ObservableArrayList<ArrayListViewModel>, positionStart: Int, itemCount: Int) {
notifyItemRangeInserted(positionStart, itemCount)
}

override fun onItemRangeMoved(sender: ObservableArrayList<ArrayListViewModel>, fromPosition: Int, toPosition: Int, itemCount: Int) {
notifyItemMoved(fromPosition, toPosition)
}

override fun onItemRangeRemoved(sender: ObservableArrayList<ArrayListViewModel>, positionStart: Int, itemCount: Int) {
notifyItemRangeRemoved(positionStart, itemCount)
}
})

}

override fun getItem(position: Int): ArrayListViewModel {
return observableDataList[position]
}

override fun getItemCount(): Int {
return observableDataList.size
}

fun add(index: Int, element: ArrayListViewModel) {
observableDataList.add(index, element)
}

fun removeAt(index: Int): ArrayListViewModel {
return observableDataList.removeAt(index)
}

fun set(index: Int, element: ArrayListViewModel): ArrayListViewModel {
return observableDataList.set(index, element)
}
}

70多行代码搞定,超级简单把。

ListAdapter 抽象类

  • layouts SparseIntArray的实现,以itemViewType为Key负责缓存layoutRes,这里这样写其实是为了兼容你扩展了ViewModel的getItemViewType的实现逻辑,当然默认情况下itemViewType就是layoutRes,所以也可以不用缓存,但我们保持我们框架的扩展性,打开这个大门让你自定义。有的人就喜欢给itemViewType定义特殊的常量,我能有什么办法,有人肯定反驳,你这没法自定义啊,设计的好垃圾,哈哈,随他去。

  • onCreateViewHolder 好多人都喜欢给Adapter传Context进来然后创建LayoutInflater,其实不然,你完全可以用parent.context,学会了没?这里用到了layouts缓存的layoutRes,来加载对应的View布局

  • getItemViewType 根据position获取对应的ViewModel,然后通过ViewModel拿到itemViewType,然后顺便缓存下layoutRes,嗯,完美。

  • onBindViewHolder 通过position拿到对应的ViewModel,然后回调ViewModel的onBindView,触发Model绑定到对应的View上,嗯,完美。

  • getItem 返回对应的ViewModel,子类负责实现,因为子类实现缓存的List是不同的实现,所以对应的获取方式有可能会不同,所以需要抽象出来。

ArrayListAdapter

  • observableDataList ObservableArrayList的实现,是Databinding里的实现,是一个对ArrayList的包装子类,如果你项目没有引用Databinding,那么请你学我,把这三个类拿过来就ok了

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

贴图不复制类名可耻(我没做到,你呢?):

CallbackRegistry
ListChangeRegistry
ObservableList
ObservableArrayList

  • addOnListChangedCallback 添加对observableDataList的监听OnListChangedCallback,然后在数据刷新的时候分别调用onItemRangeChanged、onItemRangeInserted、onItemRangeMoved、onItemRangeRemoved,当你修改observableDataList集合的元素的时候,对应的就会回调到这里,是不是也很简单

尾声

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值