BaseRecyclerViewAdapterHelper開源項(xiàng)目之BaseSectionQuickAdapter 實(shí)現(xiàn)分組效果的源碼學(xué)習(xí)

version:2.8.5

更多分享請(qǐng)看:http://cherylgood.cn

今天我們來學(xué)習(xí)下BaseRecyclerViewAdapterHelpler開源項(xiàng)目中是如何實(shí)現(xiàn)分組想過的。

首先今天的學(xué)習(xí)我們還是按照前面的學(xué)習(xí)思路,根據(jù)getItemViewType->onCreateDefViewHolder->onBindViewHolder,即從確認(rèn)viewholder類型->根據(jù)類型值創(chuàng)建viewholder->根據(jù)數(shù)據(jù)源類型綁定數(shù)據(jù)到viewholder上。

第一步:我們看一下BaseSectionQuickAdapter這個(gè)類的定義

public abstract class BaseSectionQuickAdapter extends BaseQuickAdapter {

跟前面分析的多類型BaseMultiItemQuickAdapter差不多,只是我們的數(shù)據(jù)源需要繼承自SetionEntity。那么這個(gè)SetionEntity做了什么事呢,我們來看下源碼:

package com.chad.library.adapter.base.entity;

/**

* https://github.com/CymChad/BaseRecyclerViewAdapterHelper

*/

public abstract class SectionEntity {

public boolean isHeader;

public T t;

public String header;

public SectionEntity(boolean isHeader, String header) {

this.isHeader = isHeader;

this.header = header;

this.t = null;

}

public SectionEntity(T t) {

this.isHeader = false;

this.header = null;

this.t = t;

}

}

從源碼可以看出,他是一個(gè)抽象類,可能你會(huì)問,為什么要定義成抽象類呢,為什么不定義成接口或者普通類呢。

以下理由僅由我意想得出,大家也可以發(fā)表下自己的看法:

1、我們定義SectionEntity這個(gè)類,目的自然是希望用戶的bean都具有某些規(guī)范,而我們的BaseSectionQuickAdapter將根據(jù)該規(guī)范進(jìn)行數(shù)據(jù)的處理。雖然使用普通類一樣能達(dá)到相同的效果,但是不推薦,我覺得這讓有可能會(huì)讓用戶忽略我們所需要讓用戶知道的規(guī)范。

2、接口類,接口類其實(shí)是特殊的抽象類,上次分析的MultiItemEntity為什么又定義成接口類型呢,

public interface MultiItemEntity {

int getItemType();

}

根據(jù)實(shí)際需求而定,因?yàn)槲覀冊(cè)趯?shí)現(xiàn)多類型時(shí),只需要用戶的數(shù)據(jù)源提供一個(gè)類型值給我們即可,所以此時(shí)定義成接口類是最為合適的,因?yàn)橛脩魯?shù)據(jù)源只要實(shí)現(xiàn)了該接口,他必須實(shí)現(xiàn)接口的方法,而我們需要的恰恰是在使用時(shí)調(diào)用該接口即可。

但是在SetionEntity中,我們幫用戶多做點(diǎn)事,為其提供兩個(gè)構(gòu)造方法,一個(gè)時(shí)分組頭,一個(gè)是分組體。而此時(shí)如果是定義成接口類,是不符合需求的,因?yàn)榻涌陬惖姆椒ú荒苡蟹椒w等。

SectionEntity代碼分析:從源碼可以看出,假如我們當(dāng)前數(shù)據(jù)是分組頭,那么我們?cè)趧?chuàng)建bean時(shí)使用

public SectionEntity(boolean isHeader, String header) {

this.isHeader = isHeader;

this.header = header;

this.t = null;

}

即可,當(dāng)前定義分組頭只有個(gè)string類型的分組頭名字,你在繼承時(shí)可以根據(jù)實(shí)際需求進(jìn)行擴(kuò)展,內(nèi)部調(diào)用父類的該構(gòu)造方法即可。

