前言
-
V- Layout
是阿里出品的基礎 UI 框架服鹅,用于快速實現(xiàn)頁面的復雜布局,在手機天貓Android
版 內(nèi)廣泛使用
- 讓人激動的是留荔,在上個月
V- Layout
終于在Github上開源澜倦!
- 今天藻治,我對
V- Layout
進行了詳細分析碘勉,我將獻上一份V- Layout
的使用攻略 & 源碼分析验靡,希望你們會喜歡胜嗓。
閱讀本文前請先看:
Android Tangram模型:連淘寶辞州、天貓都在用的UI框架模型你一定要懂
Carson帶你學Android開源庫系列文章:
Carson帶你學Android:主流開源圖片加載庫對比(UIL变过、Picasso媚狰、Glide、Fresco)
Carson帶你學Android:主流開源網(wǎng)絡請求庫對比(Volley辨宠、OkHttp、Retrofit)
Carson帶你學Android:網(wǎng)絡請求庫Retrofit使用教程
Carson帶你學Android:網(wǎng)絡請求庫Retrofit源碼分析
Carson帶你學Android:圖片加載庫Glide使用教程
Carson帶你學Android:圖片加載庫Glide源碼分析
Carson帶你學Android:V-Layout彭羹,淘寶泪酱、天貓都在用的UI框架墓阀,趕緊用起來吧经伙!
目錄
1. 為什么要使用 V - Layout
在講解 V - Layout
前帕膜,我們先來搞懂一個問題:為什么要使用 V - Layout
1.1 背景
-
Android
中UI
性能消耗主要來自于兩個方面:
- 布局層次嵌套導致多重
measure/layout
-
View
控件的創(chuàng)建和銷毀
- 為了解決上述問題,現(xiàn)有的解決方式是:
- 少用嵌套布局
- 使用
ListView/GirdView/RecyclerView
等基礎空間來處理View
的回收與復用荒典。
但是寺董,很多時候我們需要在一個長列表下做多種類型的布局來分配各種元素,,特別是電商平臺首頁等頁面盯滚,布局元素結(jié)構更加復雜多樣。如下圖:
此時的解決方案有所變化:不采用子View
的復用背率,只采用一個主要的復用容器(如ListView
或 RecyclerView
+LinearLayoutManager
)交排,然后在其中使用嵌套方式直接用各個組件進行拼接埃篓,減少了復用的能力
1.2 問題
這種做法還是會損失Android應用的性能。
1.3 解決方案
- 通過自定義
LayoutManager
管理所有的布局類型 - 即阿里出品的基礎 UI 框架項目
VirtualLayout
就是采用該方式來解決上述問題
2. 簡介
- 定義:
VirtualLayout
是阿里出品的基礎 UI 框架項目 - 作用:快速實現(xiàn)復雜的布局格式的混排 & 通過組件回收提高性能
- 基于
RecyclerView
&LayoutManager
擴展- 目前已在Github開源:Github - alibaba - vlayout
3. 應用場景
- 復雜的布局格式混排,如:浮動布局委刘、欄格布局、通欄布局淆珊、一拖N布局、瀑布流布局操刀,還可以組合使用這些布局
- 具體場景是:如電商平臺首頁、活動頁等等
V - Layout 目前已在手機天貓 & 淘寶 Android 版內(nèi)廣泛使用
4. 原理解析
V - Layout
的本質(zhì)原理是:通過自定義一個VirtualLayoutManager
(繼承自 LayoutManager),用于管理一系列LayoutHelper
礁遣,將具體的布局能力交給LayoutHelper
來完成杏头,從而 快速實現(xiàn)組合布局 的需求醇王。
- 每個
LayoutHelper
負責 頁面某一個范圍內(nèi)的布局V - Layout
默認實現(xiàn)了10種默認布局:(對應同名的LayoutHelper)
布局類型
4.1 源碼類說明
-
V - Layout
的源碼類圖如下:
UML類圖 具體類說明
-
V - Layout
默認實現(xiàn)了10種默認布局:(對應同名的LayoutHelper)
下面會進行詳細介紹呼渣。
- 特別注意:
- 每一種
LayoutHelper
負責布局一批組件范圍內(nèi)的組件排嫌,不同組件范圍內(nèi)的組件之間,如果類型相同,可以在滑動過程中回收復用并徘。因此回收粒度比較細,且可以跨布局類型復用姐直。 - 支持擴展外部:即注冊新的
LayoutHelper
声畏,實現(xiàn)特殊的布局方式。下面會詳細說明
介紹完類之后,我將詳細分析 V - Layout
的工作流程徘跪。
4.2 工作流程
-
V - Layout
的工作流程分為 初始化 & 布局流程鹃答。如下圖:
- 下面我將對初始化 & 布局流程作詳細分析。
4.2.1 初始化
- 在使用
V - layout
快速實現(xiàn)復雜布局前锋八,需要先做一系列的初始化工作。
初始化流程與使用普通的 RecyclerView + LayoutManager 初始化流程基本一致:Vlayout的使用者
- 此處的初始化 實際上 就是 使用者在使用
V - layout
時需要做的初始化工作紊服。 - 此處主要先講解下數(shù)據(jù)列表的獲取:本質(zhì)上煎饼,是對頁面實體 進行 卡片 & 組件的拆解,形成一個位置列表
- 其他初始化步驟將在下面實例講解進行詳細說明
4.2.2 具體布局流程
- 當完成初始化工作后,每當用戶剛打開頁面第一次渲染布局 或 用戶滑動頁面時意鲸,都會進行一次布局流程
- 布局流程的本質(zhì)是:自定義
VirtualLayoutManager
持續(xù)獲取頁面狀態(tài),并通過LayoutHelperFinder
找到對應的LayoutHelper
從而實現(xiàn)對應的布局邏輯槐雾,從而快速實現(xiàn)組合布局 的需求 - 具體流程如下
總結(jié)
下面用一張圖總結(jié) V - Layout
的原理 & 工作流程
在講完原理后,接下來我將如何使用 V - Layout
慌烧。
5. 使用步驟
-
V - Layout
的使用其實就是上面說的初始化工作 - 使用步驟如下:
下面我將對每個步驟進行詳細說明。
步驟1:創(chuàng)建RecyclerView & VirtualLayoutManager 對象并進行綁定
recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
// 創(chuàng)建RecyclerView對象
VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);
// 創(chuàng)建VirtualLayoutManager對象
// 同時內(nèi)部會創(chuàng)建一個LayoutHelperFinder對象,用來后續(xù)的LayoutHelper查找
recyclerView.setLayoutManager(layoutManager);
// 將VirtualLayoutManager綁定到recyclerView
步驟2:設置回收復用池大小
如果一屏內(nèi)相同類型的 View 個數(shù)比較多嘱兼,需要設置一個合適的大小,防止來回滾動時重新創(chuàng)建 View)
// 設置組件復用回收池
RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
recyclerView.setRecycledViewPool(viewPool);
viewPool.setMaxRecycledViews(0, 10);
步驟3:設置Adapter
設置 V - Layout的Adapter有兩種方式:
- 方式1:繼承 自
DelegateAdapter
- 方式2:繼承 自
VirtualLayoutAdapter
下面會進行詳細說明:
方式1:繼承 自 DelegateAdapter
- 定義:
DelegateAdapter
是V - Layout專門為管理LayoutHelper
定制的Adapter
繼承自VirtualLayoutAdapter
- 作用:通過管理不同布局的Adapter船殉,繼而管理不同的
LayoutHelper
挨厚,從而實現(xiàn)使用不同組合布局
- 特別注意:雖不可直接綁定LayoutHelper疫剃,但是它內(nèi)部有一個繼承自RecyclerView.Adapter的內(nèi)部類Adapter可以綁定LayoutHelper;
- 即通過一個List把綁定好的Adapter打包起來壤躲,再放去DelegateAdapter,這樣就可以實現(xiàn)組合使用不同的布局
- 具體做法:
- 寫法與復寫系統(tǒng)自帶的Adapter非常類似:只比系統(tǒng)自帶的RecyclerAdapter需要多重載onCreateLayoutHelper方法客税,其余類似
- 關于Android系統(tǒng)自帶的RecyclerAdapter的使用具體請看我寫的文章Android開發(fā):ListView、AdapterView酥夭、RecyclerView全面解析
public class MyAdapter extends DelegateAdapter.Adapter<MyAdapter.MainViewHolder> {
// 比系統(tǒng)自帶的RecyclerAdapter需要多重載onCreateLayoutHelper()
@Override
public LayoutHelper onCreateLayoutHelper() {
return layoutHelper;
}
... // 其余寫法與復寫系統(tǒng)自帶的Adapter相同
}
方式2:繼承 自 VirtualLayoutAdapter
定義:當需要實現(xiàn)復雜需求時, 可以通過繼承
VirtualLayoutAdapter
從而實現(xiàn)自定義Adapter具體使用
public class MyAdapter extends VirtualLayoutAdapter {
...// 自定義Adapter邏輯
}
步驟4:根據(jù)數(shù)據(jù)列表,創(chuàng)建對應的LayoutHelper
- 系統(tǒng)以封裝好以下布局類型(對應同名的LayoutHelper)
- 具體使用如下:
1. 線性布局(LinearLayoutHelper)
- 布局說明:布局子元素(
Item
)以線性排布的布局
- 具體使用
/**
設置線性布局
*/
LinearLayoutHelper linearLayoutHelper = new LinearLayoutHelper();
// 創(chuàng)建對應的LayoutHelper對象
// 所有布局的公共屬性(屬性會在下面詳細說明)
linearLayoutHelper.setItemCount(4);// 設置布局里Item個數(shù)
linearLayoutHelper.setPadding(10,10,10,10);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
linearLayoutHelper.setMargin(10,10,10,10);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
linearLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
linearLayoutHelper.setAspectRatio(6);// 設置設置布局內(nèi)每行布局的寬與高的比
// linearLayoutHelper特有屬性
linearLayoutHelper.setDividerHeight(1); // 設置每行Item的距離
1. 所有布局的共有屬性說明:
a. setItemCount屬性
- 作用:設置整個布局里的Item數(shù)量
如設置的Item總數(shù)如與Adapter的getItemCount()方法返回的數(shù)量不同,會取決于后者
- 具體使用
// 接口示意
public void setItemCount(int Count)
// 具體使用
linearLayoutHelper.setItemCount(4);
b. Adding & Margin屬性
-
定義:都是邊距的含義,但二者邊距的定義不同:
-
Padding
:是LayoutHelper
的子元素相對LayoutHelper
邊緣的距離; -
Margin
:是LayoutHelper
邊緣相對父控件(即RecyclerView
)的距離婆廊。具體如下圖:
示意圖
-
具體使用
// 接口示意
public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding)
public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin)
// 具體使用
linearLayoutHelper.setPadding(10,10,10,10);
// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
linearLayoutHelper.setMargin(10,10,10,10);
// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
c. bgColor屬性
- 作用:設置布局背景顏色
- 具體使用:
// 接口示意
public void setBgColor(int bgColor)
// 具體使用
linearLayoutHelper.setBgColor(Color.YELLOW);
d. aspectRatio屬性
- 作用:設置布局內(nèi)每行布局的寬與高的比湘换。如下圖
- 具體使用
// 接口示意
public void setAspectRatio(float aspectRatio);
// LayoutHelper定義的aspectRatio
((VirutalLayoutManager.LayoutParams) layoutParams).mAspectRatio
// 視圖的LayoutParams定義的aspectRatio
// 在LayoutHelper計算出視圖寬度之后筹我,用來確定視圖高度時使用的,它會覆蓋通過LayoutHelper的aspectRatio計算出來的視圖高度袁串,因此具備更高優(yōu)先級赎瑰。
// 具體使用
linearLayoutHelper.setAspectRatio(6);
2. LinearLayoutHelper布局的特有屬性說明
a. dividerHeight屬性
- 作用:設置每行Item之間的距離
設置的間隔會與RecyclerView的addItemDecoration()添加的間隔疊加
- 具體使用
// 接口示意
public void setDividerHeight(int dividerHeight)
// 具體使用
linearLayoutHelper.setDividerHeight(1);
2. 網(wǎng)格布局(GridLayout)
- 布局說明:布局里的Item以網(wǎng)格的形式進行排列
- 具體使用
/**
設置Grid布局
*/
GridLayoutHelper gridLayoutHelper = new GridLayoutHelper(3);
// 在構造函數(shù)設置每行的網(wǎng)格個數(shù)
// 公共屬性
gridLayoutHelper.setItemCount(6);// 設置布局里Item個數(shù)
gridLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
gridLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
gridLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
gridLayoutHelper.setAspectRatio(6);// 設置設置布局內(nèi)每行布局的寬與高的比
// gridLayoutHelper特有屬性(下面會詳細說明)
gridLayoutHelper.setWeights(new float[]{40, 30, 30});//設置每行中 每個網(wǎng)格寬度 占 每行總寬度 的比例
gridLayoutHelper.setVGap(20);// 控制子元素之間的垂直間距
gridLayoutHelper.setHGap(20);// 控制子元素之間的水平間距
gridLayoutHelper.setAutoExpand(false);//是否自動填充空白區(qū)域
gridLayoutHelper.setSpanCount(3);// 設置每行多少個網(wǎng)格
// 通過自定義SpanSizeLookup來控制某個Item的占網(wǎng)格個數(shù)
gridLayoutHelper.setSpanSizeLookup(new GridLayoutHelper.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position > 7 ) {
return 3;
// 第7個位置后,每個Item占3個網(wǎng)格
}else {
return 2;
// 第7個位置前,每個Item占2個網(wǎng)格
}
}
});
GridLayoutHelper布局的特有屬性說明
a. weights屬性
- 作用:設置每行中每個網(wǎng)格寬度占每行總寬度的比例
- 默認情況下鲜漩,每行中每個網(wǎng)格中的寬度相等
weights
屬性是一個float數(shù)組踩娘,每一項代表當個網(wǎng)格占每行總寬度的百分比;總和是100理卑,否則布局會超出容器寬度;- 如果布局中有4列中捆,那么weights的長度也應該是4殴蓬;長度大于4痘绎,多出的部分不參與寬度計算孤页;如果小于4允坚,不足的部分默認平分剩余的空間。
- 具體使用
// 接口示意
public void setWeights(float[] weights)
// 具體使用
gridLayoutHelper.setWeights(new float[]{40, 30, 30});
b. vGap、hGap屬性
- 作用:分別控制子元素之間的垂直間距 和 水平間距拗胜。
- 具體使用
// 接口示意
public void setHGap(int hGap)
public void setVGap(int vGap)
// 具體使用
gridLayoutHelper.setVGap(20);// 控制子元素之間的垂直間距
gridLayoutHelper.setHGap(20);// 控制子元素之間的水平間距
c. spanCount误算、spanSizeLookup屬性
- 作用:
-
spanCount
:設置每行多少個網(wǎng)格 -
spanSizeLookup
:設置每個Item
占用多少個網(wǎng)格(默認= 1)
-
- 具體使用
// 接口示意
public void setSpanCount(int spanCount)
public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup)
// 具體使用
gridLayoutHelper.setSpanCount(5);// 設置每行多少個網(wǎng)格
// 通過自定義SpanSizeLookup來控制某個Item的占網(wǎng)格個數(shù)
gridLayoutHelper.setSpanSizeLookup(new GridLayoutHelper.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position > 7 ) {
return 3;
// 第7個位置后,每個Item占3個網(wǎng)格
}else {
return 2;
// 第7個位置前,每個Item占2個網(wǎng)格
}
}
});
d. autoExpand屬性
- 作用:當一行里item的個數(shù) < (每行網(wǎng)格列數(shù) - spanCount值/ 每個Item占有2個網(wǎng)格-setSpanSizeLookup )時咖杂,是否自動填滿空白區(qū)域
- 若autoExpand=true,那么視圖的總寬度會填滿可用區(qū)域壤圃;
- 否則會在屏幕上留空白區(qū)域。
- 具體使用
// 接口示意
public void setAutoExpand(boolean isAutoExpand)
// 具體使用
gridLayoutHelper.setAutoExpand(false);
3. 固定布局(FixLayoutHelper)
- 布局說明:布局里的Item 固定位置
固定在屏幕某個位置制圈,且不可拖拽 & 不隨頁面滾動而滾動憋沿。如下圖:(左上角)
- 具體使用
/**
設置固定布局
*/
FixLayoutHelper fixLayoutHelper = new FixLayoutHelper(FixLayoutHelper.TOP_LEFT,40,100);
// 參數(shù)說明:
// 參數(shù)1:設置吸邊時的基準位置(alignType) - 有四個取值:TOP_LEFT(默認), TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT
// 參數(shù)2:基準位置的偏移量x
// 參數(shù)3:基準位置的偏移量y
// 公共屬性
fixLayoutHelper.setItemCount(1);// 設置布局里Item個數(shù)
// 從設置Item數(shù)目的源碼可以看出,一個FixLayoutHelper只能設置一個
// @Override
// public void setItemCount(int itemCount) {
// if (itemCount > 0) {
// super.setItemCount(1);
// } else {
// super.setItemCount(0);
// }
// }
fixLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
fixLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
fixLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
fixLayoutHelper.setAspectRatio(6);// 設置設置布局內(nèi)每行布局的寬與高的比
// fixLayoutHelper特有屬性
fixLayoutHelper.setAlignType(FixLayoutHelper.TOP_LEFT);// 設置吸邊時的基準位置(alignType)
fixLayoutHelper.setX(30);// 設置基準位置的橫向偏移量X
fixLayoutHelper.setY(50);// 設置基準位置的縱向偏移量Y
FixLayoutHelper特有屬性說明
a. AlignType采章、x运嗜、y屬性
- 作用:
- alignType:吸邊基準類型
共有4個取值:TOP_LEFT(默認), TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,具體請看下面示意圖
- x:基準位置的橫向偏移量
- y:基準位置的縱向偏移量
- 作用對象:FixLayoutHelper, ScrollFixLayoutHelper, FloatLayoutHelper的屬性
// 接口示意
public void setAlignType(int alignType)
public void setX(int x)
public void setY(int y)
// 具體使用
fixLayoutHelper.setAlignType(FixLayoutHelper.TOP_LEFT);
fixLayoutHelper.setX(30);
fixLayoutHelper.setY(50);
4. 可選顯示的固定布局(ScrollFixLayoutHelper)
- 布局說明:布局里的Item 固定位置
- 固定在屏幕某個位置共缕,且不可拖拽 & 不隨頁面滾動而滾動(繼承自固定布局(FixLayoutHelper))
- 唯一不同的是,可以自由設置該Item什么時候顯示(到頂部顯示 / 到底部顯示)图谷,可如下圖:(左上角)
- 需求場景:到頁面底部顯示”一鍵到頂部“的按鈕功能
以下示意圖為:滑動到底部翩活,布局才在左上角顯示
- 具體使用
/**
設置可選固定布局
*/
ScrollFixLayoutHelper scrollFixLayoutHelper = new ScrollFixLayoutHelper(ScrollFixLayoutHelper.TOP_RIGHT,0,0);
// 參數(shù)說明:
// 參數(shù)1:設置吸邊時的基準位置(alignType) - 有四個取值:TOP_LEFT(默認), TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT
// 參數(shù)2:基準位置的偏移量x
// 參數(shù)3:基準位置的偏移量y
// 公共屬性
scrollFixLayoutHelper.setItemCount(1);// 設置布局里Item個數(shù)
// 從設置Item數(shù)目的源碼可以看出,一個FixLayoutHelper只能設置一個
// @Override
// public void setItemCount(int itemCount) {
// if (itemCount > 0) {
// super.setItemCount(1);
// } else {
// super.setItemCount(0);
// }
// }
scrollFixLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
scrollFixLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
scrollFixLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
scrollFixLayoutHelper.setAspectRatio(6);// 設置設置布局內(nèi)每行布局的寬與高的比
// fixLayoutHelper特有屬性
scrollFixLayoutHelper.setAlignType(FixLayoutHelper.TOP_LEFT);// 設置吸邊時的基準位置(alignType)
scrollFixLayoutHelper.setX(30);// 設置基準位置的橫向偏移量X
scrollFixLayoutHelper.setY(50);// 設置基準位置的縱向偏移量Y
scrollFixLayoutHelper.setShowType(ScrollFixLayoutHelper.SHOW_ON_ENTER);// 設置Item的顯示模式
ScrollFixLayoutHelper特有屬性說明
a. AlignType便贵、x菠镇、y屬性
- 作用:
- alignType:吸邊基準類型
共有4個取值:TOP_LEFT(默認), TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,具體請看下面示意圖
- x:基準位置的橫向偏移量
- y:基準位置的縱向偏移量
- 具體使用
// 接口示意
public void setAlignType(int alignType)
public void setX(int x)
public void setY(int y)
// 具體使用
ScrollFixLayoutHelper.setAlignType(FixLayoutHelper.TOP_LEFT);
ScrollFixLayoutHelper.setX(30);
ScrollFixLayoutHelper.setY(50);
b. ShowType屬性
- 作用:設置Item的顯示模式
共有三種顯示模式
- SHOW_ALWAYS:永遠顯示(即效果同固定布局)
- SHOW_ON_ENTER:默認不顯示視圖承璃,當頁面滾動到該視圖位置時才顯示利耍;
- SHOW_ON_LEAVE:默認不顯示視圖,當頁面滾出該視圖位置時才顯示
- 具體使用
// 接口示意
public void setShowType(int showType)
// 具體使用
scrollFixLayoutHelper.setShowType(ScrollFixLayoutHelper.SHOW_ON_ENTER);
5. 浮動布局(FloatLayoutHelper)
- 布局說明:布局里的Item只有一個
- 可隨意拖動盔粹,但最終會被吸邊到兩側(cè)
- 不隨頁面滾動而移動
- 具體使用
/**
設置浮動布局
*/
FloatLayoutHelper floatLayoutHelper = new FloatLayoutHelper();
// 創(chuàng)建FloatLayoutHelper對象
// 公共屬性
floatLayoutHelper.setItemCount(1);// 設置布局里Item個數(shù)
// 從設置Item數(shù)目的源碼可以看出隘梨,一個FixLayoutHelper只能設置一個
// @Override
// public void setItemCount(int itemCount) {
// if (itemCount > 0) {
// super.setItemCount(1);
// } else {
// super.setItemCount(0);
// }
// }
floatLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
floatLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
floatLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
floatLayoutHelper.setAspectRatio(6);// 設置設置布局內(nèi)每行布局的寬與高的比
// floatLayoutHelper特有屬性
floatLayoutHelper.setDefaultLocation(300,300);// 設置布局里Item的初始位置
6. 欄格布局(ColumnLayoutHelper)
- 布局說明:該布局只設有一欄(該欄設置多個Item)
可理解為只有一行的線性布局
/**
設置欄格布局
*/
ColumnLayoutHelper columnLayoutHelper = new ColumnLayoutHelper();
// 創(chuàng)建對象
// 公共屬性
columnLayoutHelper.setItemCount(3);// 設置布局里Item個數(shù)
columnLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
columnLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
columnLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
columnLayoutHelper.setAspectRatio(6);// 設置設置布局內(nèi)每行布局的寬與高的比
// columnLayoutHelper特有屬性
columnLayoutHelper.setWeights(new float[]{30, 40, 30});// 設置該行每個Item占該行總寬度的比例
// 同上面Weigths屬性講解
7. 通欄布局(SingleLayoutHelper)
- 布局說明:布局只有一欄,該欄只有一個Item
- 具體使用
/**
設置通欄布局
*/
SingleLayoutHelper singleLayoutHelper = new SingleLayoutHelper();
// 公共屬性
singleLayoutHelper.setItemCount(3);// 設置布局里Item個數(shù)
singleLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
singleLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
singleLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
singleLayoutHelper.setAspectRatio(6);// 設置設置布局內(nèi)每行布局的寬與高的比
8. 一拖N布局 (OnePlusNLayoutHelper)
- 布局說明:將布局分為不同比例舷嗡,最多是1拖4轴猎。具體如下圖
- 具體使用
/**
設置1拖N布局
*/
OnePlusNLayoutHelper onePlusNLayoutHelper = new OnePlusNLayoutHelper(5);
// 在構造函數(shù)里傳入顯示的Item數(shù)
// 最多是1拖4,即5個
// 公共屬性
onePlusNLayoutHelper.setItemCount(3);// 設置布局里Item個數(shù)
onePlusNLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
onePlusNLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
onePlusNLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
onePlusNLayoutHelper.setAspectRatio(3);// 設置設置布局內(nèi)每行布局的寬與高的比
9. 吸邊布局(StickyLayoutHelper)
-
布局說明:布局只有一個Item,顯示邏輯如下:
- 當它包含的組件處于屏幕可見范圍內(nèi)時进萄,像正常的組件一樣隨頁面滾動而滾動
- 當組件將要被滑出屏幕返回的時候捻脖,可以吸到屏幕的頂部或者底部,實現(xiàn)一種吸住的效果
示意圖(吸在頂部)
- 具體使用
/**
設置吸邊布局
*/
StickyLayoutHelper stickyLayoutHelper = new StickyLayoutHelper();
// 公共屬性
stickyLayoutHelper.setItemCount(3);// 設置布局里Item個數(shù)
stickyLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
stickyLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
stickyLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
stickyLayoutHelper.setAspectRatio(3);// 設置設置布局內(nèi)每行布局的寬與高的比
// 特有屬性
stickyLayoutHelper.setStickyStart(true);
// true = 組件吸在頂部
// false = 組件吸在底部
stickyLayoutHelper.setOffset(100);// 設置吸邊位置的偏移量
Adapter_StickyLayout = new MyAdapter(this, stickyLayoutHelper,1, listItem) {
// 設置需要展示的數(shù)據(jù)總數(shù),此處設置是1
// 為了展示效果,通過重寫onBindViewHolder()將布局的第一個數(shù)據(jù)設置為Stick
@Override
public void onBindViewHolder(MainViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
if (position == 0) {
holder.Text.setText("Stick");
}
}
};
adapters.add(Adapter_StickyLayout) ;
// 將當前的Adapter加入到Adapter列表里
stickyStart中鼠、 offset屬性說明
-
作用:
-
stickyStart
:設置吸邊位置
當視圖的位置在屏幕范圍內(nèi)時可婶,視圖會隨頁面滾動而滾動;當視圖的位置滑出屏幕時援雇,StickyLayoutHelper會將視圖固定在頂部(stickyStart = true)或 底部(stickyStart = false)
-
offset
:設置吸邊的偏移量
-
具體使用
// 接口示意
public void setStickyStart(boolean stickyStart)
public void setOffset(int offset)
// 具體使用
stickyLayoutHelper.setStickyStart(true);
// true = 組件吸在頂部
// false = 組件吸在底部
stickyLayoutHelper.setOffset(100);// 設置吸邊位置的偏移量
10. 瀑布流布局(StaggeredGridLayoutHelper)
- 布局說明:以網(wǎng)格的形式進行布局矛渴。與網(wǎng)格布局類似,區(qū)別在于:
- 網(wǎng)格布局每欄的Item高度是相等的熊杨;
- 瀑布流布局每欄的Item高度是可以不相等的曙旭。
- 具體使用
/**
設置瀑布流布局
*/
StaggeredGridLayoutHelper staggeredGridLayoutHelper = new StaggeredGridLayoutHelper();
// 創(chuàng)建對象
// 公有屬性
staggeredGridLayoutHelper.setItemCount(20);// 設置布局里Item個數(shù)
staggeredGridLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
staggeredGridLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
staggeredGridLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
staggeredGridLayoutHelper.setAspectRatio(3);// 設置設置布局內(nèi)每行布局的寬與高的比
// 特有屬性
staggeredGridLayoutHelper.setLane(3);// 設置控制瀑布流每行的Item數(shù)
staggeredGridLayoutHelper.setHGap(20);// 設置子元素之間的水平間距
staggeredGridLayoutHelper.setVGap(15);// 設置子元素之間的垂直間距
自定義布局(即自定義LayoutHelper)
除了使用系統(tǒng)提供的默認布局 LayoutHelper
,開發(fā)者還可以通過自定義LayoutHelper從而實現(xiàn)自定義布局樣式晶府。有三種方式:
- 繼承
BaseLayoutHelper
:從上而下排列的順序 & 內(nèi)部View
可以按行回收的布局;主要實現(xiàn)layoutViews()
钻趋、computeAlignOffset()
等方法
LinearLayoutHelper
川陆、GridLayoutHelper
都是采用該方法實現(xiàn)
- 繼承
AbstractFullFillLayoutHelper
:有些布局內(nèi)部的View
并不是從上至下排列的順序(即Adatper
里的數(shù)據(jù)順序和物理視圖順序不一致,那么可能就不能按數(shù)據(jù)順序布局和回收)蛮位,需要一次性布局
& 回收较沪。主要實現(xiàn)layoutViews()
等方法
OnePlusNLayoutHelper
采用該方法實現(xiàn)
- 繼承
FixAreaLayoutHelper
:fix
類型布局鳞绕,子節(jié)點不隨頁面滾動而滾動。主要實現(xiàn)layoutViews()
尸曼、beforeLayout()
们何、afterLayout()
等方法
FixLayoutHelper
采用該方法實現(xiàn)
步驟5:將生成的LayoutHelper 交給Adapter,并綁定到RecyclerView 對象
此處的做法會因步驟3中Adapter的設置而有所不同
<-- Adapter繼承 自 DelegateAdapter -->
// 步驟1:設置Adapter列表(同時也是設置LayoutHelper列表)
List<DelegateAdapter.Adapter> adapters = new LinkedList<>();
// 步驟2:創(chuàng)建自定義的Adapter對象 & 綁定數(shù)據(jù) & 綁定上述對應的LayoutHelper
// 綁定你需要展示的布局LayoutHelper即可控轿,此處僅展示兩個冤竹。
MyAdapter Adapter_linearLayout = new MyAdapter(this, linearLayoutHelper,ListItem)茬射;
// ListItem是需要綁定的數(shù)據(jù)(其實取決于你的Adapter如何定義)
MyAdapter Adapter_gridLayoutHelper = new MyAdapter(this, gridLayoutHelper鹦蠕,ListItem);
// 步驟3:將創(chuàng)建的Adapter對象放入到DelegateAdapter.Adapter列表里
adapters.add(Adapter_linearLayout ) ;
adapters.add(Adapter_gridLayoutHelper ) ;
// 步驟4:創(chuàng)建DelegateAdapter對象 & 將layoutManager綁定到DelegateAdapter
DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager);
// 步驟5:將DelegateAdapter.Adapter列表綁定到DelegateAdapter
delegateAdapter.setAdapters(adapters);
// 步驟6:將delegateAdapter綁定到recyclerView
recyclerView.setAdapter(delegateAdapter);
<-- Adapter繼承 自 VirtualLayoutAdapter -->
// 步驟1:設置LayoutHelper列表
List<LayoutHelper> helpers = new LinkedList<>();
// 步驟2:綁定上述對應的LayoutHelper
helpers.add(Adapter_linearLayout )在抛;
helpers.add(Adapter_gridLayoutHelper ) ;
// 步驟3:創(chuàng)建自定義的MyAdapter對象 & 綁定layoutManager
MyAdapter myAdapter = new MyAdapter(layoutManager);
// 步驟4:將 layoutHelper 列表傳遞給 adapter
myAdapter.setLayoutHelpers(helpers);
// 步驟5:將adapter綁定到recyclerView
recycler.setAdapter(myAdapter);
至此钟病,使用過程講解完畢。
6. 實例說明
- V-Layout的優(yōu)點在于快速的組合不同布局
- 下面刚梭,我將根據(jù)上面的步驟說明肠阱,用一個實例來使用
V - Layout
快速組合布局
步驟1:在Android - Gradle
加入依賴
compile ('com.alibaba.android:vlayout:1.0.3@aar') {
transitive = true
}
步驟2:定義主 xml
布局
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="scut.carson_ho.v_layoutusage.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="horizontal" />
</RelativeLayout>
步驟3:定義 RecyclerView
每個子元素( Item
)的xml布局
item.xml
此處定義的
Item
布局是常用的 上字下圖
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="New Text"
android:id="@+id/Item" />
<ImageView
android:layout_alignParentRight="true"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/Image"/>
</LinearLayout>
步驟4:設置Adapter
- 設置 V - Layout的Adapter有兩種方式:
- 繼承 自
DelegateAdapter
- 繼承 自
此處主要以該方式進行演示
- 繼承 自
VirtualLayoutAdapter
- 具體使用
MyAdapter.java
public class MyAdapter extends DelegateAdapter.Adapter<MyAdapter.MainViewHolder> {
// 使用DelegateAdapter首先就是要自定義一個它的內(nèi)部類Adapter,讓LayoutHelper和需要綁定的數(shù)據(jù)傳進去
// 此處的Adapter和普通RecyclerView定義的Adapter只相差了一個onCreateLayoutHelper()方法朴读,其他的都是一樣的做法.
private ArrayList<HashMap<String, Object>> listItem;
// 用于存放數(shù)據(jù)列表
private Context context;
private LayoutHelper layoutHelper;
private RecyclerView.LayoutParams layoutParams;
private int count = 0;
private MyItemClickListener myItemClickListener;
// 用于設置Item點擊事件
//構造函數(shù)(傳入每個的數(shù)據(jù)列表 & 展示的Item數(shù)量)
public MyAdapter(Context context, LayoutHelper layoutHelper, int count, ArrayList<HashMap<String, Object>> listItem) {
this(context, layoutHelper, count, new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300), listItem);
}
public MyAdapter(Context context, LayoutHelper layoutHelper, int count, @NonNull RecyclerView.LayoutParams layoutParams, ArrayList<HashMap<String, Object>> listItem) {
this.context = context;
this.layoutHelper = layoutHelper;
this.count = count;
this.layoutParams = layoutParams;
this.listItem = listItem;
}
// 把ViewHolder綁定Item的布局
@Override
public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MainViewHolder(LayoutInflater.from(context).inflate(R.layout.item, parent, false));
}
// 此處的Adapter和普通RecyclerView定義的Adapter只相差了一個onCreateLayoutHelper()方法
@Override
public LayoutHelper onCreateLayoutHelper() {
return layoutHelper;
}
// 綁定Item的數(shù)據(jù)
@Override
public void onBindViewHolder(MainViewHolder holder, int position) {
holder.Text.setText((String) listItem.get(position).get("ItemTitle"));
holder.image.setImageResource((Integer) listItem.get(position).get("ItemImage"));
}
// 返回Item數(shù)目
@Override
public int getItemCount() {
return count;
}
// 設置Item的點擊事件
// 綁定MainActivity傳進來的點擊監(jiān)聽器
public void setOnItemClickListener(MyItemClickListener listener) {
myItemClickListener = listener;
}
//定義Viewholder
class MainViewHolder extends RecyclerView.ViewHolder {
public TextView Text;
public ImageView image;
public MainViewHolder(View root) {
super(root);
// 綁定視圖
Text = (TextView) root.findViewById(R.id.Item);
image = (ImageView) root.findViewById(R.id.Image);
root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (myItemClickListener != null)
myItemClickListener.onItemClick(v, getPosition());
}
}
//監(jiān)聽到點擊就回調(diào)MainActivity的onItemClick函數(shù)
);
}
public TextView getText() {
return Text;
}
}
}
以下步驟都將寫在同一個.Java
文件里
步驟5:創(chuàng)建RecyclerView & VirtualLayoutManager 對象并進行綁定
步驟6:設置回收復用池大小
步驟7:設置需要存放的數(shù)據(jù)
步驟8:根據(jù)數(shù)據(jù)列表屹徘,創(chuàng)建對應的LayoutHelper
步驟9:將生成的LayoutHelper 交給Adapter,并綁定到RecyclerView 對象
詳細請看注釋
MainActivity.java
public class MainActivity extends AppCompatActivity implements MyItemClickListener {
RecyclerView recyclerView;
MyAdapter Adapter_linearLayout,Adapter_GridLayout,Adapter_FixLayout,Adapter_ScrollFixLayout
,Adapter_FloatLayout,Adapter_ColumnLayout,Adapter_SingleLayout,Adapter_onePlusNLayout,
Adapter_StickyLayout,Adapter_StaggeredGridLayout;
private ArrayList<HashMap<String, Object>> listItem;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 步驟1:創(chuàng)建RecyclerView & VirtualLayoutManager 對象并進行綁定
* */
recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
// 創(chuàng)建RecyclerView對象
VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);
// 創(chuàng)建VirtualLayoutManager對象
// 同時內(nèi)部會創(chuàng)建一個LayoutHelperFinder對象磨德,用來后續(xù)的LayoutHelper查找
recyclerView.setLayoutManager(layoutManager);
// 將VirtualLayoutManager綁定到recyclerView
/**
* 步驟2:設置組件復用回收池
* */
RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
recyclerView.setRecycledViewPool(viewPool);
viewPool.setMaxRecycledViews(0, 10);
/**
* 步驟3:設置需要存放的數(shù)據(jù)
* */
listItem = new ArrayList<HashMap<String, Object>>();
for (int i = 0; i < 100; i++) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("ItemTitle", "第" + i + "行");
map.put("ItemImage", R.mipmap.ic_launcher);
listItem.add(map);
}
/**
* 步驟4:根據(jù)數(shù)據(jù)列表,創(chuàng)建對應的LayoutHelper
* */
// 為了展示效果,此處將上面介紹的所有布局都顯示出來
/**
設置線性布局
*/
LinearLayoutHelper linearLayoutHelper = new LinearLayoutHelper();
// 創(chuàng)建對應的LayoutHelper對象
// 公共屬性
linearLayoutHelper.setItemCount(4);// 設置布局里Item個數(shù)
linearLayoutHelper.setPadding(20, 20, 20, 20);// 設置LayoutHelper的子元素相對LayoutHelper邊緣的距離
linearLayoutHelper.setMargin(20, 20, 20, 20);// 設置LayoutHelper邊緣相對父控件(即RecyclerView)的距離
// linearLayoutHelper.setBgColor(Color.GRAY);// 設置背景顏色
linearLayoutHelper.setAspectRatio(6);// 設置設置布局內(nèi)每行布局的寬與高的比
// linearLayoutHelper特有屬性
linearLayoutHelper.setDividerHeight(10);
// 設置間隔高度
// 設置的間隔會與RecyclerView的addItemDecoration()添加的間隔疊加.
linearLayoutHelper.setMarginBottom(100);
// 設置布局底部與下個布局的間隔
// 創(chuàng)建自定義的Adapter對象 & 綁定數(shù)據(jù) & 綁定對應的LayoutHelper進行布局繪制
Adapter_linearLayout = new MyAdapter(this, linearLayoutHelper, 4, listItem) {
// 參數(shù)2:綁定綁定對應的LayoutHelper
// 參數(shù)3:傳入該布局需要顯示的數(shù)據(jù)個數(shù)
// 參數(shù)4:傳入需要綁定的數(shù)據(jù)
// 通過重寫onBindViewHolder()設置更豐富的布局效果
@Override
public void onBindViewHolder(MainViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
// 為了展示效果,將布局的第一個數(shù)據(jù)設置為linearLayout
if (position == 0) {
holder.Text.setText("Linear");
}
//為了展示效果,將布局里不同位置的Item進行背景顏色設置
if (position < 2) {
holder.itemView.setBackgroundColor(0x66cc0000 + (position - 6) * 128);
} else if (position % 2 == 0) {
holder.itemView.setBackgroundColor(0xaa22ff22);
} else {
holder.itemView.setBackgroundColor(0xccff22ff);
}
}
};
Adapter_linearLayout.setOnItemClickListener(this);
// 設置每個Item的點擊事件
....// 還有其他布局缘回,由于代碼量就較多就不貼出來了。
/**
*步驟5:將生成的LayoutHelper 交給Adapter典挑,并綁定到RecyclerView 對象
**/
// 1. 設置Adapter列表(同時也是設置LayoutHelper列表)
List<DelegateAdapter.Adapter> adapters = new LinkedList<>();
// 2. 將上述創(chuàng)建的Adapter對象放入到DelegateAdapter.Adapter列表里
adapters.add(Adapter_linearLayout) ;
adapters.add(Adapter_StickyLayout) ;
adapters.add(Adapter_ScrollFixLayout) ;
adapters.add(Adapter_GridLayout) ;
adapters.add(Adapter_FixLayout) ;
adapters.add(Adapter_FloatLayout) ;
adapters.add(Adapter_ColumnLayout) ;
adapters.add(Adapter_SingleLayout) ;
adapters.add(Adapter_onePlusNLayout) ;
adapters.add(Adapter_StaggeredGridLayout) ;
// 3. 創(chuàng)建DelegateAdapter對象 & 將layoutManager綁定到DelegateAdapter
DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager);
// 4. 將DelegateAdapter.Adapter列表綁定到DelegateAdapter
delegateAdapter.setAdapters(adapters);
// 5. 將delegateAdapter綁定到recyclerView
recyclerView.setAdapter(delegateAdapter);
/**
*步驟6:Item之間的間隔
**/
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(5, 5, 5, 5);
}
});
}
/**
*步驟7:實現(xiàn)Item點擊事件
**/
// 點擊事件的回調(diào)函數(shù)
@Override
public void onItemClick(View view, int postion) {
System.out.println("點擊了第"+postion+"行");
Toast.makeText(this, (String) listItem.get(postion).get("ItemTitle"), Toast.LENGTH_SHORT).show();
}
}
效果圖
源碼地址
Carson_Ho的Github地址:V - Layout
參考文檔:
https://github.com/alibaba/vlayout
http://pingguohe.net/2017/02/28/vlayout-design.html
7. 總結(jié)
- 看完本文酥宴,你應該非常了解阿里出品的
V - Layout
的使用 & 原理 - 但該開源庫還是存在許多小
Bug
,我在Github上也提交了一些您觉,希望大家能一起在Github - alibaba - vlayout 上進行完善拙寡,共同為開源事業(yè)做貢獻吧!
Carson帶你學Android開源庫系列文章:
Carson帶你學Android:主流開源圖片加載庫對比(UIL琳水、Picasso肆糕、Glide、Fresco)
Carson帶你學Android:主流開源網(wǎng)絡請求庫對比(Volley在孝、OkHttp诚啃、Retrofit)
Carson帶你學Android:網(wǎng)絡請求庫Retrofit使用教程
Carson帶你學Android:網(wǎng)絡請求庫Retrofit源碼分析
Carson帶你學Android:圖片加載庫Glide使用教程
Carson帶你學Android:圖片加載庫Glide源碼分析
Carson帶你學Android:V-Layout,淘寶私沮、天貓都在用的UI框架始赎,趕緊用起來吧!
歡迎關注Carson_Ho的簡書
不定期分享關于安卓開發(fā)的干貨,追求短造垛、平魔招、快,但卻不缺深度五辽。