使用 ConstraintLayout 构建响应式 UI   作为 Android Jetpack 的一部分。

尝试 Compose 方法
Jetpack Compose 是 Android 推荐的 UI 工具包。了解如何在 Compose 中使用布局。

ConstraintLayout 允许您使用扁平的视图层次结构创建大型复杂布局,无需嵌套视图组。它类似于 RelativeLayout,因为所有视图都根据同级视图和父布局之间的关系进行布局,但它比 RelativeLayout 更灵活,并且更容易与 Android Studio 的布局编辑器一起使用。

由于布局 API 和布局编辑器是为彼此专门构建的,因此可以直接从布局编辑器的可视化工具中获得 ConstraintLayout 的全部功能。您可以完全通过拖放来构建 ConstraintLayout 布局,而无需编辑 XML。

此页面展示了如何在 Android Studio 3.0 或更高版本中使用 ConstraintLayout 构建布局。有关布局编辑器的更多信息,请参阅 使用布局编辑器构建 UI

要查看您可以使用 ConstraintLayout 创建的各种布局,请参阅 GitHub 上的 Constraint Layout 示例项目

约束概述

要定义视图在 ConstraintLayout 中的位置,您需要为该视图添加至少一个水平约束和一个垂直约束。每个约束代表与另一个视图、父布局或不可见的指南的连接或对齐。每个约束定义视图沿垂直或水平轴的位置。每个视图必须至少具有一个用于每个轴的约束,但通常需要更多约束。

当您将视图拖放到布局编辑器中时,即使它没有约束,它也会停留在您放置的位置。这只是为了使编辑更容易。如果视图在您在设备上运行布局时没有约束,则它将在位置 [0,0](左上角)绘制。

在图 1 中,布局在编辑器中看起来不错,但视图 C 没有垂直约束。当此布局在设备上绘制时,视图 C 会水平对齐于视图 A 的左右边缘,但它会出现在屏幕顶部,因为它没有垂直约束。

图 1. 编辑器显示视图 C 在 A 下方,但它没有垂直约束。

图 2. 视图 C 现在垂直约束在视图 A 下方。

虽然缺少约束不会导致编译错误,但布局编辑器会在工具栏中将缺少的约束指示为错误。要查看错误和其他警告,请单击显示警告和错误 。为了帮助您避免缺少约束,布局编辑器会使用 自动连接和推断约束 功能自动为您添加约束。

将 ConstraintLayout 添加到您的项目

要在您的项目中使用 ConstraintLayout,请执行以下操作

  1. 确保您已在 settings.gradle 文件中声明了 maven.google.com 存储库

    Groovy

        dependencyResolutionManagement {
          ...
          repositories {
              google()
          }
        )
        

    Kotlin

        dependencyResolutionManagement {
          ...
          repositories {
              google()
          }
        }
        
  2. 在模块级 build.gradle 文件中添加库作为依赖项,如以下示例所示。最新版本可能与示例中显示的版本不同。

    Groovy

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.0-beta01"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-beta01"
    }
    

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-beta01")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-beta01")
    }
    
  3. 在工具栏或同步通知中,单击使用 Gradle 文件同步项目

现在,您可以使用 ConstraintLayout 构建您的布局了。

转换布局

图 3. 将布局转换为 ConstraintLayout 的菜单。

要将现有布局转换为约束布局,请执行以下步骤

  1. 在 Android Studio 中打开您的布局,然后单击编辑器窗口底部的设计选项卡。
  2. 组件树窗口中,右键单击布局,然后单击将 LinearLayout 转换为 ConstraintLayout

创建新布局

要启动新的约束布局文件,请执行以下步骤

  1. 项目窗口中,单击模块文件夹,然后选择文件 > 新建 > XML > 布局 XML
  2. 输入布局文件的名称,并为根标签输入“androidx.constraintlayout.widget.ConstraintLayout”。
  3. 单击完成

添加或删除约束

要添加约束,请执行以下操作

