BaseRecyclerViewAdapterHelper开源项目BaseMultiItemQuickAdapter实现多类型源码学习
今天我们来看下BaseRecyclerViewAdapterHelper是如何实现多布局的。首先我们要实现多类型布局,我们的adapter不再是继承自BaseQuickAdapter类,而是继承自其的子类,BaseMultiItemQuickAdapter。而且数据源类型需要继承自MultiItemEntity,MultiItemEntity是一个接口,代码很少:
package com.chad.library.adapter.base.entity; /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper */ public interface MultiItemEntity { int getItemType(); }
其主要用意是我们的数据源继承MultiItemEntity,这样子,我们可以在数据源中动态的返回一个int类型的数值(代表某一类型的item),大家可以想一下,我们在渲染viewholder的时候,如果想实现多类型的viewholder,而viewholder的类型展示又跟所需绑定的数据息息相关,那么如果我们在数据源中提供一种确定viewholder类型的能力,理论上是不是就可以达到我们要的效果了?我们带着这样的一个假设继续往下看。而这个BaseMultiItemQuickAdapter 是何许人也,是如何实现多类型布局的呢?我们来看下源码:
package com.chad.library.adapter.base; import android.support.annotation.LayoutRes; import android.util.SparseArray; import android.view.ViewGroup; import com.chad.library.adapter.base.entity.MultiItemEntity; import java.util.List; /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper */ public abstract class BaseMultiItemQuickAdapter extends BaseQuickAdapter<T, K> { /** * layouts indexed with their types */ private SparseArray layouts; private static final int DEFAULT_VIEW_TYPE = -0xff; /** * Same as QuickAdapter#QuickAdapter(Context,int) but with * some initialization data. * * @param data A new list is created out of this one to avoid mutable list */ public BaseMultiItemQuickAdapter( List data) { super( data); } @Override protected int getDefItemViewType(int position) { Object item = mData.get(position); if (item instanceof MultiItemEntity) { return ((MultiItemEntity)item).getItemType(); } return DEFAULT_VIEW_TYPE; } protected void setDefaultViewTypeLayout(@LayoutRes int layoutResId) { addItemType(DEFAULT_VIEW_TYPE, layoutResId); } @Override protected K onCreateDefViewHolder(ViewGroup parent, int viewType) { return createBaseViewHolder(parent, getLayoutId(viewType)); } private int getLayoutId(int viewType) { return layouts.get(viewType); } protected void addItemType(int type, @LayoutRes int layoutResId) { if (layouts == null) { layouts = new SparseArray<>(); } layouts.put(type, layoutResId); } }
1、存储我们的布局资源的ids
private SparseArray layouts;
private static final int DEFAULT_VIEW_TYPE = -0xff;
接下来,我们以一个BaseMultiItemQuickAdapter的创建过程来分析代码:之前我们分析了BaseQuickAdapter的代码,其执行过程是一样的,我们实现多布局功能的切入口无非是
1、在执行getItemViewType时的能够根据我们的数据源返回对应的布局类型值。
2、在onCreateDefViewHolder 能够正确拿到类型值进行viewholder的渲染。
3、我们在onBindViewHolder中根据传递给我们的数据源中接口定义的getItemViewType方法返回的类型值来确定当前的viewholder是什么类型的,需要绑定什么数据。(注:之前分析了adapter的加载数据时的生命周期方法:getItemViewType->onCreateDefViewHolder->onBindViewHolder,如果不大清楚可以看下前面的文章)
所以,我们在BaseMultiItemQuickAdapter 里面重写了getDefItemViewType方法,为什么时重写getDefItemViewType方法而不是getItemViewType方法呢?这可不是我糊弄你,因为我们在BaseQuickAdapter里面重写了getItemViewType方法,而在getItemViewType方法里调用了getDefItemViewType方法来回去类型值,该方法也在之前的分析BaseQuickAdapter源码的文章中分析了的。
重写之后做了什么呢?看代码:
@Override protected int getDefItemViewType(int position) { Object item = mData.get(position); if (item instanceof MultiItemEntity) { return ((MultiItemEntity)item).getItemType(); } return DEFAULT_VIEW_TYPE; }
很简单,因为我们的数据源实现了MultiItemEntity接口。直接判断该position的数据是不是实现了MultiItemEntity接口,是调用接口的getItemType方法返回类型值,不是返回默认类型值。第一步返回类型值的代码改造完成了,接下来第二部就是根据类型值渲染viewholder。BaseMultiItemQuickAdapter直接重写了onCreateDefViewHolder 方法来实现该扩展:
@Override protected K onCreateDefViewHolder(ViewGroup parent, int viewType) { return createBaseViewHolder(parent, getLayoutId(viewType)); }
代码很简单,从我们存储布局缓存的字段中根据viewType返回对象的布局资源的ids。所以BaseMultiItemQuickAdapter 还给我们包装了一个addItemType方法:
protected void addItemType(int type, @LayoutRes int layoutResId) { if (layouts == null) { layouts = new SparseArray<>(); } layouts.put(type, layoutResId); }
该方法很简单,就是将不同的布局资源的ids和对应的类型值存储起来。所以我们的创建多布局的时候,需要的构造函数中调用addItemType来添加不同的布局资源。最后一步,绑定数据;一般绑定数据实在onBindViewHolder中实现的,而我们的BaseRecyclerViewAdapterHelper对其进行了包装,提供了一个convert方法,所以我们只需要的。convert方法中根据数据源数据节点的类型值判断绑定的是那个布局的数据即可。
总结:理解了adapter加载数据的生命周期方法的执行顺序很重要(getItemViewType->onCreateDefViewHolder->onBindViewHolder)。只要控制viewType的返回、viewholder的渲染。viewholder数据的绑定即可。