国际惯例,官网参考文档是最好的学习资料:
一,背景
约束布局 ConstraintLayout,也有人把它称作“增强型的相对布局”,扁平式的布局方式,无任何嵌套,减少布局的层级,优化渲染性能。是最受欢迎的 Jetpack 库之一,其实是 Android Studio 2.2 中主要的新增功能之一,也是 Google 在2016年的 I/O 大会上重点宣传的一个功能。AS 已经将它作为新建页面默认布局了。经历这几年的迭代,功能已经非常的成熟,现在 2.0 正式版本也发布了,也许你已熟悉了旧版本中的功能,并开始用它来快速构建复杂的页面布局,而新版本除了包含旧版本中的所有功能之外,还在 AS 中集成了可以直接预览 XML 的工具,甚至可以直接在预览界面中对布局进行编辑。
二,控件优点
ConstraintLayout 可以理解为 RelativeLayout + LinearLayout 的混合强化版,同时新版 Android Studio 的布局编辑器也提供了对 ConstraintLayout 完善的编辑支持。使用 ConstraintLayout 可以很方便地在一个层级上实现复杂的布局,功能也很完善,是 Android 官方目前非常重视的一个 Layout(替代以前的 RelativeLayout),使用 ConstraintLayout 后基本可以抛弃 LinearLayout 和 RelativeLayout 的使用,完全不需要任何嵌套就可以实现复杂的 UI,使用起来特别清爽。减少了很多的嵌套的层级,这样 View 在渲染的时候,减少了很多多余的 measure 和 layout 的开销,如果嵌套的层次越多,提升的效果越明显。
感兴趣的话看官方给出的性能测试示例:解析ConstraintLayout的性能优势
三,项目中引入
如需在项目中使用ConstraintLayout,请按以下步骤操作:
- 确保你的
maven.google.com代码库已在模块级build.gradle文件中声明:
repositories {
google()
}
- 将该库作为依赖项添加到同一个
build.gradle文件中,如以下示例所示。请注意,最新版本可能与示例中显示的不同:
dependencies {
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
// To use constraintlayout in compose
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha05"
}
- 在工具栏或同步通知中,点击 Sync Project with Gradle Files。
现在,你可以使用 ConstraintLayout 构建布局。
四,基本使用
4.1 相对位置
要在ConstraintLayout 中确定 view 的位置,必须至少添加一个水平和垂直的约束。每一个约束表示到另一个 view、父布局或者不可见的参考线的连接或者对齐。如果水平或者垂直方向上没有约束,那么其位置就是0。
下面是 ConstraintLayout 确定位置的属性,这些属性的值即可以是 parent(父布局),也可以是某个 view 的 id,和同类型的RelativeLayout属性很相似:
| ConstraintLayout | RelativeLayout | 作用 |
|---|---|---|
| layout_constraintLeft_toLeftOf | layout_alignLeft | 与参照控件左对齐 |
| layout_constraintLeft_toRightOf | layout_toRightOf | 在参照控件的右边 |
| layout_constraintRight_toLeftOf | layout_toLeftOf | 在参照控件的左边 |
| layout_constraintRight_toRightOf | layout_alignRight | 与参照控件右对齐 |
| layout_constraintTop_toTopOf | layout_alignTop | 与参照控件上对齐 |
| layout_constraintTop_toBottomOf | layout_below | 在参照控件底部 |
| layout_constraintBottom_toTopOf | layout_alignBottom | 在参照控件的上部 |
| layout_constraintBottom_toBottomOf | layout_above | 与参照控件下对齐 |
| layout_constraintBaseline_toBaselineOf | layout_alignBaseline | 与参照控件基线对齐 |
| layout_constraintStart_toEndOf | ||
| layout_constraintStart_toStartOf | ||
| layout_constraintEnd_toStartOf | ||
| layout_constraintEnd_toEndOf |
说明:这里 start/end 一般情况下和 left/right 是一样的效果,主要是为了做国际化适配,方便从右往左读的语言文字(如阿拉伯文)。
一个简单的页面:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="左对齐"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="右对齐"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="水平居中"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="垂直居中"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="底部对齐"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="水平居中+垂直居中"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