视频 1. 视图的左侧约束到父级的左侧。

  1. 将视图从调色板窗口拖到编辑器中。

    当您在 ConstraintLayout 中添加视图时,它会显示在边界框中,每个角上都有正方形调整大小的句柄,每侧都有圆形约束句柄。

  2. 单击视图以选择它。
  3. 执行以下操作之一
    • 单击约束句柄并将其拖到可用的锚点。此点可以是另一个视图的边缘、布局的边缘或指南。请注意,当您拖动约束句柄时,布局编辑器会显示潜在的连接锚点和蓝色叠加层。
    • 单击属性窗口布局部分中的创建连接 按钮之一,如图 4 所示。

      图 4.属性窗口的布局部分允许您创建连接。

创建约束后,编辑器会为其提供 默认边距 以分隔这两个视图。

创建约束时,请记住以下规则

  • 每个视图必须至少有两个约束:一个水平约束和一个垂直约束。
  • 您只能在共享同一平面的约束句柄和锚点之间创建约束。视图的垂直平面(左右侧)只能约束到另一个垂直平面,基线只能约束到其他基线。
  • 每个约束句柄只能用于一个约束,但您可以从不同视图创建多个约束到相同的锚点。

您可以通过执行以下任一操作删除约束

  • 单击约束以选择它,然后单击 Delete
  • Control-单击(在 macOS 上为 Command-单击)约束锚点。约束将变为红色以指示您可以单击将其删除,如图 5 所示。

    图 5. 红色约束表示您可以单击将其删除。

  • 属性窗口的布局部分,单击约束锚点,如图 6 所示。

    图 6. 单击约束锚点以将其删除。

视频 2. 添加与现有约束相反的约束。

如果在视图上添加相反的约束,则约束线将像弹簧一样盘绕以指示相反的力,如视频 2 所示。当视图大小设置为“固定”或“包裹内容”时,效果最明显,在这种情况下,视图位于约束之间。如果您希望视图拉伸其大小以满足约束,请 将大小切换 为“匹配约束”。如果您希望保留当前大小但移动视图使其不居中,请 调整约束偏差

您可以使用约束来实现不同类型的布局行为,如下面的部分所述。

父级位置

将视图的一侧约束到布局的相应边缘。

在图 7 中,视图的左侧连接到父布局的左边缘。您可以使用边距定义距边缘的距离。

图 7. 对父级的水平约束。

顺序位置

定义两个视图的出现顺序,垂直或水平。

在图 8 中,B 约束为始终在 A 的右侧,C 约束在 A 下方。但是,这些约束并不意味着对齐,因此 B 仍然可以上下移动。

图 8. 水平和垂直约束。

对齐

将视图的边缘与另一个视图的同一边缘对齐。

在图 9 中,B 的左侧与 A 的左侧对齐。如果要对齐视图中心,请在两侧创建约束。

您可以通过从约束向内拖动视图来偏移对齐。例如,图 10 显示 B 带有 24dp 偏移对齐。偏移由受约束视图的边距定义。

您也可以选择所有要对齐的视图,然后单击工具栏中的对齐 以选择对齐类型。

图 9. 水平对齐约束。

图 10. 偏移水平对齐约束。

基线对齐

将视图的文本基线与另一个视图的文本基线对齐。

在图 11 中,B 的第一行与 A 中的文本对齐。

要创建基线约束,请右键单击要约束的文本视图,然后单击显示基线。然后单击文本基线并将线拖动到另一个基线。

图 11. 基线对齐约束。

约束到参考线

您可以添加垂直或水平参考线,让您约束视图,并且对应用的用户不可见。您可以根据 dp 单位或相对于布局边缘的百分比在布局中定位参考线。

要创建参考线,请单击工具栏中的参考线 ,然后单击添加垂直参考线添加水平参考线

拖动虚线以重新定位它,并单击参考线边缘的圆圈以切换测量模式。

图 12. 约束到参考线的视图。

约束到屏障

与参考线类似,屏障是一条不可见的线,您可以将视图约束到它,但屏障不定义自己的位置。相反,屏障位置根据包含在其内的视图的位置移动。当您希望将视图约束到一组视图而不是一个特定视图时,这很有用。

例如,在图 13 中,视图 C 约束到屏障的右侧。屏障设置为视图 A 和视图 B 的“末端”(或在从左到右的布局中为右侧)。屏障会根据视图 A 或视图 B 的右侧哪个最靠右而移动。