如果時(shí)普通的數(shù)據(jù)bean:調(diào)用以下構(gòu)造方法即可,當(dāng)然你也可以進(jìn)行擴(kuò)展,根據(jù)個(gè)人需求而定。

public SectionEntity(T t) {

this.isHeader = false;

this.header = null;

this.t = t;

}

看完了SectionEntity。我們來看BaseSectionQuickAdapter的源碼:

package com.chad.library.adapter.base;

import android.view.ViewGroup;

import com.chad.library.adapter.base.entity.SectionEntity;

import java.util.List;

/**

* https://github.com/CymChad/BaseRecyclerViewAdapterHelper

*/

public abstract class BaseSectionQuickAdapter extends BaseQuickAdapter {

protected int mSectionHeadResId;

protected static final int SECTION_HEADER_VIEW = 0x00000444;

/**

* Same as QuickAdapter#QuickAdapter(Context,int) but with

* some initialization data.

*

* @param sectionHeadResId The section head layout id for each item

* @param layoutResId? ? ? The layout resource id of each item.

* @param data? ? ? ? ? ? A new list is created out of this one to avoid mutable list

*/

public BaseSectionQuickAdapter( int layoutResId, int sectionHeadResId, List data) {

super(layoutResId, data);

this.mSectionHeadResId = sectionHeadResId;

}

@Override

protected int getDefItemViewType(int position) {

return? mData.get(position).isHeader ? SECTION_HEADER_VIEW : 0;

}

@Override

protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {

if (viewType == SECTION_HEADER_VIEW)

return createBaseViewHolder(getItemView(mSectionHeadResId, parent));

return super.onCreateDefViewHolder(parent, viewType);

}

@Override

public void onBindViewHolder(K holder, int positions) {

switch (holder.getItemViewType()) {

case SECTION_HEADER_VIEW:

setFullSpan(holder);

convertHead(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));

break;

default:

super.onBindViewHolder(holder, positions);

break;

}

}

protected abstract void convertHead(BaseViewHolder helper, T item);

}

大家可以看到,源碼比較少,跟BaseMultiItemQuickAdapter是一樣的思路。

字段解析:

protected int mSectionHeadResId;

mSectionHeadResId用來保存我們分組頭的布局資源ids;

protected static final int SECTION_HEADER_VIEW = 0x00000444;

定義了一個(gè)默認(rèn)的分組頭類型。思想與實(shí)現(xiàn)多類型一致;

我們?cè)趯?shí)例化BaseSectionQuickAdapter時(shí)需要多傳遞一個(gè)分組頭的資源ids過來,所以構(gòu)造方法是這樣的:

/**

* Same as QuickAdapter#QuickAdapter(Context,int) but with

* some initialization data.

*

* @param sectionHeadResId The section head layout id for each item

* @param layoutResId? ? ? The layout resource id of each item.

* @param data? ? ? ? ? ? A new list is created out of this one to avoid mutable list

*/

public BaseSectionQuickAdapter( int layoutResId, int sectionHeadResId, List data) {

super(layoutResId, data);

this.mSectionHeadResId = sectionHeadResId;

}

構(gòu)造好之后,我們也是利用來adapter的生命周期方法:

1、重寫getDefItemViewType方法,前面也解釋過為什么不是重寫Recycler.adapter的getItemViewType方法,以為我們的BaseQuickAdapter對(duì)其進(jìn)行來包裝。最終在getItemViewType方法中會(huì)調(diào)用我們的getDefItemViewType方法。

重寫該方法所做的事不多:

@Override

protected int getDefItemViewType(int position) {

return? mData.get(position).isHeader ? SECTION_HEADER_VIEW : 0;

}

根據(jù)我們當(dāng)前位置的數(shù)據(jù)bean,判斷當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)bean是不是分組頭bean,如果是,返回SECTION_HEADER_VIEW告訴BaseQuickAdapter,你要?jiǎng)?chuàng)建的viewholder是分組頭類型的viewholder。否則返回0(0時(shí)RecyclerView.Adapter的缺省值,前面有分析)

