RecyclerView理解-整体设计

滑动列表展示数据在应用开发中已经基本是最为常见的一个场景,RecyclerView作为Android新钦定的列表展示控件在5.0版本的support包中出现,又经过了后续版本的改进,目前已经有一万多行代码。其对于性能的优化和灵活的设计可以说又是Android源码中的一个经典,非常值得我们学习观摩。

RecyclerView的使用不再赘述,我们从其整体设计开始看。

RecyclerView整体的模块化设计

随便看一眼RecyclerView的源码,会发现其中有一大半是内部类的实现。这是RecyclerView设计的一大特点,RecyclerView采用了模块化的设计,将各个部分职责分别抽出来进行了模块化的封装。其中最主要的有:最复杂的LayoutManager负责整个界面的布局工作,ItemAnimator负责界面变化时的动画,ItemDecoration负责界面上的装饰效果,Recycler负责缓存回收复用工作。

其中,开发者更是可以自己编写LayoutManager,ItemAnimator,ItemDecoration实现各种各样的列表展示效果。这里要提及的是,这几者都封装得非常浅,这使得开发者拥有极大的自由度,但也导致自行编写的工作量非常大,还好附带的几个默认实现已经基本能满足绝大部分的需求。

模块化的设计使得一万多行复杂的代码分离开来,既利于维护,又方便今后的扩展,在阅读时也能最快地在宏观上把握作者的思路(虽然阅读源码还是非常头疼)。

ViewHolder的引入与适配器模式的使用

在实际开发中的数据展示场景,我们一般拿到的是一个数据的列表,而需要将其转化成视图列表展示出来。在这个场景下RecyclerView对外采用了适配器模式,将数据和视图两部分完全抽象出来。

数据部分就是我们拿到的数据列表,可能是来自网络,也可能是来自SQLite,它的类型有无限种可能性。

而视图部分,在这里RecyclerView引入了一个ViewHolder的概念,一个ViewHolder个体就是一个数据项对应的视图的抽象。ViewHolder并不继承于View,它只是被封装出来用于标准化整个RecyclerView范围内的视图个体,RecyclerView内部所有的展示、更新、回收、复用都是以ViewHolder为操作个体的,而实际被添加进ViewTree的是其内部的itemView。所以在代码层面来讲,可以说ViewHolder是封装了itemView与其一些状态的一个包裹类。

在编写Adapter的时候我们必须要重写getItemCount()onCreateViewHolder()onBindViewHolder(),其意义就是,前者就是对于每个数据项创建一个对应的ViewHolder并且描述它长什么样,交给RecyclerView;后者则是建立数据与ViewHolder的绑定关系。

如图,在这里RecyclerView使用了适配器模式以后,Adapter将RecyclerView和Data完全隔离开来,RecyclerView根本不需要知道任何与数据相关的信息,只由Adapter负责将一个个数据项适配成一个个ViewHolder后交给RecyclerView,RecyclerView再来对它熟知的ViewHolder进行安排。这样做的好处是,既保证了多变的数据部分的极大灵活性,又能让RecyclerView以不变应万变,处理各种各样的数据。这样一来,RecyclerView就能专注于它作为一个ViewGroup掌管视图绘制的使命。至于针对不同数据而需要做的相应处理,由开发者来根据实际情况在Adapter中编写就好了。就像我们通常会在Adapter中保留一份数据的引用,然后封装一些方法对它进行诸如添加,修改之类的操作,不管你怎么操作数据,只要最后将它转化成ViewHolder就可以。

Adapter数据更新与观察者模式的使用

在数据更新的时候,RecyclerView与它的Adapter通过观察者模式的配合来实现界面的同步更新。

具体的实现方法是,Adapter中持有一个AdapterDataObservable对象,AdapterDataObservable直接继承了Java源码中的Observable<T>,这是一个由Java提供的观察者模式模板。

public static abstract class Adapter<VH extends ViewHolder> {
        private final AdapterDataObservable mObservable = new AdapterDataObservable();
        ...
}

与此同时,RecyclerView中持有一个RecyclerViewDataObserver。

public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
        private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
        ...
}

于是我们完全可以认为,在这里,就是由RecyclerView作为一个观察者,时刻关注着被观察者Adapter中的数据变动,从而在数据发生变动的时候作出界面上的响应。

那Observer是何时注册到Observable的呢?在RecyclerView调用setAdapter()设置自己的Adapter时,内部的setAdapterInternal()会调用adapter的registerAdapterDataObserver()将自己的Observer注册到Adapter的Observable上。

private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        ...
        if (adapter != null) {
            adapter.registerAdapterDataObserver(mObserver);//注册
            ...
        }
        ...
    }

从这个时候开始,数据和界面就算绑定到了一起了。我们都知道,和ListView一样,在数据有更新的时候我们应该调用Adapter的notifyDataSetChanged()方法,这样RecyclerView就会知道要对界面进行重绘了。而这个通知过程在这里的实现,就是mObservable通过notifyDataSetChanged()notifyChanged()通知在自己这注册了的所有观察者mObserver数据产生了变动,要调用自己的onChanged()回调方法响应变动了。而这个需要由观察者重写的onChanged()方法:

 private class RecyclerViewDataObserver extends AdapterDataObserver {
        ...
        @Override
        public void onChanged() {
            assertNotInLayoutOrScroll(null);
            mState.mStructureChanged = true;

            setDataSetChangedAfterLayout();
            if (!mAdapterHelper.hasPendingUpdates()) {
                requestLayout();//请求界面重绘
            }
        }
        ...
}

可以看到最终会调用requestLayout()向上级请求界面重绘,于是界面就会刷新了。

我们上面说会通知所有注册了的观察者,事实上在这个套路中,一般注册在Adapter里的观察者只有RecyclerView中的mObserver一个人,但是事实上完全可以有多个观察者。也就是说,你可以给多个RecyclerView设置同一个Adapter,甚至自己创造一个观察者去关注Adapter数据的变动。

在这里使用观察者模式后,RecyclerView获知数据变动不需要依赖于Adapter,而只依赖于抽象的被观察者,这样的设计使得他们两者之间的耦合度更加低,面向未来其它形式的扩展更加灵活。

在RecyclerView诞生时,由于已经有先前大量的ListView的设计使用经验,我们可以发现RecyclerView设计的目的非常明确。设计者将开发者在列表展示这个业务场景中接触最频繁、自定义需求最多的几个,如布局、动画模块,单独抽了出来,留下了接口供开发者自行设计。而在对于其它开发者不需要太关注的实现细节上,均已经进行了非常细致的实现,而且花了非常多的功夫进行性能上的优化。在布局上,默认提供的3种LayoutManager非常实用,基本上能覆盖大部分的开发需求,而灵活的RecyclerView还能实现很多别的旧控件一模一样甚至更佳的效果。另外在回收复用方面,RecyclerView已经实现了Scrap机制、CacheView机制与RecycleViewPool机制几种缓存方式,但仍然留下了一个ViewCacheExtension可供开发者根据自己的实际情况自行设计一层额外的缓存。可以说RecyclerView如今的热门不是没有理由的,它本身就是一个非常经典的设计。

    原文作者:澳门记者
    原文地址: https://www.jianshu.com/p/1e024af1d92c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