要创建屏障,请按照以下步骤操作

  1. 单击工具栏中的参考线 ,然后单击添加垂直屏障添加水平屏障
  2. 组件树窗口中,选择要放在屏障内的视图,并将它们拖到屏障组件中。
  3. 组件树中选择屏障,打开属性 窗口,然后设置barrierDirection

现在您可以从另一个视图创建到屏障的约束。

您还可以将位于屏障内的视图约束到屏障。这样,即使您不知道哪个视图最长或最高,您也可以将屏障中的所有视图相互对齐。

您还可以将参考线包含在屏障内以确保屏障的“最小”位置。

图 13. 视图 C 约束到屏障,屏障根据视图 A 和视图 B 的位置和大小移动。

调整约束偏差

当您在视图的两侧添加约束,并且同一尺寸的视图大小为“固定”或“包裹内容”时,默认情况下,视图在两个约束之间居中,偏差为 50%。您可以通过拖动属性窗口中的偏差滑块或拖动视图来调整偏差,如视频 3 中所示。

如果您希望视图拉伸其大小以满足约束,请切换大小到“匹配约束”。

视频 3. 调整约束偏差。

调整视图大小

图 14. 选择视图时,属性窗口包含以下控件:1 尺寸比例、2 删除约束、3 高度或宽度模式、4 边距和5 约束偏差。您还可以通过在6 约束列表中单击它们来突出显示布局编辑器中的各个约束。

您可以使用角手柄调整视图大小,但这会硬编码大小——视图不会针对不同的内容或屏幕尺寸调整大小。要选择不同的尺寸模式,请单击一个视图并打开编辑器右侧的属性 窗口。

属性窗口顶部附近是视图检查器,其中包含多个布局属性的控件,如图 14 所示。这仅适用于约束布局中的视图。

您可以通过单击图 14 中带注释3的符号来更改高度和宽度的计算方式。这些符号表示尺寸模式如下。单击符号以在这些设置之间切换

  • 固定:在下面的文本框中或通过在编辑器中调整视图大小来指定特定尺寸。
  • 包裹内容:视图仅扩展到适合其内容所需的程度。
    • layout_constrainedWidth
    • 将其设置为true 以使水平尺寸更改以尊重约束。默认情况下,设置为WRAP_CONTENT 的小部件不受约束限制。

  • 匹配约束:视图尽可能扩展以满足每一侧的约束,在考虑视图边距之后。但是,您可以使用以下属性和值修改该行为。仅当您将视图宽度设置为“匹配约束”时,这些属性才会生效
    • layout_constraintWidth_min

      这需要dp 尺寸作为视图的最小宽度。

    • layout_constraintWidth_max

      这需要dp 尺寸作为视图的最大宽度。

    但是,如果给定尺寸只有一个约束,则视图会扩展以适合其内容。在高度或宽度上使用此模式还可以让您设置尺寸比例

设置尺寸比例

图 15. 视图设置为 16:9 的纵横比,宽度基于高度的比例。

如果至少一个视图尺寸设置为“匹配约束”(0dp),则可以将视图尺寸设置为比例,例如 16:9。要启用比例,请单击切换纵横比约束(图 14 中的注释1),并在出现的输入中输入宽度:高度比例。

如果宽度和高度都设置为“匹配约束”,则可以单击切换纵横比约束以选择哪个尺寸基于另一个尺寸的比例。视图检查器通过用实线连接相应的边缘来指示哪个尺寸设置为比例。

例如,如果将两侧都设置为“匹配约束”,请双击切换纵横比约束以将宽度设置为高度的比例。整个尺寸由视图的高度决定,可以以任何方式定义,如图 15 所示。

调整视图边距

要使您的视图均匀分布,请单击工具栏中的边距 以选择添加到布局的每个视图的默认边距。您对默认边距所做的任何更改仅适用于您从那时起添加的视图。

您可以通过单击表示每个约束的线上的数字来控制属性窗口中每个视图的边距。在图 14 中,注释4显示底部边距设置为 16dp。

图 16. 工具栏的边距按钮。

工具提供的所有边距都是 8dp 的倍数,以帮助您的视图与 Material Design 的 8dp 正方形网格建议对齐。

