專案需求討論-Vlayout來快速構建及擴充套件複雜介面

青蛙要fly發表於2019-02-26

大家好,今天又帶來了專案中具體遇到的需求。做一個首介面,該首介面有很多功能塊,同時這些功能塊是動態的,因為登入的人的許可權的不同,會顯示不同的功能塊,因為功能模組的數量不一定,所以當功能塊多的時候,整個介面是可以上下滑動的。其實類似有點像淘寶的首介面。如下圖所示。

專案需求討論-Vlayout來快速構建及擴充套件複雜介面
介面


首先我說的不是最優的。可能有更好的解決方式。我這裡也只是嘗試了新的方式而已。

我先說下我最剛開始想到的最傳統的想法:

1.首先因為功能塊多的時候,需要介面能夠滾動,所以我想到最外面用的ScrollView,然後ScrollView中包含了一個豎向排布的LienarLayout。
然後在放入一個ImageView顯示這個頂部圖片:

專案需求討論-Vlayout來快速構建及擴充套件複雜介面

然後需要二個橫向的LinearLayout,用來顯示這個大的分類標題:

專案需求討論-Vlayout來快速構建及擴充套件複雜介面
專案需求討論-Vlayout來快速構建及擴充套件複雜介面

然後再放入二個GridView顯示功能模組:

專案需求討論-Vlayout來快速構建及擴充套件複雜介面
專案需求討論-Vlayout來快速構建及擴充套件複雜介面

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實現一種簡單的處理。然後再實現更加通用的處理!一定要看完最後的通用處理哦!)

簡單處理:

專案需求討論-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,到後面維護又會變的很麻煩,所以這樣寫相對簡單,適合模組不多的情況。



複雜的並且更通用的實現:

專案需求討論-Vlayout來快速構建及擴充套件複雜介面

我們前面是把很多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中處理即可。

具體的使用第二種方式的程式碼及DelegateAdapter.java 的原始碼 可以在GitHub中自行觀看vlayout

求別亂噴,求點贊。哈哈。。。。。。

相關文章