居中显示:
<!--
即view的左边对齐父布局的左边,view的右边对齐父布局的右边,除非这个view的大小刚好充满整个父布局;
否则的话,就是水平居中显示了。我们可以理解为有两个相同的力,把view拉到中间。
-->
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
水平方向的相对位置:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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/btn_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="水平参照物"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Left_toLeftOf"
app:layout_constraintBottom_toTopOf="@id/btn_center"
app:layout_constraintLeft_toLeftOf="@id/btn_center"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Right_toLeftOf"
app:layout_constraintBottom_toTopOf="@id/btn_center"
app:layout_constraintRight_toLeftOf="@id/btn_center"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Right_toRightOf"
app:layout_constraintRight_toRightOf="@id/btn_center"
app:layout_constraintTop_toBottomOf="@id/btn_center"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Left_toRightOf"
app:layout_constraintLeft_toRightOf="@id/btn_center"
app:layout_constraintTop_toBottomOf="@id/btn_center"/>
</androidx.constraintlayout.widget.ConstraintLayout>
效果如下:

竖直方向的相对位置:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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/btn_center"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:text="竖直参照物"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Top_toTopOf"
app:layout_constraintTop_toTopOf="@id/btn_center"
app:layout_constraintRight_toLeftOf="@id/btn_center"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bottom_toTopOf"
app:layout_constraintBottom_toTopOf="@id/btn_center"
app:layout_constraintRight_toLeftOf="@id/btn_center"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Top_toBottomOf"
app:layout_constraintLeft_toRightOf="@id/btn_center"
app:layout_constraintTop_toBottomOf="@id/btn_center"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bottom_toBottomOf"
app:layout_constraintLeft_toRightOf="@id/btn_center"
app:layout_constraintBottom_toBottomOf="@id/btn_center"/>
</androidx.constraintlayout.widget.ConstraintLayout>
效果如下

4.2 尺寸约束
view 中使用 warp_content 或者固定值等等是没有问题的。但是在 ConstraintLayout 中不推荐使用 match_parent 这个值,如果需要实现跟 match_parent 同样的效果,可以使用 0dp 来代替,其表示 match_parent,即适应约束。其跟 match_parent 还是有区别的,后面的例子(宽高比那一小节)会提到。
我们来看下例子:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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/btn_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="@+id/btn_1"
android:layout_width="180dp"
android:layout_height="wrap_content"
android:text="具体数值:180dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_center"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="0dp(match_parent)"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_1"/>
</androidx.constraintlayout.widget.ConstraintLayout>

4.3 宽高比 Ratio
在 ConstraintLayout 中,还可以将宽定义成高的一个比例或者高定义成宽的比率。首先,需要将宽或者高设置为0dp(即match_parent),即要适应约束条件。然后通过 layout_constraintDimensionRatio 属性设置一个比率即可。这个比率可以是一个浮点数,表示宽度和高度之间的比率;也可以是“宽度:高度”形式的比率。比如:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="30dp"
app:layout_constraintTop_toTopOf="parent"
android:text="-------------------宽高比2:1-------------------"
app:layout_constraintDimensionRatio="2:1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

如果宽和高都设置为0dp(match_parent),那么 layout_constraintDimensionRatio 的值需要先加一个"W,"或者"H,"来表示约束宽度或高度。如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
4.4 百分比宽高 Percent
ConstraintLayout 还能使用百分比来设置 view 的宽高。要使用百分比,宽或高同样要设置为 0dp(match_parent)。然后设置以下属性即可:
app:layout_constraintWidth_default="percent" //设置宽为百分比,可以设置percent、spread和wrap
app:layout_constraintWidth_percent="0.3" //0到1之间的值,为占父布局宽度的多少
app:layout_constraintHeight_default="percent" //设置高为百分比,可以设置percent、spread和wrap
app:layout_constraintHeight_percent="0.3" //0到1之间的值,为占父布局宽度的多少
例子如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:layout_width="0dp"
android:layout_height="wrap_content"
android:text="宽50%"
app:layout_constraintWidth_default="percent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintWidth_percent="0.5"/>
</androidx.constraintlayout.widget.ConstraintLayout>
注:在早期版本中必需手动指定 app:layout_constraintWidth_default=“percent”,在之后的版本中如果设置了app:layout_constraintWidth_percent 属性,则可以不用指定。
效果如下:

官网的介绍是这样的:
You can also define one dimension of a widget as a ratio of the other one. In order to do that, you need to have at least one constrained dimension be set to 0dp (i.e., MATCH_CONSTRAINT), and set the attribute layout_constraintDimensionRatio to a given ratio
意思是说约束布局支持子控件设置宽高比,前提条件是至少需要将宽高中的一个设置为0dp。为了约束一个特定的边,基于另一个边的尺寸,可以预先附加W,或H以逗号隔开。
4.5 偏移量 bias
如果想让view的位置偏向某一侧,可以使用以下的两个属性来设置:
layout_constraintHorizontal_bias //水平偏向
layout_constraintVertical_bias //竖直偏向
其值同样也是0到1之间。比如,以下例子为横向偏向左侧30%,默认的居中效果就是50%:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="左边偏向30%"
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
显示效果如下:

4.6 圆形定位 Circular positioning
以一个控件为圆心设置角度和半径定位
- layout_constraintCircle:关联另一个控件,将另一个控件放置在自己圆的半径上,会和下面两个属性一起使用
- layout_constraintCircleRadius:圆的半径
- layout_constraintCircleAngle:圆的角度
4.7 权重 weight
LinearLayout中可以设置权重,ConstraintLayout 同样也有这个属性。通过设置以下两个属性:
app:layout_constraintHorizontal_weight //水平权重
app:layout_constraintVertical_weight //竖直权重
然后将相连的 view 两两约束好即可。如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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/btn_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="权重为1"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/btn_2"/>
<Button
android:id="@+id/btn_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="权重为2"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@id/btn_1"
app:layout_constraintRight_toLeftOf="@id/btn_3"/>
<Button
android:id="@+id/btn_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="2"
android:text="权重为2"
app:layout_constraintLeft_toRightOf="@id/btn_2"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

4.8 辅助线 Guideline
Guideline 可以用来辅助布局,通过 Guideline 能创建出一条水平线或者垂直线,该线不会显示到界面上,但是能够利用这些线条来添加约束去完成界面的布局。
Guideline主要的属性有:
android:orientation="horizontal|vertical"
app:layout_constraintGuide_begin="30dp"
app:layout_constraintGuide_end="30dp"
app:layout_constraintGuide_percent="0.5"
<!--
android:orientation=”horizontal|vertical”表示是水平或垂直引导线。
app:layout_constraintGuide_begin=”30dp”,如果是水平引导线,则距离布局顶部30dp,如果是垂直引导线,则距离布局左边30dp。
app:layout_constraintGuide_end=”30dp”,如果是水平引导线,则距离布局底部30dp,如果是垂直引导线,则距离布局右边30dp。
app:layout_constraintGuide_percent=”0.5”,如果是水平引导线,则距离布局顶部为整个布局高度的50%,如果是垂直引导线,则距离布局左边文这个布局宽度的50%。
-->
来看个例子:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_v"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"/>
<Button
app:layout_constraintLeft_toLeftOf="@id/guideline_v"
app:layout_constraintTop_toTopOf="@id/guideline_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="辅助线定位"/>
</androidx.constraintlayout.widget.ConstraintLayout>
如下图所示:

4.9 屏障 Barrier
Barrier,直译为障碍、屏障。在约束布局中,可以使用属性constraint_referenced_ids属性来引用多个带约束的组件,从而将它们看作一个整体,Barrier 的介入可以完成很多其他布局不能完成的功能。
开发中有这样的一个需求,看下图:

姓名、联系方式位于左侧区域(随着文本的宽度变化左侧区域的宽度也随之变化),使用传统的布局方式实现嵌套过多,布局不够优雅。那么来看看约束布局是怎么去实现的:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:padding="20dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="姓名:"
app:layout_constraintBottom_toBottomOf="@+id/et_name"
app:layout_constraintTop_toTopOf="@+id/et_name"/>
<TextView
android:id="@+id/tv_contract"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="联系方式:"
app:layout_constraintBottom_toBottomOf="@+id/et_contract"
app:layout_constraintTop_toTopOf="@+id/et_contract"/>
<EditText
android:id="@+id/et_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="请输入姓名"
app:layout_constraintLeft_toLeftOf="@+id/barrier"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<EditText
android:id="@+id/et_contract"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="请输入联系方式"
app:layout_constraintLeft_toLeftOf="@+id/barrier"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_name"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="tv_name,tv_contract"/>
</androidx.constraintlayout.widget.ConstraintLayout>
barrierDirection 指定方向,constraint_referenced_ids引用的控件 id(多个id以逗号隔开)。
4.10 Group
Group用于控制多个控件的可见性。使用非常简单,若 android:visibility="gone" 那么 btn1,btn2 控件都会隐藏。
<androidx.constraintlayout.widget.Group
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
app:constraint_referenced_ids="btn1,btn2">
<Button
android:id="@+id/btn1"
android:text="btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn2"
android:text="btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.Group>
4.11 隐藏边距 goneMargin
当约束目标的可见性为View.GONE时,还可以通过以下属性设置不同的边距值:
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
4.12 链 Chains
上面这种将相连的 view 两两约束好的实际上就形成了链。在 ConstraintLayout 中可以实现各种不同的链,权重链是其中一种。整个链由链中的第一个view(链头)上设置的属性控制。
官网上一共有4种样式的链:

- Spread:视图是均匀分布的(在考虑外边距之后)。这是默认值。
- Spread inside:第一个和最后一个视图固定在链两端的约束边界上,其余视图均匀分布。
- Weighted:当链设置为 spread 或 spread inside 时,您可以通过将一个或多个视图设置为“match_parent”(0dp) 来填充剩余空间。默认情况下,设置为“match_parent”的每个视图之间的空间均匀分布,但您可以使用 layout_constraintHorizontal_weight 和 layout_constraintVertical_weight 属性为每个视图分配重要性权重。如果您熟悉线性布局中的 layout_weight 的话,就会知道该样式与它的原理是相同的。因此,权重值最高的视图获得的空间最大;相同权重的视图获得同样大小的空间。
- Packed:视图打包在一起(在考虑外边距之后)。 然后,您可以通过更改链的头视图偏差调整整条链的偏差(左/右或上/下)。
layout_constraintHorizontal_chainStyle // 横向约束链
layout_constraintVertical_chainStyle // 纵向约束链
五,ConstraintLayout 2.0 新特性
官方更新文档:What’s New in 2.1
5.1 新增VirtualLayouts:Flow布局
Flow布局是Chains的强化版,是一种新的VirtualLayouts。用于构建流式排版效果:当出现空间不足时,可自动换行或自动延展到屏幕的另一区域。即当需要对多个View进行流式布局 / 不确定其布局空间的实际尺寸时,就可使用Flow布局。

使用步骤:
- 通过属性
constraint_referenced_ids获取要引用的视图; - 根据这些视图创建一个虚拟的virtual view group;
- 对这些视图进行流式布局。
<androidx.constraintlayout.helper.widget.Flow
app:constraint_referenced_ids="a1,a2,a3"
app:flow_wrapMode="aligned"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
核心属性:flow_wrapMode,用于控制元素的排列方式。

