1 . 前言
前幾天看到Google官方的博客介紹了Google開源的一個強大的佈局-FlexboxLayout,看見第一眼我內心的想法是,臥槽,Android 竟然有這麼一個強大的佈局。做爲一個有好奇心的工程獅,固然第一時間就去試了試手,效果很是贊,所以這篇文章就介紹一下它的用法和最新版添加的一些特性(支持集成RecyclerView),Github地址:https://github.com/google/flexbox-layout 。本文目錄以下:css
![](http://static.javashuo.com/static/loading.gif)
2 . 什麼是FlexboxLayout
那麼FlexboxLayout 它究竟是個什麼東西呢?看一下Github對這個庫的介紹:FlexboxLayout is a library project which brings the similar capabilities of CSS Flexible Box Layout Module to Android. 意思是:FlexboxLayout是一個Android平臺上與CSS的 Flexible box 佈局模塊 有類似功能的庫。Flexbox 是CSS 的一種佈局方案,能夠簡單、快捷的實現複雜佈局。FlexboxLayout能夠理解成一個高級版的LinearLayout,由於兩個佈局都把子view按順序排列。二者之間最大的差異在於FlexboxLayout具備換行的特性。html
3 . FlexboxLayout示例
既然說FlexboxLayout方便、強大,那麼咱們就先以一個示例來看一下它的一個簡單實用場景:如今不少APP都有標籤功能,本節以簡書首頁的熱門專題(標籤)爲例,看一下使用FlexboxLayout來實現有多方便。
簡書首頁熱門專題以下圖:android
![](http://static.javashuo.com/static/loading.gif)
使用Flexbox實現效果以下:
![](http://static.javashuo.com/static/loading.gif)
添加依賴:git
implementation 'com.google.android:flexbox:1.0.0'
代碼以下:程序員
1 <?xml version="1.0" encoding="utf-8"?> 2 <com.google.android.flexbox.FlexboxLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 xmlns:app="http://schemas.android.com/apk/res-auto" 7 android:id="@+id/flexbox_layout" 8 app:flexWrap="wrap" 9 app:alignItems="center" 10 app:alignContent="flex_start" 11 app:flexDirection="row" 12 app:justifyContent="flex_start" 13 app:showDivider="beginning|middle" 14 app:dividerDrawable="@drawable/divider_shape" 15 > 16 17 <TextView 18 android:layout_width="wrap_content" 19 android:layout_height="40dp" 20 android:paddingLeft="15dp" 21 android:paddingRight="15dp" 22 app:layout_alignSelf="flex_start" 23 android:text="程序員" 24 android:gravity="center" 25 android:textColor="@color/text_color" 26 android:background="@drawable/label_bg_shape" 27 /> 28 <TextView 29 android:layout_width="wrap_content" 30 android:layout_height="40dp" 31 android:paddingLeft="15dp" 32 android:paddingRight="15dp" 33 app:layout_alignSelf="flex_start" 34 android:text="影視天堂" 35 app:layout_flexGrow="1" 36 android:gravity="center" 37 android:textColor="@color/text_color" 38 android:background="@drawable/label_bg_shape" 39 /> 40 <TextView 41 android:layout_width="wrap_content" 42 android:layout_height="40dp" 43 android:paddingLeft="15dp" 44 android:paddingRight="15dp" 45 app:layout_flexGrow="1" 46 app:layout_alignSelf="flex_start" 47 android:text="美食" 48 android:gravity="center" 49 android:textColor="@color/text_color" 50 android:background="@drawable/label_bg_shape" 51 /> 52 <TextView 53 android:layout_width="wrap_content" 54 android:layout_height="40dp" 55 android:paddingLeft="15dp" 56 android:paddingRight="15dp" 57 app:layout_flexGrow="1" 58 app:layout_alignSelf="flex_start" 59 android:text="漫畫.手繪" 60 android:gravity="center" 61 android:textColor="@color/text_color" 62 android:background="@drawable/label_bg_shape" 63 /> 64 <TextView 65 android:layout_width="wrap_content" 66 android:layout_height="40dp" 67 android:paddingLeft="15dp" 68 android:paddingRight="15dp" 69 app:layout_alignSelf="flex_start" 70 android:text="廣告圈" 71 android:gravity="center" 72 android:textColor="@color/text_color" 73 android:background="@drawable/label_bg_shape" 74 /> 75 <TextView 76 android:layout_width="wrap_content" 77 android:layout_height="40dp" 78 android:paddingLeft="15dp" 79 android:paddingRight="15dp" 80 app:layout_alignSelf="flex_start" 81 android:text="旅行.在路上" 82 android:gravity="center" 83 android:textColor="@color/text_color" 84 android:background="@drawable/label_bg_shape" 85 /> 86 <TextView 87 android:layout_width="wrap_content" 88 android:layout_height="40dp" 89 android:paddingLeft="15dp" 90 android:paddingRight="15dp" 91 app:layout_alignSelf="flex_start" 92 android:text="娛樂八卦" 93 android:gravity="center" 94 android:textColor="@color/text_color" 95 android:background="@drawable/label_bg_shape" 96 /> 97 <TextView 98 android:layout_width="wrap_content" 99 android:layout_height="40dp" 100 android:paddingLeft="15dp" 101 android:paddingRight="15dp" 102 app:layout_alignSelf="flex_start" 103 android:text="青春" 104 android:gravity="center" 105 android:textColor="@color/text_color" 106 android:background="@drawable/label_bg_shape" 107 /> 108 <TextView 109 android:layout_width="wrap_content" 110 android:layout_height="40dp" 111 android:paddingLeft="15dp" 112 android:paddingRight="15dp" 113 app:layout_alignSelf="flex_start" 114 android:text="談寫做" 115 android:gravity="center" 116 android:textColor="@color/text_color" 117 android:background="@drawable/label_bg_shape" 118 /> 119 <TextView 120 android:layout_width="wrap_content" 121 android:layout_height="40dp" 122 android:paddingLeft="15dp" 123 android:paddingRight="15dp" 124 app:layout_alignSelf="flex_start" 125 android:text="短篇小說" 126 android:gravity="center" 127 android:textColor="@color/text_color" 128 android:background="@drawable/label_bg_shape" 129 /> 130 <TextView 131 android:layout_width="wrap_content" 132 android:layout_height="40dp" 133 android:paddingLeft="15dp" 134 android:paddingRight="15dp" 135 app:layout_alignSelf="flex_start" 136 android:text="散文" 137 android:gravity="center" 138 android:textColor="@color/text_color" 139 android:background="@drawable/label_bg_shape" 140 /> 141 <TextView 142 android:layout_width="wrap_content" 143 android:layout_height="40dp" 144 android:paddingLeft="15dp" 145 android:paddingRight="15dp" 146 app:layout_alignSelf="flex_start" 147 android:text="攝影" 148 app:layout_order="2" 149 android:gravity="center" 150 android:textColor="@color/text_color" 151 android:background="@drawable/label_bg_shape" 152 /> 153 </com.google.android.flexbox.FlexboxLayout>
很簡單,就一個佈局文件,以FlexboxLayout爲父佈局,向容器裏面添加子Item 就好了。固然了,你能夠在代碼中向FlexboxLayout佈局動態添加子元素,代碼以下:github
1 mFlexboxLayout = (FlexboxLayout) findViewById(R.id.flexbox_layout); 2 // 經過代碼向FlexboxLayout添加View 3 TextView textView = new TextView(this); 4 textView.setBackground(getResources().getDrawable(R.drawable.label_bg_shape)); 5 textView.setText("Test Label"); 6 textView.setGravity(Gravity.CENTER); 7 textView.setPadding(30,0,30,0); 8 textView.setTextColor(getResources().getColor(R.color.text_color)); 9 mFlexboxLayout.addView(textView); 10 //經過FlexboxLayout.LayoutParams 設置子元素支持的屬性 11 ViewGroup.LayoutParams params = textView.getLayoutParams(); 12 if(params instanceof FlexboxLayout.LayoutParams){ 13 FlexboxLayout.LayoutParams layoutParams = (FlexboxLayout.LayoutParams) params; 14 layoutParams.setFlexBasisPercent(0.5f); 15 }
要是不用這FlexboxLayout,之前要實現這樣的一個界面,須要自定義View,仍是有點麻煩,網上也有不少經過自定義View來實現可伸縮變遷界面的,有興趣的能夠去搜來看一下。要知道,這只是FlexboxLayout的冰山一角,FlexboxLayout真正強大的是它定義的這些屬性,經過設置不一樣的值能夠獲得不一樣的效果,接下來就看一下它支持哪些屬性。app
4 . FlexboxLayout支持的屬性介紹
上面說了FlexboxLayout真正強大的是它定義的屬性,那麼這一節咱們看一下Flexbox支持哪些屬性,分爲2個方面,FlexboxLayout支持的屬性和FlexboxLayout 子元素支持的屬性:ide
FlexboxLayout 支持的屬性:佈局
flexDirection
flexDirection
屬性決定了主軸的方向,即FlexboxLayout裏子Item的排列方向,有如下四種取值:post
- row (default): 默認值,主軸爲水平方向,起點在左端,從左到右。
- row_reverse:主軸爲水平方向,起點在右端,從右到左。
- column:主軸爲豎直方向,起點在上端,從上到下。
-
column_reverse:主軸爲豎直方向,起點在下端,從下往上。
文字有點蒼白無力,看一下四種排列的對比,就明白了:
![](http://static.javashuo.com/static/loading.gif)
flexWrap
flexWrap
這個屬性決定Flex 容器是單行仍是多行,而且決定副軸(與主軸垂直的軸)的方向。可能有如下3個值:
- noWrap: 不換行,一行顯示完子元素。
- wrap: 按正常方向換行。
- wrap_reverse: 按反方向換行。
justifyContent
justifyContent
屬性控制元素主軸方向上的對齊方式,有如下5種取值:
- flex_start (default): 默認值,左對齊
- flex_end: 右對齊
- center: 居中對齊
- space_between: 兩端對齊,中間間隔相同
- space_around: 每一個元素到兩側的距離相等。
alignItems
alignItems
屬性控制元素在副軸方向的對齊方式,有如下5種取值:
- stretch (default) :默認值,若是item沒有設置高度,則充滿容器高度。
- flex_start:頂端對齊
- flex_end:底部對齊
- center:居中對齊
- baseline:第一行內容的的基線對齊。
文字顯示有些蒼白無力,用一張圖來對比看一下,更好理解(圖片來自Google 開源的 Android 排版庫:FlexboxLayout):
![](http://static.javashuo.com/static/loading.gif)
alignContent
alignContent
屬性控制多根軸線的對齊方式(也就是控制多行,若是子元素只有一行,則不起做用),可能有一下6種取值:
- stretch (default): 默認值,充滿交叉軸的高度(測試發現,須要alignItems 的值也爲stretch 纔有效)。
- flex_start: 與交叉軸起點對齊。
- flex_end: 與交叉軸終點對齊。
- center: 與交叉軸居中對齊。
- space_between: 交叉軸兩端對齊,中間間隔相等。
- space_around: 到交叉軸兩端的距離相等。
showDividerHorizontal
showDividerHorizontal
控制顯示水平方向的分割線,值爲none | beginning | middle | end
其中的一個或者多個。
dividerDrawableHorizontal
dividerDrawableHorizontal
設置Flex 軸線之間水平方向的分割線。
showDividerVertical
showDividerVertical
控制顯示垂直方向的分割線,值爲none | beginning | middle | end
其中的一個或者多個。
dividerDrawableVertical
dividerDrawableVertical
設置子元素垂直方向的分割線。
showDivider
showDivider
控制顯示水平和垂直方向的分割線,值爲none | beginning | middle | end
其中的一個或者多個。
dividerDrawable
dividerDrawable
設置水平和垂直方向的分割線,可是注意,若是同時和其餘屬性使用,好比爲 Flex 軸、子元素設置了justifyContent="space_around"
、alignContent="space_between"
等等。可能會看到意料不到的空間,所以應該避免和這些值同時使用。
5 . FleboxLayout子元素支持的屬性介紹
FlexboxLayout 子元素支持的屬性:
layout_order
layout_order
屬性能夠改變子元素的排列順序,默認狀況下,FlexboxLayout子元素的排列是按照xml文件中出現的順序。默認值爲1,值越小排在越靠前。
layout_flexGrow(float)
layout_flexGrow
子元素的放大比例, 決定如何分配剩餘空間(若是存在剩餘空間的話),默認值爲0,不會分配剩餘空間,若是有一個item的 layout_flexGrow
是一個正值,那麼會將所有剩餘空間分配給這個Item,若是有多個Item這個屬性都爲正值,那麼剩餘空間的分配按照layout_flexGrow
定義的比例(有點像LinearLayout
的layout_weight
屬性)。
layout_flexShrink(float)
layout_flexShrink
:子元素縮小比例,當空間不足時,子元素須要縮小(設置了換行則無效),默認值爲1,若是全部子元素的layout_flexShrink
值爲1,空間不足時,都等比縮小,若是有一個爲0,其餘爲1,空間不足時,爲0的不縮小,負值無效。
layout_alignSelf
layout_alignSelf
屬性能夠給子元素設置對齊方式,上面講的alignItems
屬性能夠設置對齊,這個屬性的功能和alignItems
同樣,只不過alignItems
做用於全部子元素,而layout_alignSelf
做用於單個子元素。默認值爲auto, 表示繼承alignItems
屬性,若是爲auto之外的值,則會覆蓋alignItems
屬性。有如下6種取值:
- auto (default)
- flex_start
- flex_end
- center
- baseline
- stretch
除了auto
之外,其餘和alignItems
屬性同樣。
layout_flexBasisPercent (fraction)
layout_flexBasisPercent
的值爲一個百分比,表示設置子元素的長度爲它父容器長度的百分比,若是設置了這個值,那麼經過這個屬性計算的值將會覆蓋layout_width
或者layout_height
的值。可是須要注意,這個值只有設置了父容器的長度時纔有效(也就是MeasureSpec mode 是 MeasureSpec.EXACTLY)。默認值時-1。
layout_minWidth / layout_minHeight (dimension)
強制限制 FlexboxLayout的子元素(寬或高)不會小於最小值,無論layout_flexShrink
這個屬性的值爲多少,子元素不會被縮小到小於設置的這個最小值。
layout_maxWidth / layout_maxHeight (dimension)
這個和上面的恰好相反,強制限制FlexboxLayout子元素不會大於這個最大值, 無論layout_flexGrow
的值爲多少,子元素不會被放大到超過這個最大值。
layout_wrapBefore
layout_wrapBefore
屬性控制強制換行,默認值爲false,若是將一個子元素的這個屬性設置爲true,那麼這個子元素將會成爲一行的第一個元素。這個屬性將忽略flex_wrap
設置的 noWrap值。
6 . 與RecyclerView 的結合
在最新的alpha版本,Flexbox可以做爲一個LayoutManager(FlexboxlayoutManager) 用在RecyclerView裏面,這也就意味着你能夠在一個有大量Item的可滾動容器裏面使用Flexbox了。
來看一下繼承RecyclerView 使用的示例:
(1)首先要依賴最新的alpha版本,以下:
// compile 'com.google.android:flexbox:0.2.5' // 使用最新的alpha版本 compile 'com.google.android:flexbox:0.3.0-alpha2'
(2)使用FlexboxLayoutManager代替LinearLayoutManager,以下:
FlexboxLayoutManager manager = new FlexboxLayoutManager(); //設置主軸排列方式 manager.setFlexDirection(FlexDirection.ROW); //設置是否換行 manager.setFlexWrap(FlexWrap.WRAP); manager.setAlignItems(AlignItems.STRETCH);
(3) 在Adapter裏設置 flexGrow :
ImageView mImageView = rvBaseViewHolder.getImageView(R.id.image_src); mImageView .setImageResource(mData); ViewGroup.LayoutParams lp = mImageView.getLayoutParams(); if (lp instanceof FlexboxLayoutManager.LayoutParams) { FlexboxLayoutManager.LayoutParams flexboxLp = (FlexboxLayoutManager.LayoutParams) mImageView.getLayoutParams(); flexboxLp.setFlexGrow(1.0f); }
效果圖以下:
![](http://static.javashuo.com/static/loading.gif)
是否是很強大?效果相似瀑布流,可是瀑布流是指定了列數的,這個徹底是自定換行,大小屏幕的適配徹底解決,簡直完美。可是目前與RecyclerView 的集成仍是alpha版本,尚未合併到master分支,相信不久後能出穩定的版本。
因爲RecyclerView 的一些特性,Flexbox 的一些屬性在FlexboxLayoutManager中沒有實現,下面是FlexboxLayout和FlexboxLayoutManager支持的屬性的對比:
![](http://static.javashuo.com/static/loading.gif)
注意紅框中圈出來的屬性,FlexboxlayoutManager是支持View回收的,而FlexboxLayout是不支持View回收的,FlexboxLayout只適用於少許Item的場景,這也是爲何會出現FlexboxLayoutManager的緣由吧.
7 . 總結
FlexboxLayout是Google 去年開源的一個與CSS Flexbox有相似功能的強大布局,具備換行特性,使用起來特別方便,可是,FlexboxLayout是沒有考慮View回收的,所以,它只使用於只有少許子Item的場景,若是向其中添加大量Item 是灰致使內存溢出的。所幸,最新的版本添加了與RecyclerView的集成,這就能夠在有大量子Item的場景下使用了,只是最新的版本仍是alpha版,尚未出穩定的版本,相信不久後就能使用穩定的版本了。另外,FlexboxLayout的這寫屬性的意義可能很差理解 ,建議你們去寫個demo試一下每一個屬性的每一個值看看是什麼效果,這樣就能很好的理解每一個屬性了。
參考:
https://github.com/google/flexbox-layout
Build flexible layouts with FlexboxLayout
Google 開源的 Android 排版庫:FlexboxLayout
Android可伸縮佈局-FlexboxLayout(支持RecyclerView集成)