VLayout全面解析

我就是馬雲飛發表於2017-11-16

概述

前不久,阿里新開源了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內部沒有相對位置關係,不處理邊距。VLayout全面解析

對於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實現這個特性。VLayout全面解析​ 

 使用背景色

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計算出來的檢視高度,因此具備更高優先順序。VLayout全面解析

對於LayoutHelper,呼叫

public void setAspectRatio(float aspectRatio)複製程式碼

對於LayoutParams,呼叫

((VirutalLayoutManager.LayoutParams) layoutParams).mAspectRatio複製程式碼

dividerHeight

LinearLayoutHelper的屬性,LinearLayoutHelper是像ListView一樣的線性佈局,dividerHeight就是每個元件之間的間距。

VLayout全面解析

對於LinearLayoutHelper,呼叫

public void setDividerHeight(int dividerHeight)複製程式碼

weights

ColumnLayoutHelper, GridLayoutHelper的屬性,它們都是提供網格狀的佈局能力,建議使用GridLayoutHelper,它的能力更加強大,參考下文介紹。預設情況下,每個網格中每一列的寬度是一樣的,通過weights屬性,可以指定讓每一列的寬度成比例分配,就像LinearLayout的weight屬性一樣。 weights屬性是一個float陣列,每一項代表某一列佔父容器寬度的百分比,總和建議是100,否則佈局會超出容器寬度;如果佈局中有4列,那麼weights的長度也應該是4;長度大於4,多出的部分不參與寬度計算;如果小於4,不足的部分預設平分剩餘的空間。VLayout全面解析

對於ColumnLayoutHelper, GridLayoutHelper,呼叫

public void setWeights(float[] weights)複製程式碼

vGap, hGap

GridLayoutHelper與StaggeredGridLayoutHelper都有這兩個屬性,分別控制檢視之間的垂直間距和水平間距。

VLayout全面解析

對於GridLayoutHelper, StaggeredGridLayoutHelper,呼叫

public void setHGap(int hGap)
​
public void setVGap(int vGap)複製程式碼

spanCount, spanSizeLookup

GridLayoutHelper的屬性,參考於系統的GridLayoutManager,spanCount表示網格的列數,預設情況下每一個檢視都佔用一個網格區域,但通過提供自定義的spanSizeLookUp,可以指定某個位置的檢視佔用多個網格區域。VLayout全面解析

使用spanCount呼叫

public void setSpanCount(int spanCount)複製程式碼

使用spanSizeLookup

public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup)複製程式碼

autoExpand

GridLayoutHelper的屬性,當一行裡檢視的個數少於spanCount值的時候,如果autoExpand為true,檢視的總寬度會填滿可用區域;否則會在螢幕上留空白區域。VLayout全面解析

介面:

public void setAutoExpand(boolean isAutoExpand)複製程式碼

lane

StaggeredGridLayoutHelper中有這個屬性,與GridLayoutHelper裡的spanCount類似,控制瀑布流的列數。

介面:

public void setLane(int lane)複製程式碼

fixAreaAdjuster

fix型別的LayoutHelper,在可能需要設定一個相對父容器四個邊的偏移量,比如整個頁面裡有一個固定的標題欄新增在vlayout容器上,vlayout內部的fix型別檢視不希望與外部的標題有所重疊,那麼就可以設定一個fixAreaAdjuster來做偏移。VLayout全面解析

介面:

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是檢視底邊相對父容器的下邊距偏移量;

VLayout全面解析

設定基準呼叫

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:預設不顯示檢視,當頁面滾出這個檢視的位置的時候顯示;VLayout全面解析

介面:

public void setShowType(int showType)複製程式碼

stickyStart, offset

StickyLayoutHelper的屬性,當檢視的位置在螢幕範圍內時,檢視會隨頁面滾動而滾動;當檢視的位置滑出螢幕時,StickyLayoutHelper會將檢視固定在頂部(stickyStart = true)或者底部(stickyStart = false),固定的位置支援設定偏移量offset。VLayout全面解析

介面:

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);
        }
    }複製程式碼

效果圖:

VLayout全面解析

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);複製程式碼

效果圖:

VLayout全面解析

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 + "");
    }複製程式碼

效果圖:

VLayout全面解析

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);複製程式碼

效果圖:

VLayout全面解析

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);複製程式碼

效果圖:

VLayout全面解析

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);複製程式碼

效果圖:

VLayout全面解析

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);複製程式碼

效果圖:

VLayout全面解析

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);複製程式碼

效果圖:

VLayout全面解析

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);

複製程式碼

效果圖:頂部:

VLayout全面解析

實體和頂部合二為一:

VLayout全面解析

總結

對於這個開源,我的總結就是,每個開源都有它的強大之處,至於我們會不會發現就看我們如何去理解了。而且這個開源可以多個LayoutHelper進行結合,比之前那些LinearLayoutmanager、 Gridlayoutmanager之類的強大太多。我感覺有了這個。我之前那個SWPullRecyclerLayout可以在精煉,進行多方面佈局的結合來使用。現在,我們已經學會如何使用它的。之後我想我們就應該試著去看它的原始碼來了解它是如何實現的。因為我看了下原始碼,這個開源15年就開始做的。我記得recyclerview也是15年4月才釋出出來。可想而知,阿狸團隊的強大。

demo的原始碼我已上傳到github:github.com/sw950729/VL…

第一時間獲取本人的技術文章請關注微信公眾號!

    VLayout全面解析


相關文章