属性说明如下:
<androidx.constraintlayout.helper.widget.Flow
app:constraint_referenced_ids="a1,a2,a3"
app:flow_wrapMode="aligned"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<!--
NONE : 默认模式。Views不会被包裹到另一行或列。如果它们在屏幕之外,则不能被看到。
CHAIN : CHAIN与Chains非常相似,可以认为是Chains的强化版本。CHAIN是根据orientation逐行或逐列进行排列的,默认的展示风格是SPREAD。
ALIGNED : ALIGNED模式与CHAIN类似,但链式是在将不同行和列的视图对齐后考虑的,默认的样式是SPREAD。
-->
5.2 可自定义ConstraintHelper
ConstraintHelper 是一个用于记录标记 Views 的 Helper,通过引用指定 Views 从而实现具体效果。
使用场景
- 辅助布局:创建一个新的布局方式,避免创建新的 ViewGroup 从而加深层级
- 修改布局:在布局完成后,修改布局效果
- 重新渲染:在View绘制完成后,对View进行修改、重新渲染效果
具体使用:通过继承 ConstraintHelper 类实现 & 重写所需回调,具体如下所示。
// 继承ConstraintHelper类
class carsonhoTest @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintHelper(context, attrs, defStyleAttr) {
// 复写常用方法
override fun updatePostLayout(container: ConstraintLayout?) {
super.updatePostLayout(container)
getViews(container).forEach { view ->
ViewAnimationUtils.createCircularReveal(
view, view.width / 3,
view.height / 3, 0f,
hypot((view.height / 3.0), (view.width / 3.0)).toFloat()
).apply {
duration = 2000
start()
}
}
}
}
// 常用方法说明
init() // 初始化调用
updatePreLayout() // 布局前更新
updatePostLayout() // 布局后更新
updatePostMeasure() // 测量后更新
updatePostConstraints() // 更新约束
onDraw() // 进行绘制
5.3 新增切换状态布局功能:ConstraintLayoutStates
ConstraintLayoutStates 用于根据状态切换不同的布局文件。
具体使用
// 步骤1:创建不同状态的layout xml文件,布局文件的root id相同即可。
// 如:states_1、states_2
// 步骤2:在xml文件夹下创建管理文件
<?xml version="1.0" encoding="utf-8"?>
<ConstraintLayoutStates xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<State
android:id="@+id/state1"
app:constraints="@layout/states_1" />
<State
android:id="@+id/state2"
app:constraints="@layout/states_2" />
</ConstraintLayoutStates>
// 步骤3:使用loadLayoutDescription来加载管理文件
val constraintLayout = findViewById<ConstraintLayout>(R.id.constraint_state)
constraintLayout.loadLayoutDescription(R.xml.constraint_layout_states)
constraintLayout.setOnClickListener {
// 点击时切换布局文件
constraintLayout.setState(R.id.state2, 0, 0)
}
5.4 新增创建约束工具类:ConstraintProperties
ConstraintProperties 是在代码中创建约束的工具类。
具体使用:在该工具类出来前,修改属性的常用方式是:
ConstraintSet().apply {
clone(constraintLayout)
setTranslationX(R.id.xxx, 32f)
setMargin(R.id.xxx, ConstraintSet.START, 42)
applyTo(constraintLayout)
}
对于 ConstraintLayoutStates,可使用流式 API 修改属性,更加方便快捷。
ConstraintProperties(findViewById(R.id.xxx))
.translationZ(8f)
.margin(ConstraintSet.START, 8)
.apply()
PS. 推荐阅读
- 使用 ConstraintLayout 构建自适应界面【官方文档】
- 解析ConstraintLayout的性能优势【Google 开发者计划工程师 Takeshi Hagikura】
- Android新特性介绍,ConstraintLayout完全解析【郭霖,拖拽动图讲解】
- ConstraintLayout 完全解析,快来优化你的布局吧【张鸿洋】
- 自律给你自由——设计布局的新姿势【徐宜生】
- ConstraintLayout使用场景必知必会【徐宜生】
- 带你了解Android约束布局ConstraintLayout【四月葡萄】
- ConstraintLayout使用指南
- 妙用ConstraintLayout的Circular positioning【进阶实战,按钮展开效果】
- 实战篇ConstraintLayout的崛起之路
- android ConstraintLayout使用详解
- ConstraintLayout的使用教程
- ConstraintLayout 2.0 新特性详解及实战
- 约束布局ConstraintLayout看这一篇就够了
- ConstraintLayout看完一篇真的就够了么?
- 布局优化:9种让你不得不使用约束布局Constraintlayout的场景
- 万字长文 - 史上最全ConstraintLayout(约束布局)使用详解
- ConstraintLayout使用大全
本文详细介绍了Android的ConstraintLayout,包括其背景、优点、项目引入方法及基本使用技巧,如相对位置、尺寸约束、宽高比、百分比宽高、偏移量、圆形定位等。此外,还探讨了ConstraintLayout 2.0的新特性,如Flow布局、自定义ConstraintHelper、状态切换布局功能和创建约束工具类,帮助开发者更高效地构建复杂且自适应的界面。
1046

被折叠的 条评论
为什么被折叠?



