欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ConstraintLayout详解

程序员文章站 2022-05-07 08:05:36
...

简介

ConstraintLayout是谷歌2016年的I/O大会上推出的新控件,Constraint翻译过来就是“约束”的意思,这个控件跟RelativeLayout类似,其子控件通过互相约束来确定位置、大小,但是比RelativeLayout的功能强大很多。使用这个控件作为根布局,可以实现很复杂的布局情况,以前需要嵌套多层才能实现的布局,现在可能一层就够了。

谷歌的演示侧重引导开发者通过鼠标拖控件的方式来写布局,ConstraintLayout也是比较好的支持了鼠标拖动布局,也有不少相关博客主要以拖动讲解为主。但是还是有一些属性无法通过鼠标拖动来完成,比如设置visible、尺寸单位(dp、px)等,所以我想,与其学习“可视化操作布局+xml代码布局”,还不如直接学习“xml代码布局”。

要使用这个控件首先要在gralde中导入,如下:

dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
}

其次,在layout布局文件中要引入自定义属性的命名空间:xmlns:app=”http://schemas.android.com/apk/res-auto”,这样下面的控件才能使用其中的自定义属性:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"/>

</android.support.constraint.ConstraintLayout>

这篇博客分为四部分来讲解ConstraintLayout:
1、第一部分讲解ConstraintLayout中的子控件是如何确定位置的;
2、第二部分讲解子控件是如何确定自身尺寸的;
3、第三部分讲解chains链条特性,非常实用;
4、第四部分讲解一些其它属性;

控件位置

一、基本控制

主要有8个属性来设置控件的位置,跟RelativeLayout差不多:

// 左边向parent父控件的左边靠拢(注意,这里用的是“靠拢”,而不是“对齐”,后面就能体会到了)
app:layout_constraintLeft_toLeftOf="parent"

// 左边向btn2的右边靠拢。也就是再btn2的右边,相当于RelativeLayout的toRightOf
app:layout_constraintLeft_toRightOf="@+id/btn2"

// 下面的6个属性就不一一注释了,参照上面两个,一眼就能看懂
app:layout_constraintTop_toBottomOf=""
app:layout_constraintTop_toTopOf=""
app:layout_constraintRight_toLeftOf=""
app:layout_constraintRight_toRightOf=""
app:layout_constraintBottom_toBottomOf=""
app:layout_constraintBottom_toTopOf=""

在一个水平上设置一个约束是很清楚的,就跟RelativeLayout的toRightOf等属性效果一样,但是如果一个水平上设置两个约束呢?比如下面:

<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"/>

这就是为什么前面为什么要用“靠拢”这个词了,也可以理解这个控件为什么叫“约束控件”,这些属性都不是绝对的,只是一种约束,控件的最终位置(其他属性也一样)是通过各种约束条件来决定的,而不是像RelativieLayout的toRightOf那样一锤子买卖。

上面的意思就是左边尽量向parent的左边靠拢,右边尽量向parent的右边靠拢,最终就会取一个中间状态,让控件同时满足这两个约束条件,所以这个button最终是居中的。你可以脑补一下,一个力向左边拉,一个相等的力同时向右边拉,所以你最终在中间了。

二、偏向性控制

ConstraintLayout还有个很好用的属性app:layout_constraintHorizontal_bias=”0.1”,用来设置控件位置的偏向性,直接用代码说明:

<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintHorizontal_bias="0.2"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"/>

可以这么理解,btn既要向左边靠拢,有要向右边靠拢,然后就取了折中方案居中了。现在设置了layout_constraintHorizontal_bias约束,所以在满足前面两个约束的前提下,还要继续满足第三个约束。这个约束的意思是,在水平方向上,离左边的距离为0.2份,离右边的距离0.8份,也就是离左边更近一些。通过设置这个值,能轻松控制控件的相对于parent、其它控件的位置。

竖直方向的属性是app:layout_constraintVertical_bias=”0.3”,与上面同理,就不展开了。

尺寸设置

尺寸的设置主要有三种方式:通用的属性设置、约束条件设置、宽高比例设置

一、通用属性设置

1、设置具体值
与其它layout一样,设置android:layout_width=”100dp”就可以了。

2、由内容控制
与其它layout一样,设置android:layout_width=”wrap_content”。

二、约束条件设置

设置android:layout_width=”0dp”,意思就是由约束条件控制。举两个例子就明白了:

// 宽度设置为0dp,约束条件既要向parent左边靠拢,又要向parent右边靠拢,结果就是match_parent了
<Button
    android:id="@+id/btn1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"/>


// 宽度设置为0dp,约束条件既要向btn1的左边靠拢,又要向btn1的右边靠拢,结果就是与btn1宽度一样
<Button
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="@+id/btn1"
    app:layout_constraintLeft_toLeftOf="@+id/btn1"/>

注意:如果只是单纯地设置为0dp,而没有其它约束条件来约束尺寸,最终效果就是wrap_content,可以理解为,在没有其它约束条件时,wrap_content就成了唯一约束条件。

