概述
最近看了下VLayout,它主要用在超複雜界面佈局,感覺確實比較好用,看了網上這篇文章,基本用法都已經寫到了,索性就轉載過來,方便後續複習。
VLayout簡介
vlayout全稱VirtualLayout,它是一個針對RecyclerView的LayoutManager擴展, 主要提供一整套佈局方案和佈局間的組件複用的問題。它通過定製化的LayoutManager,接管整個RecyclerView的佈局邏輯;LayoutManager管理了一系列LayoutHelper,LayoutHelper負責具體佈局邏輯實現的地方;每一個LayoutHelper負責頁面某一個範圍內的組件佈局;不同的LayoutHelper可以做不同的佈局邏輯,因此可以在一個RecyclerView頁面裏提供異構的佈局結構,這就能比系統自帶的LinearLayoutManager、GridLayoutManager等提供更加豐富的能力。同時支持擴展LayoutHelper來提供更多的佈局能力。
主要功能
-
默認通用佈局實現,解耦所有的View和佈局之間的關係: Linear, Grid, 吸頂, 浮動, 固定位置等1:LinearLayoutHelper: 線性佈局2:GridLayoutHelper: Grid佈局, 支持橫向的colspan3:FixLayoutHelper: 固定佈局,始終在屏幕固定位置顯示4:ScrollFixLayoutHelper: 固定佈局,但之後當頁面滑動到該圖片區域才顯示, 可以用來做返回頂部或其他書籤等5:FloatLayoutHelper: 浮動佈局,可以固定顯示在屏幕上,但用戶可以拖拽其位置6:ColumnLayoutHelper: 欄格佈局,都佈局在一排,可以配置不同列之間的寬度比值7:SingleLayoutHelper: 通欄佈局,只會顯示一個組件View8:OnePlusNLayoutHelper: 一拖N佈局,可以配置1-5個子元素9:StickyLayoutHelper: stikcy佈局, 可以配置吸頂或者吸底10:StaggeredGridLayoutHelper: 瀑布流佈局,可配置間隔高度/寬度
-
上述默認實現裏可以大致分爲兩類:一是非fix類型佈局,像線性、Grid、欄格等,它們的特點是佈局在整個頁面流裏,隨頁面滾動而滾動;另一類就是fix類型的佈局,它們的子節點往往不隨頁面滾動而滾動。
-
所有除佈局外的組件複用,VirtualLayout將用來管理大的模塊佈局組合,擴展了RecyclerView,使得同一RecyclerView內的組件可以複用,減少View的創建和銷燬過程。
使用方法
版本請參考mvn repository上的最新版本(目前最新版本是1.0.3),最新的 aar 都會發布到 jcenter 和 MavenCentral 上,確保配置了這兩個倉庫源,然後引入aar依賴:
compile ('com.alibaba.android:vlayout:1.0.3@aar') {
transitive = true
}
LayoutHelper功能介紹
margin, padding
Margin, padding就是外邊距、內邊距,概念與Android系統的margin, padding一樣,但也有不同的地方:
- 它不是整個RecyclerView頁面的margin和padding,它是每一塊LayoutHelper所負責的區域的margin和padding。
- 一個頁面裏可以有多個LayoutHelper,意味着不同LayoutHelper可以設置不同的margin和padding。
- LayoutHelper的margin和padding與頁面RecyclerView的margin和padding可以共存。
- 目前主要針對非fix類型的LayoutHelper實現了margin和padding,fix類型LayoutHelper內部沒有相對位置關係,不處理邊距。
對於LayoutHelper,調用
public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding)
public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin)
bgColor, bgImg
背景顏色或者背景圖,這其實不是佈局屬性,但是由於在vlayout對視圖進行了直接佈局,不同區域的視圖的父節點都是RecyclerView,如果想要針對某一塊區域單獨繪製背景,就很難做到了。vlayout框架對此做了特殊處理,對於非fix、非float類型的LayoutHelper,支持配置背景色或背景圖。同樣目前主要針對非fix類型的LayoutHelper實現這個特性。
使用背景色
public void setBgColor(int bgColor)
使用背景圖首先爲LayoutManager提供一個ImageView簡單工廠
this.mLayoutManager.setLayoutViewFactory(new LayoutViewFactory() {
@Override
public opinion generateLayoutView(@NonNull Context context) {
return new XXImageView(context);
}
});
再爲LayoutHelper提設置圖片加載的Listener
baseHelper.setLayoutViewBindListener(new BindListener(imgUrl));
baseHelper.setLayoutViewUnBindListener(new UnbindListener(imgUrl));
private static class BindListener implements BaseLayoutHelper.LayoutViewBindListener {
private String imgUrl;
public BindListener(String imgUrl) {
this.imgUrl = imgUrl;
}
@Override
public void onBind(View layoutView, BaseLayoutHelper baseLayoutHelper) {
//loading image
}
}
private static class UnbindListener implements BaseLayoutHelper.LayoutViewUnBindListener {
private String imgUrl;
public UnbindListener(String imgUrl) {
this. imgUrl = imgUrl;
}
@Override
public void onUnbind(View layoutView, BaseLayoutHelper baseLayoutHelper) {
//cancel loading image
}
}
aspectRatio
爲了保證佈局過程中視圖的高度一致,我們設計了aspectRatio屬性,它是寬與高的比例,LayoutHelper裏有aspectRatio屬性,通過vlayout添加的視圖的LayoutParams也有aspectRatio屬性,後者的優先級比前者高,但含義不一樣。
- LayoutHelper定義的aspectRatio,指的是一行視圖整體的寬度與高度之比,當然整體的寬度是減去了RecyclerView和對應的LayoutHelper的margin, padding。
- 視圖的LayoutParams定義的aspectRatio,指的是在LayoutHelper計算出視圖寬度之後,用來確定視圖高度時使用的,它會覆蓋通過LayoutHelper的aspectRatio計算出來的視圖高度,因此具備更高優先級。
對於LayoutHelper,調用
public void setAspectRatio(float aspectRatio)
對於LayoutParams,調用
((VirutalLayoutManager.LayoutParams) layoutParams).mAspectRatio
對於LinearLayoutHelper,調用
public void setDividerHeight(int dividerHeight)
weights
ColumnLayoutHelper, GridLayoutHelper的屬性,它們都是提供網格狀的佈局能力,建議使用GridLayoutHelper,它的能力更加強大,參考下文介紹。默認情況下,每個網格中每一列的寬度是一樣的,通過weights屬性,可以指定讓每一列的寬度成比例分配,就像LinearLayout的weight屬性一樣。 weights屬性是一個float數組,每一項代表某一列佔父容器寬度的百分比,總和建議是100,否則佈局會超出容器寬度;如果佈局中有4列,那麼weights的長度也應該是4;長度大於4,多出的部分不參與寬度計算;如果小於4,不足的部分默認平分剩餘的空間。
對於ColumnLayoutHelper, GridLayoutHelper,調用
public void setWeights(float[] weights)
vGap, hGap
GridLayoutHelper與StaggeredGridLayoutHelper都有這兩個屬性,分別控制視圖之間的垂直間距和水平間距。
對於GridLayoutHelper, StaggeredGridLayoutHelper,調用
public void setHGap(int hGap)
public void setVGap(int vGap)
spanCount, spanSizeLookup
GridLayoutHelper的屬性,參考於系統的GridLayoutManager,spanCount表示網格的列數,默認情況下每一個視圖都佔用一個網格區域,但通過提供自定義的spanSizeLookUp,可以指定某個位置的視圖佔用多個網格區域。
使用spanCount調用
public void setSpanCount(int spanCount)
使用spanSizeLookup
public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup)
autoExpand
GridLayoutHelper的屬性,當一行裏視圖的個數少於spanCount值的時候,如果autoExpand爲true,視圖的總寬度會填滿可用區域;否則會在屏幕上留空白區域。
接口:
public void setAutoExpand(boolean isAutoExpand)
lane
StaggeredGridLayoutHelper中有這個屬性,與GridLayoutHelper裏的spanCount類似,控制瀑布流的列數。
接口:
public void setLane(int lane)
fixAreaAdjuster
fix類型的LayoutHelper,在可能需要設置一個相對父容器四個邊的偏移量,比如整個頁面裏有一個固定的標題欄添加在vlayout容器上,vlayout內部的fix類型視圖不希望與外部的標題有所重疊,那麼就可以設置一個fixAreaAdjuster來做偏移。
接口:
public void setAdjuster(FixAreaAdjuster adjuster)
alignType, x, y
FixLayoutHelper, ScrollFixLayoutHelper, FloatLayoutHelper的屬性,表示吸邊時的基準位置,有四個取值,分別是TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT。x和y是相對這四個位置的偏移量,最終的偏移量還要受上述的fixAreaAdjuster影響。
- TOP_LEFT:基準位置是左上角,x是視圖左邊相對父容器的左邊距偏移量,y是視圖頂邊相對父容器的上邊距偏移量;
- TOP_RIGHT:基準位置是右上角,x是視圖右邊相對父容器的右邊距偏移量,y是視圖頂邊相對父容器的上邊距偏移量;
- BOTTOM_LEFT:基準位置是左下角,x是視圖左邊相對父容器的左邊距偏移量,y是視圖底邊相對父容器的下邊距偏移量;
- BOTTOM_RIGHT:基準位置是右下角,x是視圖右邊相對父容器的右邊距偏移量,y是視圖底邊相對父容器的下邊距偏移量;
設置基準調用
public void setAlignType(int alignType)
設置偏移量調用
public void setX(int x)
public void setY(int y)
showType
ScrollFixLayoutHelper的屬性,取值有SHOW_ALWAYS, SHOW_ON_ENTER, SHOW_ON_LEAVE。
- SHOW_ALWAYS:與FixLayoutHelper的行爲一致,固定在某個位置;
- SHOW_ON_ENTER:默認不顯示視圖,當頁面滾動到這個視圖的位置的時候,才顯示;
- SHOW_ON_LEAVE:默認不顯示視圖,當頁面滾出這個視圖的位置的時候顯示;
接口:
public void setShowType(int showType)
stickyStart, offset
StickyLayoutHelper的屬性,當視圖的位置在屏幕範圍內時,視圖會隨頁面滾動而滾動;當視圖的位置滑出屏幕時,StickyLayoutHelper會將視圖固定在頂部(stickyStart = true)或者底部(stickyStart = false),固定的位置支持設置偏移量offset。
接口:
public void setStickyStart(boolean stickyStart)
public void setOffset(int offset)
實例演示
上面我們已經詳細介紹的各種LayoutHelper以及它的各種屬性,現在,我們通過demo來進行實例演示。
LinearLayoutHelper
我們activity只要進行一些簡單的配置就可以了:
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
adapter.addAdapter(new DelegateRecyclerAdapter(this,new LinearLayoutHelper()));
recycler.setAdapter(adapter);
對於adapter ,我們繼承DelegateAdapter來實現,代碼很簡單,如下:
public LayoutHelper onCreateLayoutHelper() {
return helper;
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewholder(inflater.inflate(R.layout.item, parent, false));
}
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((MyViewholder) holder).text.setText(position + 1 + "");
}
public int getItemCount() {
return 60;
}
public class MyViewholder extends RecyclerView.ViewHolder {
private TextView text;
public MyViewholder(View view) {
super(view);
text = (TextView) view.findViewById(R.id.text);
}
}
效果圖:
GridLayoutHelper
我們只要把LinearLayouthelper改成Gridlayouthelper就可以了:
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
adapter.addAdapter(new DelegateRecyclerAdapter(this,new GridLayoutHelper(3)));
recycler.setAdapter(adapter);
效果圖:
StaggeredGridLayoutHelper
還是直接修改LayoutHelper就可以了:
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
//StaggeredGridLayoutHelper(int num,int gap)
//num爲每行顯示數目,gap爲兩個item的邊距
adapter.addAdapter(new StaggeredAdapter(this,new StaggeredGridLayoutHelper(3,20)));
recycler.setAdapter(adapter);
爲了做成瀑布流的效果,我們對每個item進行一個隨機高度的設置:
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ViewGroup.LayoutParams layoutParams = ((MyViewholder) holder).text.getLayoutParams();
layoutParams.height = 260 + position % 7 * 20;
((MyViewholder) holder).text.setLayoutParams(layoutParams);
((MyViewholder) holder).text.setText(position + 1 + "");
}
效果圖:
FixLayoutHelper
對於fixlayout類型的,我們需要先後添加一次LinearLayoutHelper和FixLayoutHelper。
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
adapter.addAdapter(new DelegateRecyclerAdapter(this, new LinearLayoutHelper()));
adapter.addAdapter(new ScrollFixAdapter(this, new FixLayoutHelper(FixLayoutHelper.BOTTOM_LEFT, 200, 200), recycler));
recycler.setAdapter(adapter);
效果圖:
ScrollFixLayoutHelper
同上,代碼是差不多的,不過官方所說的標籤,置頂等功能。並不能實現。官方demo也並沒有實現此功能。雖然我們可以通過點擊圖片來進行置頂。但是具體功能感覺和fixlayout無異。
ColumnLayoutHelper
代碼如下:
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
adapter.addAdapter(new DelegateRecyclerAdapter(this,new ColumnLayoutHelper()));
recycler.setAdapter(adapter);
效果圖:
SingleLayoutHelper
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
adapter.addAdapter(new DelegateRecyclerAdapter(this,new SingleLayoutHelper()));
recycler.setAdapter(adapter);
效果圖:
OnePlusNLayoutHelper
一拖N佈局
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper(3);
adapter.addAdapter(new OnePlusNRecyclerAdapter(this,helper));
recycler.setAdapter(adapter);
效果圖:
FloatLayoutHelper
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
adapter.addAdapter(new FloatAdapter(this,new FloatLayoutHelper()));
adapter.addAdapter(new DelegateRecyclerAdapter(this,new LinearLayoutHelper()));
recycler.setAdapter(adapter);
效果圖:
StickyLayoutHelper
這個吸頂和吸底效果還是比較強大,自我感覺可以深入研究的就這個和FloatLayoutHelper了。具體代碼如下:
VirtualLayoutManager manager = new VirtualLayoutManager(this);
recycler.setLayoutManager(manager);
DelegateAdapter adapter = new DelegateAdapter(manager, true);
//在頂部時需先添加sticklayout,在底部時最後添加sticklayout
StickyLayoutHelper helper = new StickyLayoutHelper(true);
// adapter.addAdapter(new StickRecyclerAdapter(this, helper, recycler));
// adapter.addAdapter(new DelegateRecyclerAdapter(this, new LinearLayoutHelper()));
//頂部和實體合二爲一
adapter.addAdapter(new DelegateRecyclerAdapter(this, helper));
adapter.addAdapter(new DelegateRecyclerAdapter(this, new LinearLayoutHelper()));
//底部
// StickyLayoutHelper helper = new StickyLayoutHelper(false);
// adapter.addAdapter(new DelegateRecyclerAdapter(this, new LinearLayoutHelper()));
// adapter.addAdapter(new StickRecyclerAdapter(this, helper));
recycler.setAdapter(adapter);
效果圖:頂部:
實體和頂部合二爲一:
總結
這個開源可以多個LayoutHelper進行結合,比之前那些LinearLayoutmanager、 Gridlayoutmanager之類的強大太多。我感覺有了這個。我之前那個SWPullRecyclerLayout可以在精煉,進行多方面佈局的結合來使用。現在,我們已經學會如何使用它的。之後我想我們就應該試着去看它的源碼來了解它是如何實現的。