官方 Flexbox Layout(弹性盒子布局) 的使用

介绍

这篇主要是关于 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种:

  1. FlexboxLayout(类似 LinearLayout )
  2. FlexboxLayoutManager (与RecyclerView 的结合)

现在,我们先看看FlexboxLayout使用,并详细介绍其属性。

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个问题:

  1. 高度是60dp,但默认被拉长,等于屏幕高度

  2. 宽度是60dp,即使设置更大的宽度,效果也一样。发现:如果总的宽度超过屏幕宽度,那么宽度设置没有效果,最终会平分!(即,不能换行!)

嗯,对于这2个问题,暂不针对性解决。目前,只是简单使用FlexboxLayout,并未使用其属性。接下来,介绍属性的作用,并带着这2个问题,找到解决方法。

属性的介绍与使用

在介绍之前,先说明2点:

首先,Flexbox 是以插件的方式引用,那么需要在根节点添加:
xmlns:app=“http://schemas.android.com/apk/res-auto
通过 app:Xxx来使用其属性,如:app:flexDirection=“column_reverse”

其次,属性可分为FlexboxLayout属性,还有其子元素属性。

先介绍 FlexboxLayout 属性

flexDirection

此属性决定了主轴的方向,类似于 LinearLayout 属性 orientation 设置布局方向,其值有:

    row (default)         		   主轴为水平方向,起点在左端
    row_reverse     			   主轴为水平方向,起点在右端。
    column         				   主轴为垂直方向,起点在上沿
    column_reverse  		       主轴为垂直方向,起点在下沿

效果如下:

flexWrap

设置Flex容器是单行还是多行,以及交叉轴的方向。其值有

	nowrap (default)  					不换行
	wrap              					按指定的方向换行  (与flexDirection 属性关联)
	wrap_reverse     				    按指定的反方向换行

效果如下:

加了这属性之后,布局就可以换行了,这个功能是最常用之一,换行的功能,是很强大,也很实用!
这里也可以看出,加了这个属性之后,前面的第2个问题解决了,现在设置宽度的大小就有效果了。

justifyContent

此属性控制元素主轴方向上的对齐方式,类似LinearLayout中 android:gravity属性, 有以下5种取值:

    flex_start (default)  左对齐
    flex_end              右对齐
    center                居中对齐
    space_between         先两端对齐,元素之间的间隔相等。
    space_around          每个元素左右的两侧的距离相等(那么2个元素之间的距离 = 元素1右边距 + 元素2左边距)。
    space_evenly          每个元素左右的间隔距离都相等

看效果图:

alignItems

此属性控制元素在副轴方向的对齐方式,其值有:

    stretch (default)   默认值,将占满整个容器的高度
    flex_start          副轴的起点对齐
    flex_end            副轴的终点对齐
    center              副轴的中点对齐
    baseline            根据每一行的第一个元素文字的基线对齐。

这里的 “副轴”,要根据flexDirection属性,是其相交的轴。

效果图:

官方关于这些属性值的介绍更直观,如图:

这里说明一下,设置值为 baseline 时,将视图1的TextView设为:android:gravity="bottom",这样效果才明显。

文本是有基线的,在文字的下方,如果想详细了解,可以参考我之前的一篇博客:
自定义View——Paint 之 文本绘制

加了属性之后,高度就可以被限制了,显示指定的高度。也可以看出,加了这个属性之后,第2个问题解决了,但是,还不是想要的结果,2行之间的间隔太宽了,下面这个属性,就能解决这个问题。

alignContent

此属性控制多根副轴线的对齐方式(也就是控制多行,如果子元素只有一行,则不起作用),其值有:

    stretch (default)     默认值, 充满交叉轴的高度
    flex_start            副轴的起点对齐
    flex_end              副轴的终点对齐
    center                副轴的中点对齐
    space_between         两端对齐,元素之间的间隔相等。
    space_around          每个元素左右的两侧的距离相等。

效果图如下:

alignContentjustifyContent 其实里面的属性值都是一样的 ,一个是设置主轴的对齐方式,一个是设置多个副轴的对齐方式,通俗的讲可以理解为比如是项目是水平换行,alignContent就是设置垂直方向的对齐方式, justifyContent就是设置水平方向的对齐方式。

目前的效果,布局的属性设置如下:

    app:flexWrap="wrap"       //能够换行,不然没有意义
    app:flexDirection="row"
    app:justifyContent="flex_start"
    app:alignItems="flex_start"
    app:alignContent="flex_start"