三、宽高比例设置

其实这个也属于“约束条件设置”,只是这里的约束条件变成了宽高比例。

ConstraintLayout有个app:layout_constraintDimensionRatio=”2:1”属性,用于控制宽高比例,前面的数字是宽,后面数字是高。

要使这个属性生效,需要要满足两个条件:
1、宽高属性中其中一个属性由约束条件设置,也就是设置为0dp,并且设置了Right_toRight和Left_toLeft属性;
2、另一个属性不能为0dp(从实际使用效果来看,最好设置为固定值,设置为wrap_content的效果很奇怪);

看下代码示例就理解了:

// 最终的效果就是宽是高的2倍。如果height设置为wrap_content,然后通过设置text属性设置内容,最终效果也确实是宽是高的2倍,但是text的内容会换行。。。效果不尽人意。
<Button
    android:id="@+id/btn2"
    android:layout_width="0dp"
    android:layout_height="100dp"
    app:layout_constraintDimensionRatio="2:1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"/>


Chains链条布局

链条就是一系列控件在同一水平上首位相连,形成一个整体。实现链条需要满足两个条件:
1、btn1设置靠拢btn2左边,同时btn2要设置靠拢btn1右边,两个控件都要设置属性;
2、最左边的btn要设置靠拢parent左边,最右边的btn要设置靠拢parent右边;

形成chains链条后,这个chains就是一个整体了,通过对head头(该链条上的第一个控件)设置app:layout_constraintHorizontal_chainStyle=”“属性,能够设置这个链条样式。

下面是官网上显示的各种样式链条的效果图:

ConstraintLayout详解

这5个样式分为三类,一类是直接通过style属性设置(样式1、2、4),一类是通过权重设置(样式3),最后一类是通过bias属性设置(样式5)。

一、style属性设置

通过对head头控件设置app:layout_constraintHorizontal_chainStyle属性,可以显示出上面样式1、2、4的效果:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        app:layout_constraintHorizontal_chainStyle="spread" // 对应样式1(默认样式)
        app:layout_constraintHorizontal_chainStyle="spread_inside" // 对应样式2
        app:layout_constraintHorizontal_chainStyle="packed" // 对应样式4
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent" // 最左边的链条靠拢parent左边
        app:layout_constraintRight_toLeftOf="@+id/btn2" />

// 可以看到,btn1靠拢btn2的左边,btn2靠拢btn1的右边,同样btn2和btn3也是如此
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintRight_toLeftOf="@+id/btn3" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintRight_toRightOf="parent" //最右边的链条靠拢parent右边
        app:layout_constraintLeft_toRightOf="@+id/btn2" />

</android.support.constraint.ConstraintLayout>


二、权重链

第3种样式是加权链,在链条样式为spread的情况下,通过设置btn的app:layout_constraintHorizontal_weight=”“属性来控制btn宽度,跟LinearLayout是一样的,也需要把宽度设置为0dp,代码如下:

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content" // 不需要由weight决定宽
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/btn2" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="0dp" // width设置为0,让weight决定宽度
        android:layout_height="wrap_content"
        app:layout_constraintHorizontal_weight="1" // 占剩下空间的1份
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintRight_toLeftOf="@+id/btn3" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="0dp"
        app:layout_constraintHorizontal_weight="1" //也占1份,所以剩下空间要与btn2平分
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn2"
        app:layout_constraintRight_toRightOf="parent"/>


三、bias链条

在链条样式是packed的情况下,通过设置head头控件的bias属性,来设置链条的位置(更偏向左或更偏向右)

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintHorizontal_bias="0.3" // 权重为靠左0.3
        app:layout_constraintHorizontal_chainStyle="packed" // 样式为packed
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/btn2" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintRight_toLeftOf="@+id/btn3" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn2"
        app:layout_constraintRight_toRightOf="parent" />


其它属性

一、app:layout_goneMarginLeft=”50dp”

这个属性的意思是,如果左边的控件是可见的,这个属性不生效;如果左边控件设置为gone,这个属性就会生效,离左边间隔50dp。

这个属性与android:layout_marginLeft=”“属性不冲突,可以同时生效。

二、android.support.constraint.Guideline控件

这是一条基准线,没有内容、不会显示、draw方法都是空实现,它的作用就是辅助我们进行控件定位。

android:orientation=”vertical”属性用来设置基准线的方向,垂直线或水平线;

app:layout_constraintGuide_percent=”0.7”属性或android:layout_margin=”“属性用来设置它的位置,percent=0.7就是离父控件左边/上边70%距离,margin=100dp就是离父控件左边/上边100dp

确定了它的方向和位置,我们就可以通过它来定位,设置其它控件的位置了

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.constraint.Guideline
        android:id="@+id/line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" // 设置方向和位置
        app:layout_constraintGuide_percent="0.7"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/line"/>

</android.support.constraint.ConstraintLayout>

大致的使用就是这些了,只用这个控件,很多布局都变得方便了,嵌套层级减少不是一点两点,视图加载效率也会高很多。