某天在作項目的時候,遇到了個看似簡單但又無從下手的小功能,效果相似微信羣聊選擇聯繫人界面: html
而後一想,嘿嘿,沒事,輪子沒有本身造唄,一看就得自定義佈局。遂拿起個人小本本,準備大幹一番,用筆寫下思路,磨蹭了十幾分鍾,本子上只有幾個立體幾何。。。(有個小癖好,寫不出來喜歡在本子上畫立方體)。前端
而後想一時半會自定義也掌握很差啊,時間又趕,遂又開始了大規模搜索。在快要放棄的時候看到了這篇文章:Android 彈性佈局 FlexboxLayout瞭解一下 看到其中這張圖的時候,兩眼放光,異常激動啊。 react
嗯,廢話那麼多,鋪墊那麼長,下面來介紹一下今天的主角 : flexboxLayout 。android
首先要作的事情固然是在項目中集成了:git
compile 'com.google.android:flexbox:1.0.0'
複製代碼
github.com/google/flex… 項目簡介中是這樣一句話來歸納 flexboxLayout 的:github
FlexboxLayout is a library project which brings the similar capabilities of CSS Flexible Box Layout Module to Android. FlexboxLayout 是一個在 Android 上實現 CSS 的 彈性盒狀佈局 模塊的庫編程
有前端基礎的同窗估計都知道 CSS 中這個佈局,用來爲盒狀模型提供最大的靈活性。由於 android 中這個庫屬性和 CSS 中 都同樣,而且阮一峯老師寫的前端知識真的很通俗易懂,因此這裏的介紹大多來自 Flex 佈局教程。 segmentfault
容器默認存在兩根軸:水平的主軸(main axis)和垂直的交叉軸(cross axis)。這裏與 react native 相反,與前端 CSS 保持一致。bash
主軸的開始位置(與邊框的交叉點)叫作main start,結束位置叫作main end;交叉軸的開始位置叫作cross start,結束位置叫作cross end微信
項目默認沿主軸排列。單個項目佔據的主軸空間叫作 main size,佔據的交叉軸空間叫作 cross size。
這裏說的容器也就是上面的採用了 Flex 佈局的元素,在 android 中也就是引用了 FlexboxLayout 的 控件。即 FlexboxLayout 控件支持的屬性。主要屬性有:
設置被 FlexboxLayout 包裹的子 View 的屬性,由於 android 中的 flexboxLayout 佈局 和 CSS 中 flex 佈局關於子 View 屬性有些差別,因此這裏詳細說明下,取值以下:
下面來看一下github文檔中對這些屬性的描述
這個屬性能夠改變佈局子視圖的順序。默認狀況下,子元素的顯示和佈局順序與佈局XML中的順序相同。若是沒有指定,則將1設置爲默認值( CSS 中默認值爲 0) ,數值越小,排列越靠前。
這個屬性相似於 LinearLayout 中的 layout_weight 屬性,若是沒有指定,則將 0 設置爲默認值。若是果同一 flex 行中的多個子 View 有正的 layout_flexGrow 值,那麼剩餘的空閒空間將根據它們聲明的 layout_flexGrow 值的比例分佈。
該屬性定義了子 View 的縮小比例,默認爲 1,即若是空間不足,該子 View 將縮小。 若是全部子 View 的 layout_flexShrink 屬性都爲 1,當空間不足時,都將等比例縮小。若是一個項目的 layout_flexShrink 屬性爲0,其餘子View都爲 1,則空間不足時,layout_flexShrink 屬性爲 0 的不縮小。
layout_alignSelf 屬性容許單個子 View 有與其餘 View 不同的對齊方式,可覆蓋 align-items 屬性。默認值爲 auto,表示繼承父元素的 align-items 屬性,若是沒有父元素,則等同於 stretch。 該屬性可能取 6 個值,除了 auto,其餘都與 align-items 屬性徹底一致
flex-layout_flexBasisPercent 屬性定義了在分配多餘空間以前,子 View 佔據的主軸空間(main size)。根據這個屬性來計算主軸是否有多餘空間。它的默認值爲 -1,即不設置,採用子 View 的原本大小。
若是設置了這個值,layout_width (或 layout_height )中指定的長度將被該屬性的計算值覆蓋。這個屬性只有在父 View 的長度是肯定的時候纔有效(測量模式是 MeasureSpec.EXACTLY 模式下)。 而且該屬性值只接受百分比值。
這個屬性設置了子 View 的最小的寬和高。在 layout_flexShrink 模式下,再怎麼縮小也不會小於這個值
這個屬性設置了子 View 的最大的寬和高。在 layout_flexGrow 模式下,再怎麼放大也不會大於這個值
這個屬性使得子 View 能夠強制換行,無論在 main size 剩餘空間有多少。這種對於相似 grid 網格佈局中特殊設置某一個 item 佈局特別有用。 這個屬性是 CSS 中沒有的屬性。該屬性在 flex_wrap 屬性值 爲 nowrap(不換行)的時候是無效的。 該屬性結束 boolean 變量,默認 false,即不強制換行
到這裏,flexboxLayout 基本屬性就介紹完畢了。
而後再來介紹一下跟 recycleView 結合使用。
Flexbox 可以做爲一個 LayoutManager(FlexboxlayoutManager) 用在 RecyclerView 裏面,這也就意味着你能夠在一個有大量 Item 的可滾動容器裏面使用 Flexbox,提升性能。具體使用示例:
//設置主軸方向爲橫軸
FlexboxLayoutManager manager = new FlexboxLayoutManager(this, FlexDirection.ROW);
//設置item沿主軸方向的位置
manager.setJustifyContent(JustifyContent.CENTER);
//設置item 沿次軸方向的位置
manager.setAlignItems(AlignItems.CENTER);
recycleView.setLayoutManager(manager);
centerGridAdapter = new CenterGridAdapter(items, this);
recycleView.setAdapter(centerGridAdapter);
複製代碼
能夠看到跟 RecycleView 的其餘 manager 使用同樣,只需設置 manager 屬性便可,屬性值爲上面敘述的幾個容器的屬性。 若是想對某個 item 進行單獨的設置,能夠在 adapter 中去設置,設置示例代碼爲:
ViewGroup.LayoutParams lp = holder.itemLL.getLayoutParams();
if (lp instanceof FlexboxLayoutManager.LayoutParams) {
FlexboxLayoutManager.LayoutParams flexboxLp =
(FlexboxLayoutManager.LayoutParams) holder.itemLL.getLayoutParams();
flexboxLp.setFlexGrow(1.0f);
}
複製代碼
我這裏是設置每一個 item 有個權重(至關於 Linearlayout 的 weight 屬性),因此會按比例分配 item 的寬,而不是我佈局中設定的固定寬高。看下效果:
是否是有種鍵盤的感受?而且我只是修改了極少的代碼就實現了這個功能。
最後,看了那麼多,回到最開始的問題上,如今知道相似微信的那個中間擴展的網格佈局怎麼寫的嗎? 首先咱們簡單分析一下,
也就是上面講 Recycleview 結合的例子中去掉單獨設置 item 的部分,而且 item 的寬高要根據屏幕來適配的。 嗯,就是 so easy。
//設置主軸方向爲橫軸
FlexboxLayoutManager manager = new FlexboxLayoutManager(this, FlexDirection.ROW);
//設置item沿主軸方向的位置
manager.setJustifyContent(JustifyContent.FLEX_START);
//設置item 沿次軸方向的位置
manager.setAlignItems(AlignItems.CENTER);
recycleView.setLayoutManager(manager);
labelAdapter = new LabelAdapter(labels,this);
recycleView.setAdapter(labelAdapter);
複製代碼
因此說了這麼多,那麼咱們何時會用到這種佈局呢?我目前想到的場景主要有 3 類:
固然這些場景加上 RecycleView 就會更加暢享絲滑了。
demo 傳送門