使用链控制线性组

图 17. 具有两个视图的水平链。

链是一组通过双向位置约束相互链接的视图。链中的视图可以垂直或水平分布。

图 18. 每种链样式的示例。

链可以以以下方式之一进行样式化

  1. 扩展:在考虑边距后,视图均匀分布。这是默认值。
  2. 内部扩展:第一个和最后一个视图固定在链两端的约束上,其余视图均匀分布。
  3. 加权:当链设置为扩展内部扩展时,您可以通过将一个或多个视图设置为“匹配约束”(0dp)来填充剩余空间。默认情况下,空间在每个设置为“匹配约束”的视图之间均匀分布,但您可以使用layout_constraintHorizontal_weightlayout_constraintVertical_weight 属性为每个视图分配重要性权重。这与layout_weight线性布局中的工作方式相同:权重值最高的视图获得最多的空间,权重相同的视图获得相同数量的空间。
  4. 打包:在考虑边距后,视图将打包在一起。您可以通过更改链的“头部”视图偏差来调整整个链的偏差——左或右,或上或下。

链的“头部”视图——水平链中最左侧的视图(在从左到右的布局中)和垂直链中最顶部的视图——在 XML 中定义链的样式。但是,您可以通过选择链中的任何视图并单击视图下方显示的链按钮扩展内部扩展打包之间切换。

要创建链,请执行以下操作,如视频 4 中所示

  1. 选择要包含在链中的所有视图。
  2. 右键单击其中一个视图。
  3. 选择
  4. 选择水平居中垂直居中

视频 4. 创建水平链。

以下是在使用链时需要考虑的一些事项

  • 一个视图可以同时是水平链和垂直链的一部分,因此您可以构建灵活的网格布局。
  • 只有当链的两端都约束到同一轴上的另一个对象时,链才能正常工作,如图 14 所示。
  • 虽然链的方向是垂直或水平的,但使用一个方向并不会在该方向上对齐视图。要实现链中每个视图的正确位置,请包含其他约束,例如对齐约束

自动创建约束

无需在将每个视图放置到布局中时都添加约束,您可以将每个视图移动到布局编辑器中所需的位置,然后单击推断约束 以自动创建约束。

推断约束扫描布局以确定所有视图最有效的约束集。它将视图约束到其当前位置,同时提供灵活性。您可能需要进行一些调整,以使布局根据您的意图对不同的屏幕尺寸和方向做出响应。

自动连接到父布局 是一项可单独启用的功能。启用此功能后,当您向父布局添加子视图时,此功能会在您将每个视图添加到布局时自动创建两个或多个约束,但仅在将视图约束到父布局合适的情况下才会创建。自动连接不会创建到布局中其他视图的约束。

自动连接默认处于禁用状态。点击布局编辑器工具栏中的启用自动连接到父布局 以启用它。

关键帧动画

ConstraintLayout 中,您可以使用 ConstraintSetTransitionManager 来为元素的大小和位置变化设置动画。

ConstraintSet 是一个轻量级对象,它表示 ConstraintLayout 中所有子元素的约束、边距和填充。当您将 ConstraintSet 应用于显示的 ConstraintLayout 时,布局会更新其所有子元素的约束。

要使用 ConstraintSet 构建动画,请指定两个用作动画开始和结束关键帧的布局文件。然后,您可以从第二个关键帧文件加载 ConstraintSet 并将其应用于显示的 ConstraintLayout

以下代码示例演示了如何为将单个按钮移动到屏幕底部设置动画。

// MainActivity.kt

fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.keyframe_one)
    constraintLayout = findViewById(R.id.constraint_layout) // member variable
}

fun animateToKeyframeTwo() {
    val constraintSet = ConstraintSet()
    constraintSet.load(this, R.layout.keyframe_two)
    TransitionManager.beginDelayedTransition()
    constraintSet.applyTo(constraintLayout)
}
// layout/keyframe1.xml
// Keyframe 1 contains the starting position for all elements in the animation
// as well as final colors and text sizes.

<?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/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
// layout/keyframe2.xml
// Keyframe 2 contains another ConstraintLayout with the final positions.

<?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/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

其他资源

ConstraintLayout 用于 Sunflower 演示应用中。