Android:神奇的约束布局 ConstraintLayout

目录

1.介绍
2.为什么要用ConstraintLayout
3.如何使用ConstraintLayout
3.1 添加依赖
3.2 相对定位
3.3 角度定位
3.4 边距
3.5 居中和偏移
3.6 尺寸约束
3.7 链
4.辅助工具
4.1 Optimizer
4.2 Barrier
4.3 Group
4.4 Placeholder
4.5.Guideline
5.总结

1.介绍

约束布局 ConstraintLayout 是一个 ViewGroup,可以在 Api9 以上的 Android 系统使用它,它的出现主要是为了解决布局嵌套过多的问题,以灵活的方式定位和调整小部件。从 Android Studio 2.3 起,官方的模板默认使用 ConstraintLayout。

ConstraintLayout 官方文档

2.为什么要用 ConstraintLayout

在开发过程中经常能遇到一些复杂的 UI,可能会出现布局嵌套过多的问题,嵌套得越多,设备绘制视图所需的时间和计算功耗也就越多。简单举个例子:
在这里插入图片描述
假设现在要写一个这样的布局,可能有人会这么写:
首先是一个垂直的 LinearLayout,里面放两个水平的 LinearLayout,然后在水平的 LinearLayout 里面放 TextView。这样的写法就嵌套了两层 LinearLayout。
在这里插入图片描述
有些人考虑到了嵌套布局带来的风险,所以用一个 RelativeLayout 来装下所有的控件。那么问题来了,既然用 RelativeLayout 可以解决问题,为什么还要使用 ConstraintLayout 呢?因为 ConstraintLayout 使用起来比 RelativeLayout 更灵活,性能更出色!还有一点就是 ConstraintLayout 可以按照比例约束控件位置和尺寸,能够更好地适配屏幕大小不同的机型。

3.如何使用ConstraintLayout

3.1 添加依赖

首先我们需要在 app/build.gradle 文件中添加 ConstraintLayout 的依赖,如下所示。

 implementation 'com.android.support.constraint:constraint-layout:1.1.3'
3.2 相对定位

相对定位是部件对于另一个位置的约束,这么说可能有点抽象,举个例子:
在这里插入图片描述
如图所示,TextView2 在 TextView1 的右边,TextView3 在 TextView1 的下面,这个时候在布局文件里面应该这样写:

    <TextView
        android:id="@+id/TextView1"
        ...
        android:text="TextView1" />

    <TextView
        android:id="@+id/TextView2"
        ...
        app:layout_constraintLeft_toRightOf="@+id/TextView1" />

    <TextView
        android:id="@+id/TextView3"
        ...
        app:layout_constraintTop_toBottomOf="@+id/TextView1" />

上面代码中在 TextView2 里用到了 app:layout_constraintLeft_toRightOf="@+id/TextView1" 这个属性,他的意思是把 TextView2 的左边约束到 TextView1 的右边,如下图所示:
在这里插入图片描述
同理 TextView3 在 TextView1 的下面,就需要用到 app:layout_constraintTop_toBottomOf="@+id/TextView1",即把 TextView3 的上面约束到 TextView1 的下面。

下面来看看相对定位的常用属性:

layout_constraintLeft_toLeftOf	
layout_constraintLeft_toRightOf	
layout_constraintRight_toLeftOf	
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf	
layout_constraintTop_toBottomOf	
layout_constraintBottom_toTopOf	
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf		
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf

上面属性中有一个比较有趣的 layout_constraintBaseline_toBaselineOf
Baseline 指的是文本基线,举个例子:
在这里插入图片描述
如图所示,两个 TextView 的高度不一致,但是又希望他们文本对齐,这个时候就可以使用 layout_constraintBaseline_toBaselineOf,代码如下:

    <TextView
        android:id="@+id/TextView1"
        .../>

    <TextView
        android:id="@+id/TextView2"
        ...
        app:layout_constraintLeft_toRightOf="@+id/TextView1" 
        app:layout_constraintBaseline_toBaselineOf="@+id/TextView1"/>

