这篇主要是关于 flexbox-layout的介绍与使用
FlexboxLayout 是 2016 年 Google I/O 上开源的一个布局控件。它是一个库项目,它将CSS Flexible Box Layout Module的类似功能引入Android,使他具有非常超大的功能。
说起来惭愧,这个开源库出来已经很久了,但,是因为最近一个项目的小需求,才发现,真是相见恨晚!
需求很简单,就是实现如下效果:
之前,实现的方式,是自定义View,但,总是觉得功能不强大。
于是,就找到这个。网上关于这个的资源还挺多,但自己还是再整理学习一下吧。
官方文档与介绍:
https://github.com/google/flexbox-layout
首先,在build文件添加:
dependencies {
implementation 'com.google.android:flexbox:1.0.0'
}
在布局中使用方式有2种:
现在,我们先看看FlexboxLayout使用,并详细介绍其属性。
这一种是像LinearLayout 和 RelativeLayout那样,FlexboxLayout 都是继承了 ViewGroup。
先简单使用FlexboxLayout,看看效果如何。xml文件代码如下:
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".common.flexboxlayout.FlexboxLayoutActivity">
<TextView
android:layout_width="@dimen/flex_box_width"
android:layout_height="@dimen/flex_box_height"
android:background="@android:color/holo_red_light"
android:gravity="center"
android:text="1" />
<TextView
android:layout_width="@dimen/flex_box_width"
android:layout_height="@dimen/flex_box_height"
android:background="@android:color/holo_green_light"
android:gravity="center"
android:text="2" />
<TextView
android:layout_width="@dimen/flex_box_width"
android:layout_height="@dimen/flex_box_height"
android:background="@android:color/holo_blue_light"
android:gravity="center"
app:layout_wrapBefore="true"
<TextView
android:layout_width="@dimen/flex_box_width"
android:layout_height="@dimen/flex_box_height"
android:background="@color/colorAccent"
android:gravity="center"
android:text="4" />
<TextView
android:layout_width="@dimen/flex_box_width"
android:layout_height="@dimen/flex_box_height"
android:background="@android:color/holo_orange_light"
android:gravity="center"
android:text="5" />
<TextView
android:layout_width="@dimen/flex_box_width"
android:layout_height="@dimen/flex_box_height"
android:background="@android:color/holo_purple"
android:gravity="center"
android:text="6" />
</com.google.android.flexbox.FlexboxLayout>
分析:使用FlexboxLayout作为根节点,然后里面包含6个TextView,其中:flex_box_width 和flex_box_height多为60dp,每个TextView的背景多设置不同的颜色,更好区分。
效果图:
这图,乍一看,还挺漂亮! 但细看代码,好像和我们预想的效果不一样,有点不对,有2个问题:
高度是60dp,但默认被拉长,等于屏幕高度
宽度是60dp,即使设置更大的宽度,效果也一样。发现:如果总的宽度超过屏幕宽度,那么宽度设置没有效果,最终会平分!(即,不能换行!)
嗯,对于这2个问题,暂不针对性解决。目前,只是简单使用FlexboxLayout,并未使用其属性。接下来,介绍属性的作用,并带着这2个问题,找到解决方法。
在介绍之前,先说明2点:
首先,Flexbox 是以插件的方式引用,那么需要在根节点添加:
xmlns:app=“http://schemas.android.com/apk/res-auto”
通过app:Xxx
来使用其属性,如:app:flexDirection=“column_reverse”
其次,属性可分为FlexboxLayout属性,还有其子元素属性。
先介绍 FlexboxLayout 属性
此属性决定了主轴的方向,类似于 LinearLayout 属性 orientation 设置布局方向,其值有:
row (default) 主轴为水平方向,起点在左端
row_reverse 主轴为水平方向,起点在右端。
column 主轴为垂直方向,起点在上沿
column_reverse 主轴为垂直方向,起点在下沿
效果如下:
设置Flex容器是单行还是多行,以及交叉轴的方向。其值有
nowrap (default) 不换行
wrap 按指定的方向换行 (与flexDirection 属性关联)
wrap_reverse 按指定的反方向换行
效果如下:
加了这属性之后,布局就可以换行了,这个功能是最常用之一,换行的功能,是很强大,也很实用!
这里也可以看出,加了这个属性之后,前面的第2个问题解决了,现在设置宽度的大小就有效果了。
此属性控制元素主轴方向上的对齐方式,类似LinearLayout中 android:gravity
属性, 有以下5种取值:
flex_start (default) 左对齐
flex_end 右对齐
center 居中对齐
space_between 先两端对齐,元素之间的间隔相等。
space_around 每个元素左右的两侧的距离相等(那么2个元素之间的距离 = 元素1右边距 + 元素2左边距)。
space_evenly 每个元素左右的间隔距离都相等
看效果图:
此属性控制元素在副轴方向的对齐方式,其值有:
stretch (default) 默认值,将占满整个容器的高度
flex_start 副轴的起点对齐
flex_end 副轴的终点对齐
center 副轴的中点对齐
baseline 根据每一行的第一个元素文字的基线对齐。
这里的 “副轴”,要根据flexDirection属性,是其相交的轴。
效果图:
官方关于这些属性值的介绍更直观,如图:
这里说明一下,设置值为
baseline
时,将视图1的TextView设为:android:gravity="bottom"
,这样效果才明显。文本是有基线的,在文字的下方,如果想详细了解,可以参考我之前的一篇博客:
自定义View——Paint 之 文本绘制
加了属性之后,高度就可以被限制了,显示指定的高度。也可以看出,加了这个属性之后,第2个问题解决了,但是,还不是想要的结果,2行之间的间隔太宽了,下面这个属性,就能解决这个问题。
此属性控制多根副轴线的对齐方式(也就是控制多行,如果子元素只有一行,则不起作用),其值有:
stretch (default) 默认值, 充满交叉轴的高度
flex_start 副轴的起点对齐
flex_end 副轴的终点对齐
center 副轴的中点对齐
space_between 两端对齐,元素之间的间隔相等。
space_around 每个元素左右的两侧的距离相等。
效果图如下:
alignContent
和 justifyContent
其实里面的属性值都是一样的 ,一个是设置主轴的对齐方式,一个是设置多个副轴的对齐方式,通俗的讲可以理解为比如是项目是水平换行,alignContent
就是设置垂直方向的对齐方式, justifyContent
就是设置水平方向的对齐方式。
目前的效果,布局的属性设置如下:
app:flexWrap="wrap" //能够换行,不然没有意义
app:flexDirection="row"
app:justifyContent="flex_start"
app:alignItems="flex_start"
app:alignContent="flex_start"
到了这里,将 alignContent 设置为flex_start ,并结合前面的一些属性,解决了前面说的2个问题,最终的效果才是我们想要的效果。如图:
好了,到了这里,我们大概知道,一般常用的属性就是:
设置水平分割线,其属性值可设置为:
none 不设置分割线
beginning 起始位置设置分割线
middle 中间位置设置分割线
end 结束位置设置分割线
以上属相可以一个或者多个共同使用
设置水平分割线的资源文件
设置垂直分割线
设置垂直分割线的资源文件
同时设置 水平/垂直 分割线
设置分割线的资源文件
如果设置了分割线,则建议不再设置:
justifyContent="space_around"
oralignContent="space_between"
等属性,避免元素之间有空白显示
代码如下:
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:flexWrap="wrap"
app:flexDirection="row"
app:justifyContent="flex_start"
app:alignItems="flex_start"
app:alignContent="flex_start"
app:dividerDrawable="@drawable/divider_bg2"
app:showDivider="beginning|middle">
效果图如下:
前面说的多是FlexboxLayout 的属性,下面介绍其子元素的属性。
此属性可以更改子视图的排序方式。默认情况下,子项的显示和布局顺序与布局XML中显示的顺序相同。
而order属性可以控制排列的顺序,负值在前,正值在后,按照从小到大的顺序依次排列。如果未指定,则将1设置为默认值。
操作: 现在分别将这6个子视图分别将app:layout_order
设置为:2、1、-1、0、-2、-5
那根据 layout_order 的大小顺序,那么对应的子视图排列是:视图6,视图5,视图3,视图4,视图2,视图1
效果图如下:
此属性设置子元素的放大比例, 决定如何分配剩余空间(如果存在剩余空间的话),默认值为0,不会分配剩余空间。(类似于LinearLayout中的layout_weight属性)如果未指定,则将0设置为默认值。
操作: 将视图1、视图2、视图6的该属性分别设置为:2, 1, 1
效果图如下:
此属性设置子元素缩小比例,当空间不足时,子元素需要缩小,默认值为1。
注意:如设置了换行则无效,只有在单行才有效果
操作: 先设置为单行,然后将视图1、视图2、视图6 的该属性分别设置为:2, 3, 1
效果图如下:
这个属性决定了该子元素在 交叉轴(垂直于主轴)的对齐方式。在同方向对齐可以根据父的alignItems决定,但是如果这个属性设置了其他的属性除了auto,交叉轴的对齐将会被子view的该属性重写,可能的值:
auto (default) 默认值为auto, 表示继承alignItems属性
flex_start 起点对齐
flex_end 终点对齐
center 中点对齐
baseline 根据每一行的第一个元素文字的基线对齐。
stretch 充满交叉轴的高度
该属性可能取6个值,除了auto,其他都与align_items属性完全一致。
注意:部分属性值,在可以换行的情况下,是没有效果的。如flex_start,flex_end,这里设置为不换行
app:flexWrap="nowrap"
操作:
视图1设置为 android:gravity="bottom"
视图2设置 app:layout_alignSelf="center"
视图4 设置app:layout_alignSelf="baseline"
效果图如下:
初始flex元素长度相对于其父级长度的百分比。 如果设置了此值,则layout_width(或layout_height)指定的长度将被此属性中的计算值覆盖。但是需要注意,这个值只有父容器设置有效的长度时才有效果(MeasureSpec模式为MeasureSpec.EXACTLY)默认值为-1,表示未设置。
操作:
视图2设置为:app:layout_flexBasisPercent="80%"
视图5设置为:app:layout_flexBasisPercent="70%"
效果图如下:
此属性控制强制换行,默认值为false。 即如果该属性设置为true,则该项目都将成为flexbox中的第一项。 即使flex_wrap属性设置为nowrap,也将忽略此属性。
操作: 视图3设置:app:layout_wrapBefore="true"
这些属性对FlexboxLayout的子项施加了最小大小约束。 无论layout_flexShrink属性如何,子视图的缩小都不会小于这些属性的值(根据flexDirection属性的不同而不同,哪个属性强制沿主轴施加大小约束)。
这些属性对FlexboxLayout的子项施加了最大大小限制。 无论layout_flexGrow属性如何,子视图的扩展都不会超过这些属性的值(根据flexDirection属性的不同而不同,哪个属性强制沿主轴施加大小约束)。
好,介绍很多关于FlexboxLayout的属性,多是基础,掌握基础,才能应万变。
下面,介绍FlexboxLayoutManage的使用,知道了属性的使用,FlexboxLayoutManage就很简单了。
在开发中,使用最多的还是这种方式:将FlexboxLayoutManager 与 RecyclerView 结合使用。
下面,用一个小demo,来介绍。
布局代码,和平时一样,在RelativeLayout 根布局中添加一个RecyclerView即可,如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".common.flexboxlayout.FlexboxLayoutActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:background="@color/whiel"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
Activity代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flexbox_layout);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
List<String> datas = new ArrayList<>();
datas.add("十年之前 我不认识你 你不属于我");
datas.add("今日推荐");
datas.add("全民行动");
datas.add("2018最好看的电影");
datas.add("如何实现流式动态布局");
datas.add("故事");
datas.add("无处安放的灵魂");
datas.add("流行歌曲");
datas.add("流行歌曲");
datas.add("十年之后 我们是朋友 还可以问候");
FlexboxAdapter adapter = new FlexboxAdapter(getApplicationContext(), datas);
FlexboxLayoutManager manager = new FlexboxLayoutManager(this);
//设置主轴排列方式
manager.setFlexDirection(FlexDirection.ROW);
//设置是否换行
manager.setFlexWrap(FlexWrap.WRAP);
recyclerView.setLayoutManager(manager);
recyclerView.setAdapter(adapter);
}
平时,我们一般使用的是 LinearLayoutManager
,这里使用FlexboxLayoutManager
,并用代码的方式设置其属性。
FlexboxAdapter 代码如下:
public class FlexboxAdapter extends RecyclerView.Adapter<FlexboxAdapter.ViewHolder> {
private Context mContext;
private List<String> mDatas;
public FlexboxAdapter(Context context, List<String> datas) {
this.mContext = context;
this.mDatas = datas;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_name1,parent,false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.tvTitle.setText(mDatas.get(position));
}
@Override
public int getItemCount() {
return mDatas.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
private TextView tvTitle;
public ViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.name_tv);
/**
* 如果想对子元素单独设置,那么就可以在这里设置了
*/
ViewGroup.LayoutParams lp = tvTitle.getLayoutParams();
if (lp instanceof FlexboxLayoutManager.LayoutParams) {
FlexboxLayoutManager.LayoutParams flexboxLp = (FlexboxLayoutManager.LayoutParams) tvTitle.getLayoutParams();
flexboxLp.setFlexGrow(1.0f);
}
}
}
}
关于子元素的属性设置,就可以在Adapter这里设置了。
最终效果图:
灰常~ 灰常~ 简单!和平时使用RecyclerView 几乎一样。FlexboxLayoutManage 的使用就完了。
关于FlexboxLayout 和FlexboxLayoutManager 的区别如下(官方图):
*1 部分可能用ScrollView来包装,但是它不太可能用作与一个大集合布局内部的视图,因为它不考虑到视图的回收
Android的Flexbox ,尽可能得实现了CSS中Flexible Box规范的相同功能,但对于android,使用的方式(其属性)不同,其实本质是一样的。
总体来说,FlexboxLayout 和 FlexboxLayoutManager 的使用比较简单,主要是理解其属性的意思。比较麻烦的就是,其属性名,真的不好记。不过属性不多,记住常用的就可以了。
好了,FlexboxLayout 的介绍就到这里,大家用起来吧,有问题欢迎交流!
参考: