Android 巧用 flexboxLayout 布局
FlexBoxlayout是Google推出的開(kāi)源的可伸縮性布局,在項(xiàng)目中也會(huì)經(jīng)場(chǎng)使用,大大提高了用戶的體驗(yàn)。
compile 'com.google.android:flexbox:1.0.0'有前端基礎(chǔ)的同學(xué)估計(jì)都知道 CSS 中這個(gè)布局,用來(lái)為盒狀模型提供最大的靈活性。因?yàn)?android 中這個(gè)庫(kù)屬性和 CSS 中 都一樣,并且阮一峰老師寫的前端知識(shí)真的很通俗易懂,所以這里的介紹大多來(lái)自?Flex 布局教程。
采用 Flex 布局的元素,稱為 Flex 容器(flex container),簡(jiǎn)稱”容器”。它的所有子元素自動(dòng)成為容器成員,稱為 Flex 項(xiàng)目(flex item),簡(jiǎn)稱”項(xiàng)目”。
容器默認(rèn)存在兩根軸:水平的主軸(main axis)和垂直的交叉軸(cross axis)。這里與 react native 相反,與前端 CSS 保持一致。
主軸的開(kāi)始位置(與邊框的交叉點(diǎn))叫做main start,結(jié)束位置叫做main end;交叉軸的開(kāi)始位置叫做cross start,結(jié)束位置叫做cross end
項(xiàng)目默認(rèn)沿主軸排列。單個(gè)項(xiàng)目占據(jù)的主軸空間叫做 main size,占據(jù)的交叉軸空間叫做 cross size。
2.容器的屬性 (FlexboxLayout 屬性介紹)
這里說(shuō)的容器也就是上面的采用了 Flex 布局的元素,在 android 中也就是引用了 FlexboxLayout 的 控件。即 FlexboxLayout 控件支持的屬性。主要屬性有:
各個(gè)屬性的詳細(xì)含義這里就不再贅述,阮一峰老師?這篇文章?寫的超級(jí)棒,圖文并茂,很容易理解,推薦大家看一下。
3.項(xiàng)目的屬性 (子 View 屬性介紹)
設(shè)置被 FlexboxLayout 包裹的子 View 的屬性,因?yàn)?android 中的 flexboxLayout 布局 和 CSS 中 flex 布局關(guān)于子 View 屬性有些差異,所以這里詳細(xì)說(shuō)明下,取值如下:
- layout_order (integer)
- layout_flexGrow (float)
- layout_flexGrow (float)
- layout_alignSelf
- layout_flexBasisPercent (fraction)
- layout_minWidth / layout_minHeight (dimension)
- layout_maxWidth / layout_maxHeight (dimension)
- layout_wrapBefore (boolean)
下面來(lái)看一下github文檔中對(duì)這些屬性的描述
3.1 layout_order
這個(gè)屬性可以改變布局子視圖的順序。默認(rèn)情況下,子元素的顯示和布局順序與布局XML中的順序相同。如果沒(méi)有指定,則將1設(shè)置為默認(rèn)值( CSS 中默認(rèn)值為 0) ,數(shù)值越小,排列越靠前。
看下文檔中的這張圖,可以看到將 “2” 號(hào) View 的 layout_order 屬性設(shè)置為 -1時(shí),由于其他的 View 默認(rèn)都是 1,所以 “2” 號(hào) view會(huì)排在最前面,同理,將 “2” 號(hào) View 的 layout_order 的屬性值設(shè)為 2 時(shí),比其他默認(rèn)值 1 都大,所以會(huì)排在最后。
3.2 layout_flexGrow
這個(gè)屬性類似于 LinearLayout 中的?layout_weight?屬性,如果沒(méi)有指定,則將 0 設(shè)置為默認(rèn)值。如果果同一 flex 行中的多個(gè)子 View 有正的 layout_flexGrow 值,那么剩余的空閑空間將根據(jù)它們聲明的 layout_flexGrow 值的比例分布。
3.3 layout_flexShrink
該屬性定義了子 View 的縮小比例,默認(rèn)為 1,即如果空間不足,該子 View 將縮小。
如果所有子 View 的 layout_flexShrink 屬性都為 1,當(dāng)空間不足時(shí),都將等比例縮小。如果一個(gè)項(xiàng)目的 layout_flexShrink 屬性為0,其他子View都為 1,則空間不足時(shí),layout_flexShrink 屬性為 0 的不縮小。
看一下文檔中的這張圖,開(kāi)始設(shè)置所有子 view 的 layout_flexShrink 屬性為1,添加子 view 的時(shí)候所有子 view 等比縮小,但是如果設(shè)置 layout_flexShrink 屬性值為 0,子 view 將會(huì)按照原有比例顯示,不縮小。
3.4 layout_alignSelf
layout_alignSelf 屬性允許單個(gè)子 View 有與其他 View 不一樣的對(duì)齊方式,可覆蓋 align-items 屬性。默認(rèn)值為 auto,表示繼承父元素的 align-items 屬性,如果沒(méi)有父元素,則等同于 stretch。
該屬性可能取 6 個(gè)值,除了 auto,其他都與 align-items 屬性完全一致
3.5 layout_flexBasisPercent
flex-layout_flexBasisPercent 屬性定義了在分配多余空間之前,子 View 占據(jù)的主軸空間(main size)。根據(jù)這個(gè)屬性來(lái)計(jì)算主軸是否有多余空間。它的默認(rèn)值為 -1,即不設(shè)置,采用子 View 的本來(lái)大小。
如果設(shè)置了這個(gè)值,layout_width (或 layout_height )中指定的長(zhǎng)度將被該屬性的計(jì)算值覆蓋。這個(gè)屬性只有在父 View 的長(zhǎng)度是確定的時(shí)候才有效(測(cè)量模式是 MeasureSpec.EXACTLY 模式下)。
并且該屬性值只接受百分比值。
可以分析下文檔中的這張圖:可以看到,如果把中間子 View 的這個(gè)屬性值設(shè)為 50% 或 90%,那么這個(gè) View 將占據(jù)主軸 50% 或 90% 的空間,然后剩余 View 會(huì)看有沒(méi)有剩余空間換行。如果設(shè)置為 -1 默認(rèn)值,那么將占據(jù)給定的大小。
3.6 layout_minWidth / layout_minHeight
這個(gè)屬性設(shè)置了子 View 的最小的寬和高。在 layout_flexShrink 模式下,再怎么縮小也不會(huì)小于這個(gè)值
3.7 layout_maxWidth / layout_maxHeight
這個(gè)屬性設(shè)置了子 View 的最大的寬和高。在 layout_flexGrow 模式下,再怎么放大也不會(huì)大于這個(gè)值
3.8 layout_wrapBefore
這個(gè)屬性使得子 View 可以強(qiáng)制換行,不管在 main size 剩余空間有多少。這種對(duì)于類似 grid 網(wǎng)格布局中特殊設(shè)置某一個(gè) item 布局特別有用。
這個(gè)屬性是 CSS 中沒(méi)有的屬性。該屬性在 flex_wrap 屬性值 為 nowrap(不換行)的時(shí)候是無(wú)效的。
該屬性結(jié)束 boolean 變量,默認(rèn) false,即不強(qiáng)制換行
分析下文檔中的這張圖,“5” 號(hào)和 “6” 號(hào) View 設(shè)置 layout_wrapBefore 屬性為ture 的時(shí)候,不管前面剩余多少空間,都會(huì)強(qiáng)制換行
到這里,flexboxLayout 基本屬性就介紹完畢了。
然后再來(lái)介紹一下跟 recycleView 結(jié)合使用。
4. 高能:與 RecyclewView 結(jié)合使用
Flexbox 能夠作為一個(gè) LayoutManager(FlexboxlayoutManager) 用在 RecyclerView 里面,這也就意味著你可以在一個(gè)有大量 Item 的可滾動(dòng)容器里面使用 Flexbox,提高性能。具體使用示例:
| 1 2 3 4 5 6 7 8 9 10 | //設(shè)置主軸方向?yàn)闄M軸 FlexboxLayoutManager manager = new FlexboxLayoutManager(this, FlexDirection.ROW); //設(shè)置item沿主軸方向的位置 manager.setJustifyContent(JustifyContent.CENTER); //設(shè)置item 沿次軸方向的位置 manager.setAlignItems(AlignItems.CENTER);recycleView.setLayoutManager(manager); centerGridAdapter = new CenterGridAdapter(items, this); recycleView.setAdapter(centerGridAdapter); |
可以看到跟 RecycleView 的其他 manager 使用一樣,只需設(shè)置 manager 屬性即可,屬性值為上面敘述的幾個(gè)容器的屬性。
如果想對(duì)某個(gè) item 進(jìn)行單獨(dú)的設(shè)置,可以在 adapter 中去設(shè)置,設(shè)置示例代碼為:
| 1 2 3 4 5 6 | ViewGroup.LayoutParams lp = holder.itemLL.getLayoutParams();if (lp instanceof FlexboxLayoutManager.LayoutParams) {FlexboxLayoutManager.LayoutParams flexboxLp =(FlexboxLayoutManager.LayoutParams) holder.itemLL.getLayoutParams();flexboxLp.setFlexGrow(1.0f);} |
我這里是設(shè)置每個(gè) item 有個(gè)權(quán)重(相當(dāng)于 Linearlayout 的 weight 屬性),所以會(huì)按比例分配 item 的寬,而不是我布局中設(shè)定的固定寬高。看下效果:
是不是有種鍵盤的感覺(jué)?并且我只是修改了極少的代碼就實(shí)現(xiàn)了這個(gè)功能。
小試牛刀1
最后,看了那么多,回到最開(kāi)始的問(wèn)題上,現(xiàn)在知道類似微信的那個(gè)中間擴(kuò)展的網(wǎng)格布局怎么寫的嗎?
首先我們簡(jiǎn)單分析一下,
- 主軸方向我們應(yīng)該設(shè)置為水平方向,即默認(rèn) flexDirection :“row”’
- 可以換行,即 flexWrap:“wrap”
- 子 View 在主軸方向的對(duì)其方式為居中(這一步實(shí)現(xiàn)從中間往兩邊展開(kāi)),即 justifyContent: “center”
- 子 View 在交叉軸方向的對(duì)其方式為居中,即 alignItems:”center”
- 子 View 寬高固定
也就是上面講 Recycleview 結(jié)合的例子中去掉單獨(dú)設(shè)置 item 的部分,并且 item 的寬高要根據(jù)屏幕來(lái)適配的。
嗯,就是 so easy。
小試牛刀2
實(shí)際應(yīng)用中還有種很常見(jiàn)的就是那種分類選擇的布局,像圖中的網(wǎng)易和簡(jiǎn)書中,這種布局用我們今天的這個(gè)主角是不是輕而易舉的就實(shí)現(xiàn)了?都不用設(shè)置特別的屬性,內(nèi)容超過(guò)一行自動(dòng)換行。
代碼如下:
| 1 2 3 4 5 6 7 8 9 10 | //設(shè)置主軸方向?yàn)闄M軸FlexboxLayoutManager manager = new FlexboxLayoutManager(this, FlexDirection.ROW);//設(shè)置item沿主軸方向的位置manager.setJustifyContent(JustifyContent.FLEX_START);//設(shè)置item 沿次軸方向的位置manager.setAlignItems(AlignItems.CENTER);recycleView.setLayoutManager(manager);labelAdapter = new LabelAdapter(labels,this);recycleView.setAdapter(labelAdapter); |
小試牛刀3
以項(xiàng)目需求為例,如圖所示:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ??
在第一張需求圖中,品牌篩選時(shí) 最后有一個(gè)”+“號(hào),想要的效果時(shí) 根據(jù)品牌選擇后,這個(gè)”+“號(hào)會(huì)緊挨著顯示,如果顯示不完,就換行顯示。
剛開(kāi)始考慮的是的在RecyclerView添加FooterView,FooterView為一個(gè)“+”的按鈕,這個(gè)按鈕會(huì)換一行顯示,不能緊挨著顯示,如果超過(guò)一行再換行顯示。
此時(shí)會(huì)遇到兩個(gè)問(wèn)題,
按照RecyclerView中的基本用法使用后 會(huì)報(bào)錯(cuò),日志如下:
ClassCastException: android.support.v7.widget.RecyclerView$LayoutParams cannot be cast to com.google.android.flexbox.FlexItem。
我們添加的FooterView,無(wú)法轉(zhuǎn)換成FlexlItem。網(wǎng)上找到解決方案,詳情見(jiàn)FlexboxLayoutManager 踩坑。
一是需要重寫FlexboxLayoutManager,代碼示例如下:
?
第二個(gè)問(wèn)題:顯示問(wèn)題。此時(shí)會(huì)出現(xiàn)我們的FooterView(”+“號(hào))換一行顯示,不能緊挨著顯示。
于是陷入思考,在上述代碼中,RecyclerView.LayoutParams的計(jì)算方法LayoutParams(lp)也需要重寫。
在查閱資料和源碼后發(fā)現(xiàn)比較麻煩,最后在網(wǎng)友的幫助下(FlexboxLayoutManager 踩坑),換一種思路,用RecyclerView多布局來(lái)實(shí)現(xiàn),豁然開(kāi)朗。
思路如下:在數(shù)據(jù)實(shí)體類中添加一個(gè)標(biāo)志,如是否是添加標(biāo)志isAdd,根據(jù)該值的是不同顯示不同的item布局。
class CameraVehicleBrandAdapter : MyBaseMultiItemAdapter<CameraVehicleBrandEntity>() {init {addItemType(CameraVehicleBrandEntity.ITEM_TYPE_NORMAL, R.layout.home_recycle_item_camera_vehicle_brand_selected)addItemType(CameraVehicleBrandEntity.ITEM_TYPE_ADD, R.layout.home_recycle_item_camera_vehicle_brand_add)}override fun convert(helper: BaseViewHolder, item: CameraVehicleBrandEntity) {super.convert(helper, item)if (item.isAdd) {helper.addOnClickListener(R.id.ivAddBrand)return}helper.setText(R.id.tvBrandName, item.brandName)helper.addOnClickListener(R.id.ivDelete)} }調(diào)用方法:
//品牌篩選 rvBrand.layoutManager = FlexboxLayoutManager(context) rvBrand.adapter = brandAdapter總結(jié)
所以說(shuō)了這么多,那么我們什么時(shí)候會(huì)用到這種布局呢?我目前想到的場(chǎng)景主要有 3 類:
當(dāng)然這些場(chǎng)景加上 RecycleView 就會(huì)更加暢享絲滑了。
demo地址:https://github.com/xiaxiayang/YX_FlexboxLayout
總結(jié)
以上是生活随笔為你收集整理的Android 巧用 flexboxLayout 布局的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 精品绿色便携软件 录制操作工具
- 下一篇: vue 商城浏览足迹_vue 移动端记录