效果如下:
在这里插入图片描述
ConstraintLayout 相对定位的用法跟 RelativeLayout 还是比较相似的,下面用一个图来总结相对定位:
在这里插入图片描述

3.3 角度定位

角度定位指的是可以用一个角度和一个距离来约束两个空间的中心。举个例子:

    <TextView
        android:id="@+id/TextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/TextView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintCircle="@+id/TextView1"
        app:layout_constraintCircleAngle="120"
        app:layout_constraintCircleRadius="150dp" />

上面例子中的 TextView2 用到了3个属性:
app:layout_constraintCircle="@+id/TextView1"
app:layout_constraintCircleAngle="120"(角度)
app:layout_constraintCircleRadius="150dp"(距离)

指的是 TextView2 的中心在 TextView1 的中心的120度,距离为150dp,效果如下:
神奇的效果

3.4 边距
3.4.1 常用 margin

ConstraintLayout的边距常用属性如下:

android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom

看起来跟别的布局没有什么差别,但实际上控件在 ConstraintLayout 里面要实现 margin,必须先约束该控件在 ConstraintLayout 里的位置,举个例子:

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/TextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp" />
</android.support.constraint.ConstraintLayout>

如果在别的布局里,TextView1 的位置应该是距离边框的左边和上面有一个 10dp 的边距,但是在 ConstraintLayout 里,是不生效的,因为没有约束 TextView1 在布局里 (frame) 的位置。正确的写法如下:

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/TextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp" 
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

把 TextView1 的左边和上边约束到 parent 的左边和上边,这样 margin 就会生效,效果如下:
在这里插入图片描述
在使用 margin 的时候要注意两点:

  1. 控件必须在布局里约束一个相对位置。
  2. margin 只能大于等于 0。
3.4.2 goneMargin

goneMargin 主要用于约束的控件可见性被设置为 gone 的时候使用的 margin 值,属性如下:

layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom

举个例子:
假设 TextView2 的左边约束在 TextView1 的右边,并给 TextView2 设一个 app:layout_goneMarginLeft="10dp",代码如下:

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/TextView1"
        .../>

    <TextView
        android:id="@+id/TextView2"
        ...
        app:layout_constraintLeft_toRightOf="@+id/TextView1"
        app:layout_goneMarginLeft="10dp"
        />

</android.support.constraint.ConstraintLayout>

效果如下,TextView2在TextView1的右边,且没有边距。
在这里插入图片描述
这个时候把 TextView1 的可见性设为 gone,效果如下:
在这里插入图片描述
TextView1 消失后,TextView2 有一个距离左边 10dp 的边距。

3.5 居中和偏移

在 RelativeLayout 中,把控件放在布局中间的方法是把 layout_centerInParent 设为 true,而在 ConstraintLayout 中的写法是:

app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"

意思是把控件的上下左右约束在布局的上下左右,这样就能把控件放在布局的中间了。同理 RelativeLayout 中的水平居中 layout_centerHorizontal 相当于在 ConstraintLayout 约束控件的左右为 parent 的左右;RelativeLayout 中的垂直居中 layout_centerVertical 相当于在 ConstraintLayout 约束控件的上下为 parent 的上下。
由于 ConstraintLayout 中的居中已经为控件约束了一个相对位置,所以可以使用 margin,如下所示:

<TextView
        android:id="@+id/TextView1"
        ...
        android:layout_marginLeft="100dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

效果如下:
在这里插入图片描述
上面 TextView1 在水平居中后使用 layout_marginLeft="100dp" 向右偏移了 100dp。除了这种偏移外, ConstraintLayout 还提供了另外一种偏移的属性(bias):

  1. layout_constraintHorizontal_bias 水平偏移
  2. layout_constraintVertical_bias 垂直偏移

举个例子:

<TextView
        android:id="@+id/TextView1"
        ...
        app:layout_constraintHorizontal_bias="0.3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

效果如下:
在这里插入图片描述
假如现在要实现水平偏移,给 TextView1 的 layout_constraintHorizontal_bias 赋一个范围为 0-1 的值,假如赋值为 0,则 TextView1 在布局的最左侧,假如赋值为1,则TextView1在布局的最右侧,假如赋值为 0.5,则水平居中,假如赋值为 0.3,则更倾向于左侧。
垂直偏移同理。

3.6 尺寸约束

控件的尺寸可以通过四种不同方式指定:

  • 使用指定的尺寸
    使用 wrap_content,让控件自己计算大小
    当控件的高度或宽度为 wrap_content 时,可以使用下列属性来控制最大、最小的高度或宽度:
    android:minWidth 最小的宽度
    android:minHeight 最小的高度
    android:maxWidth 最大的宽度
    android:maxHeight 最大的高度
    

**注意:**当 ConstraintLayout 为 1.1 版本以下时,使用这些属性需要加上强制约束,如下所示:

app:constrainedWidth=”true”
app:constrainedHeight=”true”

使用 0dp (MATCH_CONSTRAINT)
官方不推荐在 ConstraintLayout 中使用 match_parent,可以设置 0dp (MATCH_CONSTRAINT) 配合约束代替 match_parent,举个例子:

<TextView
        android:id="@+id/TextView1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:visibility="visible" />

宽度设为 0dp,左右两边约束 parent 的左右两边,并设置左边边距为 50dp,效果如下:
在这里插入图片描述

  • 宽高比
    当宽或高至少有一个尺寸被设置为0dp时,可以通过属性 layout_constraintDimensionRatio 设置宽高比,举个例子:
<TextView
        android:id="@+id/TextView1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

宽设置为 0dp,宽高比设置为 1:1,这个时候 TextView1 是一个正方形,效果如下:
在这里插入图片描述
除此之外,在设置宽高比的值的时候,还可以在前面加 W 或 H,分别指定宽度或高度限制。 例如:

app:layout_constraintDimensionRatio="H,2:3"指的是 高:宽=2:3`
app:layout_constraintDimensionRatio="W,2:3"指的是 宽:高=2:3`
3.7 链

如果两个或以上控件通过下图的方式约束在一起,就可以认为是他们是一条链(图为横向的链,纵向同理)。
在这里插入图片描述
用代码表示:

    <TextView
        android:id="@+id/TextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/TextView2" />

    <TextView
        android:id="@+id/TextView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/TextView1"
        app:layout_constraintRight_toLeftOf="@+id/TextView3"
        app:layout_constraintRight_toRightOf="parent" />

    <TextView
        android:id="@+id/TextView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/TextView2"
        app:layout_constraintRight_toRightOf="parent" />

3个 TextView 相互约束,两端两个 TextView 分别与 parent 约束,成为一条链,效果如下:
在这里插入图片描述
一条链的第一个控件是这条链的链头,我们可以在链头中设置 layout_constraintHorizontal_chainStyle 来改变整条链的样式。chains 提供了3种样式,分别是:

CHAIN_SPREAD —— 展开元素 (默认);
CHAIN_SPREAD_INSIDE —— 展开元素,但链的两端贴近parent;
CHAIN_PACKED —— 链的元素将被打包在一起。

如图所示:
在这里插入图片描述
上面的例子创建了一个样式链,除了样式链外,还可以创建一个权重链。
可以留意到上面所用到的3个 TextView 宽度都为 wrap_content,如果我们把宽度都设为 0dp,这个时候可以在每个 TextView 中设置横向权重 layout_constraintHorizontal_weight(constraintVertical 为纵向)来创建一个权重链,如下所示:

 <TextView
        android:id="@+id/TextView1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/TextView2"
        app:layout_constraintHorizontal_weight="2" />

    <TextView
        android:id="@+id/TextView2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/TextView1"
        app:layout_constraintRight_toLeftOf="@+id/TextView3"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_weight="3" />

    <TextView
        android:id="@+id/TextView3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/TextView2"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_weight="4" />

效果如下:
在这里插入图片描述

4.辅助工具

4.1 Optimizer

当我们使用 MATCH_CONSTRAINT 时,ConstraintLayout 将对控件进行 2 次测量,ConstraintLayout 在 1.1 中可以通过设置 layout_optimizationLevel 进行优化,可设置的值有:

none:无优化
standard:仅优化直接约束和屏障约束(默认)
direct:优化直接约束
barrier:优化屏障约束
chain:优化链约束
dimensions:优化尺寸测量
4.2 Barrier

假设有 3 个控件 ABC,C 在 AB 的右边,但是 AB 的宽是不固定的,这个时候 C 无论约束在 A 的右边或者 B 的右边都不对。当出现这种情况可以用 Barrier 来解决。Barrier 可以在多个控件的一侧建立一个屏障,如下所示:

这个时候 C 只要约束在 Barrier 的右边就可以了,代码如下:

    <TextView
        android:id="@+id/TextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/TextView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/TextView1" />

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="right"
        app:constraint_referenced_ids="TextView1,TextView2" />

    <TextView
        android:id="@+id/TextView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/barrier" />

app:barrierDirection 为屏障所在的位置,可设置的值有:bottom、end、left、right、start、top
app:constraint_referenced_ids 为屏障引用的控件,可设置多个(用“,”隔开)

4.3 Group

Group 可以把多个控件归为一组,方便隐藏或显示一组控件,举个例子:

    <TextView
        android:id="@+id/TextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/TextView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/TextView1" />

    <TextView
        android:id="@+id/TextView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@id/TextView2" />

现在有 3 个并排的 TextView,用 Group 把 TextView1 和 TextView3 归为一组,再设置这组控件的可见性,如下所示:

   <android.support.constraint.Group
        android:id="@+id/group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        app:constraint_referenced_ids="TextView1,TextView3" />

效果如下:
在这里插入图片描述

4.4 Placeholder

Placeholder 指的是占位符。在 Placeholder 中可使用 setContent() 设置另一个控件的 id,使这个控件移动到占位符的位置。举个例子:

<android.support.constraint.Placeholder
        android:id="@+id/placeholder"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:content="@+id/textview"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#cccccc"
        android:padding="16dp"
        android:text="TextView"
        android:textColor="#000000"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

新建一个 Placeholder 约束在屏幕的左上角,新建一个 TextView 约束在屏幕的右上角,在 Placeholder 中设置 app:content="@+id/textview",这时 TextView 会跑到屏幕的左上角。效果如下:
在这里插入图片描述

4.5 Guideline

Guildline 像辅助线一样,在预览的时候帮助你完成布局(不会显示在界面上)。
Guildline 的主要属性:

android:orientation 垂直 vertical,水平 horizontal
layout_constraintGuide_begin 开始位置
layout_constraintGuide_end 结束位置
layout_constraintGuide_percent 距离顶部的百分比(orientation = horizontal时则为距离左边)

举个例子:

    <android.support.constraint.Guideline
        android:id="@+id/guideline1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="50dp" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />

guideline1 为水平辅助线,开始位置是距离顶部 50dp,guideline2 位垂直辅助线,开始位置为屏幕宽的0.5(中点位置),效果如下:
在这里插入图片描述

5.总结

本篇文章主要介绍了 ConstraintLayout 和它在布局文件里的用法,和一些辅助 ConstraintLayout 布局的工具,跟着敲一遍就能学会 ConstraintLayout。

作者:四会歌神陈子豪
本文为在原作者文章的基础上稍加整理所成。
链接:https://www.jianshu.com/p/17ec9bd6ca8a

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值