大家好,今天又帶來了專案中具體遇到的需求。做一個首介面,該首介面有很多功能塊,同時這些功能塊是動態的,因為登入的人的許可權的不同,會顯示不同的功能塊,因為功能模組的數量不一定,所以當功能塊多的時候,整個介面是可以上下滑動的。其實類似有點像淘寶的首介面。如下圖所示。
首先我說的不是最優的。可能有更好的解決方式。我這裡也只是嘗試了新的方式而已。
我先說下我最剛開始想到的最傳統的想法:
1.首先因為功能塊多的時候,需要介面能夠滾動,所以我想到最外面用的ScrollView,然後ScrollView中包含了一個豎向排布的LienarLayout。
然後在放入一個ImageView顯示這個頂部圖片:
然後需要二個橫向的LinearLayout,用來顯示這個大的分類標題:
然後再放入二個GridView顯示功能模組:
OK。我發現我的首介面寫好了之後:
<ScrollView>
<LinearLayout>
<ImageView> <頂部圖片>
<LinearLayout><View/><TextView/><LinearLayout> <我的服務標題欄>
<GridView /> <我的服務功能塊>
<LinearLayout><View/><TextView/><LinearLayout> <我的功能標題欄>
<GridView /> <我的功能功能塊>
<LinearLayout>
<ScrollView/>複製程式碼
1.佈局的內容非常之多。維護很不方便
2.定製化功能差了很多,如果我下次想在《我的服務》和《我的功能》大功能分類中,再多加一個《我的售後》,又的去佈局中查詢相應的位置,然後去去新增新的佈局程式碼,或者是我想刪除模組功能了,我還得去佈局中找出來,然後去刪除它。反正就是很麻煩。
3.當前這個介面還算簡單的,畢竟功能塊都是以類似九宮格的形式呈現,如果哪天多了個《我的售後》,然後這個《我的售後》不是以這種九宮格的形式呈現,整個介面中有各種各樣的布句呈現,管理會變的十分麻煩。
有沒有什麼好的辦法呢。
上面說到過。我們的介面有沒有像淘寶的首介面,各種布句雜糅在一起,然後又可以上下滾動,沒錯,那我就模仿淘寶的首頁一樣寫個不就行了麼。
這時候我的思路就變了:整個介面就使用一個RecycleView來完成。
然後裡面的不同佈局方式使用不同的LayoutManager不就可以了麼。當然因為前面講了我們可以模仿淘寶的首頁來寫,那我們當然是使用阿里巴巴開源的vlayout。
這時候介紹一下我們的主角:vlayout
vlayout is a powerfull LayoutManager extension for RecyclerView, it provides a group of layouts for RecyclerView. Make it able to handle a complicate situation when grid, list and other layouts in the same recyclerview.
我們可以看到,vlayout是一個強大的RecycleView的LayoutManager,它可以幫我在RecycleView中呈現多種佈局方式。
正式起航:
首先,vlayout的基本使用方法,其他大神寫的很多也很好。我也不會浪費時間再寫一遍:
請看這篇,基本就能夠對Vlayout有所瞭解及使用了:
重要的事情說三遍!!!大家一定要看一遍再使用。
Android開源庫V – Layout:淘寶、天貓都在用的UI框架,趕緊用起來吧!
Android開源庫V – Layout:淘寶、天貓都在用的UI框架,趕緊用起來吧!
Android開源庫V – Layout:淘寶、天貓都在用的UI框架,趕緊用起來吧!
我們回頭再來看我們上面的具體的專案需求:
(我會先用VLayout實現一種簡單的處理。然後再實現更加通用的處理!一定要看完最後的通用處理哦!)
簡單處理:
我們首先整個activity的佈局變為了:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/work_recycleview"
android:background="@android:color/white"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
>
</android.support.v7.widget.RecyclerView>複製程式碼
是不是變的乾淨簡潔了!!!
然後我們要使用Vlayout來設定我們RecycleView中的各種佈局。
RecycleView workRecycleview = (RecycleView)findViewById(R.id.work_recycleview);
//建立我們的委託LayoutManger
VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);
workRecycleview.setLayoutManager(layoutManager);
//通過這個layoutManager來管理一系列的LayoutHelper
//所以我們先建立一個List來存放等會要用到的LayoutHelper.
List<LayoutHelper> helperList = new LinkedList<>();
//1.
//因為第一個底部圖片就這一項,所以我們就直接使用SingleLayoutHelper
SingleLayoutHelper bannerLayoutHelper = new SingleLayoutHelper();
bannerLayoutHelper.setItemCount(1);
helperList.add(bannerLayoutHelper);
//2.
//因為大標題欄是一個橫向的LinearLayout,所以使用LinearLayoutHelper
LinearLayoutHelper personTitleHelper = new LinearLayoutHelper();
personTitleHelper.setItemCount(1);
helperList.add(personTitleHelper);
//3.
//因為功能塊目前是九宮格,所以使用的是GridLayoutHelper
GridLayoutHelper personGridHelper = new GridLayoutHelper(3);
personGridHelper.setAutoExpand(false);
personGridHelper.setWeights(new float[]{33, 33, 33});
//設定登入時候獲取到的該使用者許可權下顯示的功能數量。
personGridHelper.setItemCount(mPersonFunctions.size());
helperList.add(personGridHelper);
//4.
//同2介面
LinearLayoutHelper companyTitleHelper = new LinearLayoutHelper();
companyTitleHelper.setItemCount(1);
helperList.add(companyTitleHelper);
//5.
//同3介面
GridLayoutHelper companyGridHelper = new GridLayoutHelper(3);
companyGridHelper.setWeights(new float[]{33, 33, 33});
companyGridHelper.setAutoExpand(false);
//設定登入時候獲取到的該使用者許可權下顯示的功能數量。
companyGridHelper.setItemCount(mCompanyFunctions.size());
helperList.add(companyGridHelper);複製程式碼
如果我們需要增加新的佈局控制。我們只需要新增新的LayoutHelper,按順序新增到我們的helperList中即可。
目前的LayoutHelper有以下幾種:
- LinearLayoutHelper: 線性佈局
- GridLayoutHelper: Grid佈局, 支援橫向的colspan
- FixLayoutHelper: 固定佈局,始終在螢幕固定位置顯示
- ScrollFixLayoutHelper: 固定佈局,但之後當頁面滑動到該圖片區域才顯示, 可以用來做返回頂部或其他書籤等
- FloatLayoutHelper: 浮動佈局,可以固定顯示在螢幕上,但使用者可以拖拽其位置
- ColumnLayoutHelper: 欄格佈局,都佈局在一排,可以配置不同列之間的寬度比值
- SingleLayoutHelper: 通欄佈局,只會顯示一個元件View
- OnePlusNLayoutHelper: 一拖N佈局,可以配置1-5個子元素
- StickyLayoutHelper: stikcy佈局, 可以配置吸頂或者吸底
- StaggeredGridLayoutHelper: 瀑布流佈局,可配置間隔高度/寬度
既然用到RecycleView ,那怎麼可以沒有Adapter呢,上面的準備工作做了一部分後,我們開始寫我們的Adapter。
我們這裡選擇繼承了VirtualLayoutAdapter:
我們在建構函式中傳入我們二個九宮格功能塊對應的List進來。
public class WorkAdapter extends VirtualLayoutAdapter {
int oneFuncs, twoFuncs;//
public List<FunctionBean> oneFunctions;
public List<FunctionBean> twoFunctions;
public static final int BANNER_VIEW_TYPE = 0;
public static final int DIVIDER_VIEW_TYPE = 1;
public static final int FUN_VIEW_TYPE = 2;
public WorkAdapter(@NonNull VirtualLayoutManager layoutManager, List<FunctionBean> oneFunctions, List<FunctionBean> twoFunctions,funcItemOnClickListener listener) {
super(layoutManager);
this.oneFunctions = oneFunctions;
this.twoFunctions = twoFunctions;
this.listener = listener;
oneFuncs = oneFunctions.size();
twoFuncs = twoFunctions.size();
}
}複製程式碼
我們來分別看Adapter中每個方法具體的複寫:
1.
@Override
public int getItemCount() {
int totalCount = 0;
List<LayoutHelper> helpers = getLayoutHelpers();
if (helpers == null) {
return 0;
}
for (int i = 0; i < helpers.size(); i++) {
totalCount += helpers.get(i).getItemCount();
}
return totalCount;
}複製程式碼
我們可以看到在getItemCount()
方法中,我們通過遍歷了LayoutHelper,分別取每個LayoutHelper中我們剛設定的個數。然後加起來,作為整個RecycleView 的個數。
2.
@Override
public int getItemViewType(int position) {
if (position == 0) {
return BANNER_VIEW_TYPE;
} else if (position == 1 || position == (2 + oneFuncs)) {
return DIVIDER_VIEW_TYPE;
} else {
return FUN_VIEW_TYPE;
}
}複製程式碼
我們可以看到。我們在getItemViewType
方法中肯定position的值,返回不同的type,這樣等會在onCreateViewHolder
方法中就可以返回不同的ViewHolder了。
3.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case BANNER_VIEW_TYPE:
return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_work_banner, parent, false));
case DIVIDER_VIEW_TYPE:
return new DividerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_work_divider, parent, false));
case FUN_VIEW_TYPE:
return new FuncViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_work_func, parent, false));
default:
return null;
}
}複製程式碼
4.
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof DividerViewHolder) {
if (position == 1) {
((DividerViewHolder) holder).dividerTitle.setText("我的服務");
((DividerViewHolder) holder).colorView.setBackgroundColor(Color.parseColor("#F9C025"));
} else {
((DividerViewHolder) holder).dividerTitle.setText("我的功能");
((DividerViewHolder) holder).colorView.setBackgroundColor(Color.parseColor("#35A7FF"));
}
} else if (holder instanceof FuncViewHolder) {
if(position > 1 && position < 2+ oneFuncs){
...
...
...
}else if(position > 2+ oneFuncs){
...
...
...
}
}
}複製程式碼
就這樣我們就具體的實現了多個佈局的設定,而且當你要再加一個新的也很方便。但是也許你這時候會發現,如果我們的佈局很長,有很多九宮格,或者真的像淘寶一樣,這個介面有各種功能塊。那我們剛寫的
@Override
public int getItemViewType(int position) {
if (position == 0) {
return BANNER_VIEW_TYPE;
} else if (position == 1 || position == (2 + oneFuncs)) {
return DIVIDER_VIEW_TYPE;
} else {
return FUN_VIEW_TYPE;
}
}複製程式碼
這裡你判斷的position就會很多。你可能就要有很多的if-else 來控制返回不同的type.
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof DividerViewHolder) {
if (position == 1) {
((DividerViewHolder) holder).dividerTitle.setText("我的服務");
((DividerViewHolder) holder).colorView.setBackgroundColor(Color.parseColor("#F9C025"));
} else {
((DividerViewHolder) holder).dividerTitle.setText("我的功能");
((DividerViewHolder) holder).colorView.setBackgroundColor(Color.parseColor("#35A7FF"));
}
} else if (holder instanceof FuncViewHolder) {
if(position > 1 && position < 2+ oneFuncs){
...
...
...
}else if(position > 2+ oneFuncs){
...
...
...
}
}
}複製程式碼
然後在onBindViewHolder
方法裡面也要有很多的if-else,到後面維護又會變的很麻煩,所以這樣寫相對簡單,適合模組不多的情況。
複雜的並且更通用的實現:
我們前面是把很多LayoutHelper加入到了Adapter中,然後RecycleView直接設定該Adapter,
我們這次就不這麼做了。而且中間多設定一步,就是先每個LayoutHelper設定一個Adapter,成為<子的Adapter>,然後把這些Adapter再統一放入到一個<總的Adapter>中,再把這個<總的Adapter>賦值給RecycleView即可。
因為這樣在<總的Adapter>中,對於每個Helper就可以知道個數,並且ViewHolder等賦值都被分配到了那些<子的Adapter>中處理了。我們不用再if-else的寫很多情況了。
我們直接來看這個<總的Adapter> 是如何實現的:
(不過我不會完全很仔細的講解,程式碼我也不會貼全部,就貼一些主要的地方,講主要的部分。)
1.
public class DelegateAdapter extends VirtualLayoutAdapter<RecyclerView.ViewHolder> {
private int mTotal = 0;
private int mIndex = 0;
private SparseArray<Adapter> mItemTypeAry = new SparseArray<>();
@NonNull
private final List<Pair<AdapterDataObserver, Adapter>> mAdapters = new ArrayList<>();
private final SparseArray<Pair<AdapterDataObserver, Adapter>> mIndexAry = new SparseArray<>();
}複製程式碼
我們先定義了幾個引數,
mIndex
用來等於標記各個加入的<子的Adapter>的序號
一個用來存<子的Adapter>的Map(別問我為什麼Map型別裡面只填了一個Adapter,不是Key-Value? 可以補下SparseArray和ArrayMap的知識了,Android中用來替換HashMap的類。)
一個存放了Pair<AdapterDataObserver, Adapter>
的List集合(Pair如果也不知道,也可以去補充下,就簡單理解為一個有二個屬性的物件,第一個屬性是AdapterDataObserver,第二個是Adapter)
一個存放了Pair<AdapterDataObserver, Adapter>
的Map集合。
2.
那我們知道肯定要有個方法把這些子的Adapter給加進來:
public void setAdapters(@Nullable List<Adapter> adapters) {
clear();//把相關的引數都重新置空,這裡不寫出來了。
if (adapters == null) {
adapters = Collections.emptyList();
}
List<LayoutHelper> helpers = new LinkedList<>();
mTotal = 0;
Pair<AdapterDataObserver, Adapter> pair;
for (Adapter adapter : adapters) {
// every adapter has an unique index id
//自定義類AdapterDataObserver ,繼承於RecyclerView.AdapterDataObserver
AdapterDataObserver observer = new AdapterDataObserver(mTotal, mIndex++);
adapter.registerAdapterDataObserver(observer);
//子的Adapter中自定義的方法:onCreateLayoutHelper(),用來返回子的Adapter中的LayoutHelper
LayoutHelper helper = adapter.onCreateLayoutHelper();
//而且這些子的Adapter中的LayoutHelper的個數,就是這些子的Adapter的個數
helper.setItemCount(adapter.getItemCount());
//總數為每個LayoutHelper的個數之和,也就是每個子的Adapter的個數之和
mTotal += helper.getItemCount();
helpers.add(helper);
pair = Pair.create(observer, adapter);
//這裡的mIndexAry存放了以加入的順序mIndex為Key的Pair<AdapterObserver,Adapter>
mIndexAry.put(observer.mIndex, pair);
//同時mAdapters的List集合中也存放了Pair<AdapterObserver,Adapter>
mAdapters.add(pair);
}
super.setLayoutHelpers(helpers);
}複製程式碼
我們看下AdapterDataObser的部分程式碼:
protected class AdapterDataObserver extends RecyclerView.AdapterDataObserver {
int mStartPosition;
int mIndex = -1;
public AdapterDataObserver(int startPosition, int index) {
this.mStartPosition = startPosition;
this.mIndex = index;
}
}複製程式碼
我們可以看到我們剛在new每個AdapterDataObser的時候傳入的建構函式引數是
mTotal
,mIndex++
,這樣是不是正好每個Adapter中的AdapterDataObserver中的mStartPosition
引數就是你的這個Adapter在所有整個RecycleView中的開始的position值。而mIndex
又說明了這個AdapterDataObserver是第幾個,也就是這個Adapter是所有的<子的Adapter>中的第幾個。
3.
@Override
public int getItemCount() {
return mTotal;
}複製程式碼
總數就是返回上面我們的mTotal引數。
4.
@Override
public int getItemViewType(int position) {
Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(position);
if (p == null) {
return RecyclerView.INVALID_TYPE;
}
int subItemType = p.second.getItemViewType(position - p.first.mStartPosition);
if (subItemType < 0) {
// negative integer, invalid, just return
return subItemType;
}
if (mHasConsistItemType) {
mItemTypeAry.put(subItemType, p.second);
return subItemType;
}
int index = p.first.mIndex;
return (int) getCantor(subItemType, index);
}複製程式碼
我們一步步來看這個比較關鍵的地方,我們之所以不用我們最剛開始第一次講的Vlayout使用的方法,就是因為我們的LayoutHelper多了之後,在getItemViewType()
方法中返回不同的ViewType需要很多if-else來處理。所以這裡我們看他是如何自動處理的。
第一步:Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(position);
我們看findAdapterByPosition方法的具體實現:
@Nullable
public Pair<AdapterDataObserver, Adapter> findAdapterByPosition(int position) {
//獲取我們上面的mAdapter集合,裡面存的是Pair<AdapterObserver,Adapter>
final int count = mAdapters.size();
if (count == 0) {
return null;
}
int s = 0, e = count - 1, m;
Pair<AdapterDataObserver, Adapter> rs = null;
// binary search range
while (s <= e) {
m = (s + e) / 2;
rs = mAdapters.get(m);
int endPosition = rs.first.mStartPosition + rs.second.getItemCount() - 1;
if (rs.first.mStartPosition > position) {
e = m - 1;
} else if (endPosition < position) {
s = m + 1;
} else if (rs.first.mStartPosition <= position && endPosition >= position) {
break;
}
rs = null;
}
return rs;
}複製程式碼
通過這個方法的字面意思我們不難理解:通過這個<總的Adapter>返回的item的position,來知道這個position是屬於我們存了Adapter集合中的哪個Adapter的。
先獲取我們上面已經儲存了各個Pair<AdapterObserver,Adapter>
的mAdapters集合,然後判斷個數,為0就直接返回了。不為0,我們就通過二分法查詢的方式來進行查詢。我們前面已經在每個AdapterDataObserver中存了相對於的Adapter的起始的Position,我們只需要不停的判斷現在傳給這個方法的position是在(子的Adapter 的起始position) 與 (子的Adapter 的起始position + 子的Adapter的個數)之間,如果是,就說明是屬於這個Adapter,我們就在mAdapters集合中取出相應的Pair<AdapterObserver,Adapter>
。
第二步:
int subItemType = p.second.getItemViewType(position - p.first.mStartPosition);
if (subItemType < 0) {
// negative integer, invalid, just return
return subItemType;
}
if (mHasConsistItemType) {
mItemTypeAry.put(subItemType, p.second);
return subItemType;
}
int index = p.first.mIndex;
return (int) getCantor(subItemType, index);複製程式碼
這裡的
(position - p.first.mStartPosition)
其實就是這個<總的Adapter>的處於position的這一項,在這個<子的Adapter>裡面的具體的position值。最後通過這個<子的Adapter>的getItemViewType
來得到<子的Adapter>的ViewType。這樣就自動幫我們判斷了在<總的Adapter>中的某個position值的Item的所屬的<子的Adapter>的ViewType了。而不用寫很多if-else來判斷了。
這裡又要分二種情況,也就是一個boolean值mHasConsistItemType
來控制:
在這個<總的Adapter>建構函式中傳入,它的作用是whether sub adapters itemTypes are consistent
,就是我們的所有的<子的Adapter>的itemType都是一樣的。因為如果你在<子的Adapter>中沒有覆寫getItemViewType
方法的話,預設都是返回0,即:
public int getItemViewType(int position) {
return 0;
}複製程式碼
我們也知道,RecycleView在執行的時候,執行順序是:
getItemViewType ->onCreateViewHolder ->
getItemViewType ->onCreateViewHolder ->
getItemViewType ->onCreateViewHolder ->...複製程式碼
如果我們的mHasConsistItemType設定為true的話:
所以我們如果所有的<子的Adapter>中的要用同一個viewType的話,比如這裡是0,我們就在getItemViewType
方法中執行mItemTypeAry.put(subItemType, p.second);
,這樣當前的這個<子的Apdater>就存在了key為0的集合中了,然後我們在onCreateViewHolder
方法中通過Adapter adapter = mItemTypeAry.get(viewType);
取出來就行了,這時候因為viewType為0,就正好取出來我們剛存的Adapter,然後再進入下一次的getItemViewType
的時候,就用新的adapter覆蓋了key為0的value值,然後再拿到onCreateViewHolder
方法裡面使用。
如果我們的mHasConsistItemType設定為false的話:
那這時候就用了另外一種方法,首先,因為子的Adapter預設拿到的ViewType都是0,所以我們用了要設定一個可逆演算法,比如A方法和還原的B方法,A方法中我們每次傳入viewType和另外一個值(這裡選定了上面我們拿到的Pair<AdapterDataObserver, Adapter>
中的AdapterDataObserver的index值),因為每個<子的Adapter>的index值不同,所以生成的ViewType也不同,然後我們在onCreateViewHolder
方法裡面,用還原的B方法,獲取到index值,然後通過這個index再找回<子的Adapter>,這時候我們就可以呼叫<子的Adapter>的onCreateViewHolder
方法了。
A方法:
private static long getCantor(long k1, long k2) {
return (k1 + k2) * (k1 + k2 + 1) / 2 + k2;
}複製程式碼
B方法:(具體看onCreateViewHolder方法中)
// reverse Cantor Function
int w = (int) (Math.floor(Math.sqrt(8 * viewType + 1) - 1) / 2);
int t = (w * w + w) / 2;
int index = viewType - t;
int subItemType = w - index;複製程式碼
5.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// reverse Cantor Function
int w = (int) (Math.floor(Math.sqrt(8 * viewType + 1) - 1) / 2);
int t = (w * w + w) / 2;
int index = viewType - t;
int subItemType = w - index;
Adapter adapter = findAdapterByIndex(index);
if (adapter == null) {
return null;
}
return adapter.onCreateViewHolder(parent, subItemType);
}複製程式碼
6.
@SuppressWarnings("unchecked")
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Pair<AdapterDataObserver, Adapter> pair = findAdapterByPosition(position);
if (pair == null) {
return;
}
pair.second.onBindViewHolder(holder, position - pair.first.mStartPosition);
pair.second.onBindViewHolderWithOffset(holder, position - pair.first.mStartPosition, position);
}複製程式碼
findAdapterByPosition:(也是二分法查詢,和上面的findAdapterByIndex方法一樣,不介紹了。)
@Nullable
public Pair<AdapterDataObserver, Adapter> findAdapterByPosition(int position) {
final int count = mAdapters.size();
if (count == 0) {
return null;
}
int s = 0, e = count - 1, m;
Pair<AdapterDataObserver, Adapter> rs = null;
// binary search range
while (s <= e) {
m = (s + e) / 2;
rs = mAdapters.get(m);
int endPosition = rs.first.mStartPosition + rs.second.getItemCount() - 1;
if (rs.first.mStartPosition > position) {
e = m - 1;
} else if (endPosition < position) {
s = m + 1;
} else if (rs.first.mStartPosition <= position && endPosition >= position) {
break;
}
rs = null;
}
return rs;
}複製程式碼
看了上面我們發現了,最後我們雖然給RecycleView賦值了一個<總的Adapter>,但是實際上的onCreateViewHolder
方法和onBindViewHolder
方法都是呼叫了每個具體的<子的Adapter>的。
所以我們最終在我們的Activity中的使用
final DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, true);
recyclerView.setAdapter(delegateAdapter);
List<DelegateAdapter.Adapter> adapters = new LinkedList<>();
adapters.add(XXXXX);//新增不同的子Adapter.
...
//比如這樣:
GridLayoutHelper layoutHelper;
layoutHelper = new GridLayoutHelper(4);
layoutHelper.setMargin(0, 10, 0, 10);
layoutHelper.setHGap(3);
layoutHelper.setAspectRatio(4f);
adapters.add(new ASubAdapter(this, layoutHelper, 8));
...
...
delegateAdapter.setAdapters(adapters);複製程式碼
如果我想新加一個功能塊,只要新建一個針對這個功能塊的Adapter,然後新增到adapters集合中就可以了。完全不用修改原來的程式碼。只需要在這個新加的功能塊的Adapter中處理即可。