1. 程式人生 > >BaseRecyclerViewAdapterHelper開源專案之BaseMultiItemQuickAdapter 實現多型別原始碼學習

BaseRecyclerViewAdapterHelper開源專案之BaseMultiItemQuickAdapter 實現多型別原始碼學習

version:2.8.5

今天我們來看下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;

2、預設的佈局型別值,當使用多佈局時,在渲染viewholder的時候型別址不是我們配置的型別值中,就會使用這個。

    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資料的繫結即可。