到了这里,将 alignContent 设置为flex_start ,并结合前面的一些属性,解决了前面说的2个问题,最终的效果才是我们想要的效果。如图:

好了,到了这里,我们大概知道,一般常用的属性就是:

  • flexWrap 设置是否换行
  • flexDirection 设置主轴方向
  • justifyContent 设置主轴的对齐方式
  • alignItems 设置元素在副轴的对齐方式
  • alignContent 设置多个副轴的对齐方式

设置分割线

showDividerHorizontal

设置水平分割线,其属性值可设置为:

	none                不设置分割线
	beginning       	起始位置设置分割线
	middle        		中间位置设置分割线
	end        			结束位置设置分割线

以上属相可以一个或者多个共同使用

dividerDrawableHorizontal

设置水平分割线的资源文件

showDividerVertical

设置垂直分割线

dividerDrawableVertical

设置垂直分割线的资源文件

showDivider

同时设置 水平/垂直 分割线

dividerDrawable

设置分割线的资源文件

如果设置了分割线,则建议不再设置:justifyContent="space_around" or alignContent="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子View的属性

前面说的多是FlexboxLayout 的属性,下面介绍其子元素的属性。

layout_order

此属性可以更改子视图的排序方式。默认情况下,子项的显示和布局顺序与布局XML中显示的顺序相同。

而order属性可以控制排列的顺序,负值在前,正值在后,按照从小到大的顺序依次排列。如果未指定,则将1设置为默认值。

操作: 现在分别将这6个子视图分别将app:layout_order 设置为:2、1、-1、0、-2、-5

那根据 layout_order 的大小顺序,那么对应的子视图排列是:视图6,视图5,视图3,视图4,视图2,视图1

效果图如下:

layout_flexGrow

此属性设置子元素的放大比例, 决定如何分配剩余空间(如果存在剩余空间的话),默认值为0,不会分配剩余空间。(类似于LinearLayout中的layout_weight属性)如果未指定,则将0设置为默认值。

操作: 将视图1、视图2、视图6的该属性分别设置为:2, 1, 1

效果图如下:

layout_flexShrink

此属性设置子元素缩小比例,当空间不足时,子元素需要缩小,默认值为1。

注意:如设置了换行则无效,只有在单行才有效果

操作: 先设置为单行,然后将视图1、视图2、视图6 的该属性分别设置为:2, 3, 1

效果图如下:

layout_alignSelf

这个属性决定了该子元素在 交叉轴(垂直于主轴)的对齐方式。在同方向对齐可以根据父的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"

效果图如下:

layout_flexBasisPercent (百分数)

初始flex元素长度相对于其父级长度的百分比。 如果设置了此值,则layout_width(或layout_height)指定的长度将被此属性中的计算值覆盖。但是需要注意,这个值只有父容器设置有效的长度时才有效果(MeasureSpec模式为MeasureSpec.EXACTLY)默认值为-1,表示未设置。

操作:
视图2设置为:app:layout_flexBasisPercent="80%"
视图5设置为:app:layout_flexBasisPercent="70%"

效果图如下:

layout_wrapBefore (boolean)

此属性控制强制换行,默认值为false。 即如果该属性设置为true,则该项目都将成为flexbox中的第一项。 即使flex_wrap属性设置为nowrap,也将忽略此属性。

操作: 视图3设置:app:layout_wrapBefore="true"

layout_minWidth / layout_minHeight

这些属性对FlexboxLayout的子项施加了最小大小约束。 无论layout_flexShrink属性如何,子视图的缩小都不会小于这些属性的值(根据flexDirection属性的不同而不同,哪个属性强制沿主轴施加大小约束)。

layout_maxWidth / layout_maxHeight

这些属性对FlexboxLayout的子项施加了最大大小限制。 无论layout_flexGrow属性如何,子视图的扩展都不会超过这些属性的值(根据flexDirection属性的不同而不同,哪个属性强制沿主轴施加大小约束)。

好,介绍很多关于FlexboxLayout的属性,多是基础,掌握基础,才能应万变。

下面,介绍FlexboxLayoutManage的使用,知道了属性的使用,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 的介绍就到这里,大家用起来吧,有问题欢迎交流!


参考:

  1. https://github.com/google/flexbox-layout
  2. https://www.cnblogs.com/huolongluo/p/6607877.html
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值