概述
前不久,阿里新開源了2個東西,Atlas和vlayout。今天我來介紹下vlayout的使用。在介紹前,先抱怨兩句,阿里放開源出來,感覺就是讓我們這群人給他們找bug~~我曾遇到一個奇怪的問題,然後一直以為自己寫的有問題,結果去down了官方demo跑了一下,結果官方的demo居然並沒有做這個效果~不解!!好了,話不多說,我會通過官方的介紹以及自己的寫的demo一一介紹。先放上官方的github地址:github.com/alibaba/vla…
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
}複製程式碼
或者maven
<dependency>
<groupId>com.alibaba.android</groupId>
<artifactId>vlayout</artifactId>
<version>1.0.3</version>
<type>aar</type>
</dependency>複製程式碼
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複製程式碼
dividerHeight
LinearLayoutHelper的屬性,LinearLayoutHelper是像ListView一樣的線性佈局,dividerHeight就是每個元件之間的間距。
對於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無異。文末有demo。博友們自己下載試試就知道了。
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可以在精煉,進行多方面佈局的結合來使用。現在,我們已經學會如何使用它的。之後我想我們就應該試著去看它的原始碼來了解它是如何實現的。因為我看了下原始碼,這個開源15年就開始做的。我記得recyclerview也是15年4月才釋出出來。可想而知,阿狸團隊的強大。
demo的原始碼我已上傳到github:github.com/sw950729/VL…
第一時間獲取本人的技術文章請關注微信公眾號!