FlexboxLayout是Google开源的一个强大的控件,直接继承ViewGroup,效果类似于加强版的LinearLayout,但与LinearLayout并无关联。
官方原话是:
FlexboxLayout is a library project which brings the similar capabilities of CSS Flexible Box Layout Module to Android.
意思是:FlexBoxLayout是为Android带来了与 CSS Flexible Box Layout(CSS 弹性盒子)相似功能的库。
Github地址:https://github.com/google/flexbox-layout
FlexBoxLayout有非常多的属性,下面一一验证各个属性的作用
dependencies {
implementation 'com.google.android:flexbox:1.1.0'
}
如果还没用上AndroidX:
dependencies {
implementation 'com.google.android:flexbox:1.0.0'
}
TextStyle如下:
shape_pink_border文件:
运行看一下效果:
可以看到内容虽然出来了,但都挤在同一行了,这显然并不是我要的效果。
那么接下来就该FlexboxLayout属性们出来救场了:
简单来说,该属性表示是否换行和换行的方向。
由上图可见,flexWrap属性一共有三个枚举值,分别是nowrap
、wrap
和wrap_reverse
nowrap
:单行显示,不换行,默认就是这个属性。wrap
:当内容超过一行时,自动换行。效果:wrap_reverse
:反向换行(下一行内容在当前行之上)该属性表示主轴的方向,子元素的排列按照轴线方向依次添加。
再看一下源码,可见flexDirection的枚举值有row
和row_reverse
,column
和column_reverse
row
: 主轴方向按水平(行)方向排版,默认就是这个属性。效果:
row_reverse
: 主轴方向按水平(行)方向反向排版。效果:
由效果图可见,row
是从左向右绘制,row_reverse
是row的反向,也就是从右向左绘制啦。同理column
也是一样。
column
: 主轴方向按竖直(列)方向排版。效果:
column_reverse
: 主轴方向按竖直(列)方向反向排版(从下向上)。
关于这个属性,官方有一段说明:
作用是控制元素在主轴上的对齐方式,需要配合flexDirection
或flexWrap
属性来使用。
看一下源码,可见app:justifyContent
属性有flex_start
、flex_end
、center
、space_between
、space_around
和space_evenly
6个枚举值。
下面把以上两个属性改为:
app:flexWrap="wrap"
app:flexDirection="row"
来看效果。
flex_start
: 左对齐,默认值。
flex_end
: 右对齐。
center
:居中对齐。
space_between
: 两端对齐。
space_around
: 分散对齐。
space_evenly
:子元素在一行内均匀分布空间。
该属性表示元素在每条轴线
上的对齐方式。
注意:如果设置了
alignContent
,且值不为stretch,那么该属性失效。
app:alignItems
属性有flex_start
、flex_end
、center
、baseline
和stretch
下面把FlexBoxLayout指定一个高度,并为TextView添加不同的padding来看效果。
stretch
: 默认值,如果子元素未设置高度,则沾满父布局高度。flex_start
:顶端对齐。flex_end
:底端对齐。center
: 居中对齐。baseline
:按照第一个元素的基线对齐。下面这张图片可以很直观的表达这个属性的作用。图片来源
该属性表示每条轴线
在整个布局中的对齐方式。
app:alignContent属性有flex_start
、flex_end
、center
、space_between
、space_around
和stretch
6个枚举值。
stretch
:默认值,轴线占满整个父布局。
flex_start
:顶部对齐所有轴线。
flex_end
:底部对齐所有轴线。
center
:居中对齐所有轴线。
space_between
:两端对齐所有轴线。
space_around
:分散对齐所有轴线。
插一句嘴,轴线方向可以由flexDirection
属性来指定,注意属性搭配,举一反三。
水平和竖直方向分割线。
水平/竖直方向分割线。
显示水平和竖直方向分割线方式。
枚举值有:
none
不显示。
beginning
middle
end
显示水平/竖直方向分割线方式。
指定子元素排序优先级,值越小越排在前面,默认值为1。设置值类型为float。
如图,蓝牙TextView元素在xml中排序为第二个,但给它的layout_order
属性指定为2,蓝牙排名到最后一位了。
分配同一轴线剩余控件所占权重,默认值为0,表示不参与分配。用法类似于LinearLayout的weight
,不过weight分配的是整个父布局控件,而layout_flexGrow
分配的是同一行/列的剩余空间。
举个例子:将蓝牙的layout_flexGrow
设为1,程序的layout_flexGrow
属性设为2
如图,蓝牙与程序都参与到了剩余空间分配,由于程序设置的权重为2,所以比蓝牙多分配了一倍的空间。
子元素缩放比例,如果设置了换行(flexWrap=“wrap或wrap_reverse”)则该属性无效。
设置值类型为float,0表示不缩放,数值越大,缩放比例越大,默认为1,负值无效。
例子:推送和即时通讯设置为0(不缩放),旅行——在路上设置为2(双倍缩放)
官方解释:
表示子元素长度是其父布局长度的百分比,设置了元素的layout_flexBasisPercent
将覆盖子元素原本长度,默认值为-1。要注意的是,只有具体设置了父布局的长度才能生效。
设置的值为百分数,例如50%。
例子:将第一个元素宽度设置为父布局的50%(app:layout_flexBasisPercent="50%"
)。
所用与alignItems
属性一样,不同点是alignItems
设置的是所有元素,而layout_alignSelf
作用于单个元素。
需要注意的一点是,若父布局设置了alignContent
,并且枚举值不为stretch
,则改属性失效。
layout_alignSelf
的枚举值有auto
,flex_start
,flex_end
,center
,baseline
和stretch
,作用与alignItems
属性相同。
例子:将推送和即时通讯设置为flex_start,蓝牙的layout_alignSelf设置为stretch。效果:
强制换行,默认为false。
若为子元素设置了layout_wrapBefore
属性为false,那么这个子元素将重新另起一行。
例:为蓝牙设置layout_wrapBefore
属性为true
限制 FlexboxLayout的子元素(宽或高)不会小于最小值,无论layout_flexShrink
属性为多少,子元素不会被缩小到小于设置的这个最小值。
限制 FlexboxLayout的子元素(宽或高)不会大于最小值,无论layout_flexGrow
属性为多少,子元素不会被放大到大于于设置的这个最小值。
FlexBoxLayout的常用属性就这么多了,灵活运用这些属性的搭配可以达到非常灵活的效果。
官方不仅提供了FlexboxLayout
布局,还提供了FlexboxLayoutManager
来与RecyclerView
配合使用。
The second one is FlexboxLayoutManager that can be used within RecyclerView.
虽然RecyclerView之前也有StaggeredGridLayoutManager
来提供瀑布流效果,但还是要指定行/列数、相比之下,FlexboxLayoutManager
更加灵活,且应用场景也不尽相同。
下面将RecyclerView的layoutManager设置成FlexboxLayoutManager
先看一下效果:
白色背景为设置了FlexboxLayoutManager的RecyclerView,绿色背景为FlexboxLayout,可见FlexboxLayoutManager
y与FlexboxLayout
可以达到相同的效果。
主要代码:
val flexboxLayoutManager = FlexboxLayoutManager(this)
flexboxLayoutManager.flexWrap = FlexWrap.WRAP
flexboxLayoutManager.flexDirection = FlexDirection.ROW
flexboxLayoutManager.justifyContent = JustifyContent.FLEX_START
flexboxLayoutManager.alignItems = AlignItems.FLEX_START
//flexboxLayoutManager.alignContent = AlignContent.FLEX_START
val adapter = FlexAdapter()
initData(adapter)
rv_flex.layoutManager = flexboxLayoutManager
rv_flex.adapter = adapter
注意:flexboxLayoutManager.alignContent,
FlexboxLayoutManager
不支持alignContent 属性
看一下setAlignContent的源码:
@Override
public void setAlignContent(@AlignContent int alignContent) {
throw new UnsupportedOperationException("Setting the alignContent in the "
+ "FlexboxLayoutManager is not supported. Use FlexboxLayout "
+ "if you need to use this attribute.");
}
可见,FlexboxLayoutManager
不支持alignContent 属性,如果强行设置则会报出以下异常。
Caused by: java.lang.UnsupportedOperationException: Setting the alignContent in the FlexboxLayoutManager is not supported. Use FlexboxLayout if you need to use this attribute.
Demo源码
那么FlexboxLayoutManager
和FlexboxLayout
还有哪些使用的不同呢?官方这张表给出了很好地说明。
Attribute / Feature | FlexboxLayout | FlexboxLayoutManager (RecyclerView) |
---|---|---|
flexDirection | ||
flexWrap | (except wrap_reverse ) |
|
justifyContent | ||
alignItems | ||
alignContent | - | |
layout_order | - | |
layout_flexGrow | ||
layout_flexShrink | ||
layout_alignSelf | ||
layout_flexBasisPercent | ||
layout_(min/max)Width | ||
layout_(min/max)Height | ||
layout_wrapBefore | ||
Divider | ||
View recycling | - | |
Scrolling | *1 |
这样的流失布局,如果在项目中碰到,貌似只能自定义View,或者寻求别人写好的库了。
但有了FlexboxLayout之后可以完美解决。
空谈误国,实干兴邦
要真想理解FlexboxLayout的各个属性,自己实践和阅读源码是必不可少的,话不多说,还是多多动手实践吧。
FlexboxLayout与FlexboxLayoutManager可以完美实现灵活的流式布局,并且该库已经添加到androidX了,Google大大为我们的开发真是操碎了心啊。
https://blog.csdn.net/tabolt/article/details/51799226
https://www.jianshu.com/p/3c471953e36d
https://www.oschina.net/news/73442/google-flexbox-layout