接下來,我們也同樣是重寫了onCreateDefViewHolder方法。

@Override

protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {

if (viewType == SECTION_HEADER_VIEW)

return createBaseViewHolder(getItemView(mSectionHeadResId, parent));

return super.onCreateDefViewHolder(parent, viewType);

}

根據(jù)返回的類型值,如果是SECTION_HEADER_VIEW 那么我們就創(chuàng)建一個(gè)分組頭viewholder返回。否則調(diào)用父類的方法按原流程走。

在這里我們還需要重寫onBindViewHolder方法,因?yàn)槲覀円嘧鰞杉虑椋?/p>

1、對(duì)我們的分組頭進(jìn)行特殊處理;

2、增加一個(gè)分組頭數(shù)據(jù)綁定的抽象方法的調(diào)用;

@Override

public void onBindViewHolder(K holder, int positions) {

switch (holder.getItemViewType()) {

case SECTION_HEADER_VIEW:

setFullSpan(holder);

convertHead(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));

break;

default:

super.onBindViewHolder(holder, positions);

break;

}

}

里面有個(gè)很有趣的方法。setFullSpan,從字面上理解是設(shè)置充滿空間,我們來看下代碼:

/**

* When set to true, the item will layout using all span area. That means, if orientation

* is vertical, the view will have full width; if orientation is horizontal, the view will

* have full height.

* if the hold view use StaggeredGridLayoutManager they should using all span area

*

* @param holder True if this item should traverse all spans.

*/

protected void setFullSpan(RecyclerView.ViewHolder holder) {

if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {

StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder

.itemView.getLayoutParams();

params.setFullSpan(true);

}

}

里面原來是對(duì)Layoutmanager為StaggeredGridLayoutManager類型時(shí)做特殊處理,大家可以去了解下StaggeredGridLayoutManager這種類型的LayoutManager。

最后會(huì)調(diào)用一個(gè)方法

params.setFullSpan(true);

繼續(xù)看該方法源碼:

/**

* When set to true, the item will layout using all span area. That means, if orientation

* is vertical, the view will have full width; if orientation is horizontal, the view will

* have full height.

*

* @param fullSpan True if this item should traverse all spans.

* @see #isFullSpan()

*/

public void setFullSpan(boolean fullSpan) {

mFullSpan = fullSpan;

}

該方法是StaggeredGridLayoutManager提供的,英文說明的大意是:

如果你設(shè)置true,當(dāng)前item將使用布局的所有空間。如果是垂直的,會(huì)沾滿水平方向的寬度空間,如果是水平,會(huì)占滿垂直方向的高度空間。

然后將holder和當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)bean作為參數(shù)調(diào)用convertHead函數(shù)。

所以當(dāng)你實(shí)現(xiàn)的是帶分組頭的adapter時(shí),會(huì)多出一個(gè)數(shù)據(jù)綁定的回調(diào)接口:

protected abstract void convertHead(BaseViewHolder helper, T item);

可能你還會(huì)看到以下代碼:

convertHead(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));

里面有一句holder.getLayoutPosition()。

getLayoutPosition是干什么用的呢,因?yàn)镽ecyclerView的item的布局和渲染其實(shí)是交給layoutManager來完成的,所以adapter中的item的位置可能跟data的index匹配不上,而getLayoutPosition將返回給我們當(dāng)前viewholder在recyclerView中的最新位置信息。

總結(jié):分析思路還是老套路,根據(jù)一個(gè)組件的生命周期及業(yè)務(wù)流程進(jìn)行分析,掌握一個(gè)控件的執(zhí)行流程是理解一個(gè)控件的實(shí)現(xiàn)的一個(gè)較好的方法,本人是這么認(rèn)為的,也是這么做的,大家有更好的學(xué)習(xí)方法可以多多留言,多多交流,沒有最好,只有更好!以上即為本次的代碼學(xué)習(xí),希望對(duì)大家有幫助。后面會(huì)繼續(xù)分析其他功能的源碼,歡迎一起學(xué)習(xí)!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容