使用 ConstraintLayout 构建响应式界面 Android Jetpack 的一部分。

试用 Compose 方式
Jetpack Compose 是推荐用于 Android 的界面工具包。了解如何在 Compose 中使用布局。

借助 ConstraintLayout,您可以使用扁平视图层次结构(无嵌套视图组)创建复杂的大型布局。它与 RelativeLayout 类似,因为所有视图均根据同级视图和父布局之间的关系进行布局,但它比 RelativeLayout 更灵活,并且更易于与 Android Studio 的布局编辑器搭配使用。

ConstraintLayout 的所有功能都可以直接从布局编辑器的可视化工具中获得,因为布局 API 和布局编辑器是专为彼此构建的。您可以通过拖动(而不是修改 XML)完全使用 ConstraintLayout 构建布局。

本页介绍了如何在 Android Studio 3.0 或更高版本中使用 ConstraintLayout 构建布局。如需详细了解布局编辑器,请参阅使用布局编辑器构建界面

如需查看您可以使用 ConstraintLayout 创建的各种布局,请参阅 GitHub 上的“约束布局示例”项目

约束条件概览

如需在 ConstraintLayout 中定义视图的位置,请为视图至少添加一个水平约束条件和一个垂直约束条件。每个约束条件都表示与其他视图、父布局的连接或对齐方式,或一条不可见的引导线。每个约束条件都定义了视图沿垂直轴或水平轴的位置。每个视图都必须针对每个轴至少有一个约束条件,但通常需要更多约束条件。

当您将某个视图拖放到布局编辑器中时,即使没有任何约束条件,该视图也会保留在您放置的位置。这么做只是为了方便您进行修改。当您在设备上运行布局时,如果视图没有任何约束条件,则会在位置 [0,0](左上角)绘制该视图。

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

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

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

虽然缺少约束条件不会导致编译错误,但布局编辑器会在工具栏中将缺少约束条件显示为错误。如需查看错误和其他警告,请点击 Show Warnings and Errors 。 为了帮助您避免缺少约束条件,布局编辑器会使用 Autoconnect 和 Induct constraints 功能自动为您添加约束条件。

将 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-alpha13"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha13"
    }
    

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha13")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha13")
    }
    
  3. 在工具栏或同步通知中,点击 Sync Project with Gradle Files

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

转换布局

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

如需将现有布局转换为约束布局,请按以下步骤操作:

  1. 在 Android Studio 中打开您的布局,然后点击编辑器窗口底部的 Design 标签页。
  2. Component Tree 窗口中,右键点击该布局,然后点击 Convert LinearLayout to ConstraintLayout

创建新布局

如需开始新的约束布局文件,请按以下步骤操作:

  1. Project 窗口中,点击模块文件夹,然后依次选择 File > New > XML > Layout XML
  2. 输入布局文件的名称,并对根标记输入“androidx.constraintlayout.widget.ConstraintLayout”。
  3. 点击 Finish(完成)。

添加或移除约束条件

如需添加约束条件,请执行以下操作:

视频 1. 视图的左侧被约束在父视图的左侧。

  1. 将视图从 Palette 窗口拖到编辑器中。

    ConstraintLayout 中添加视图时,该视图会显示在边界框中,每个角上都有方形大小调整手柄,每边都有圆形约束手柄。

  2. 点击视图将其选中。
  3. 执行以下某项操作:
    • 点击约束手柄并将其拖动到可用的定位点。 此点可以是另一个视图的边缘、布局的边缘或引导线。请注意,在拖动约束手柄时,布局编辑器会显示可能的连接锚点和蓝色叠加层。
    • 点击 Attributes 窗口的 Layout 部分中的 Create a connection 按钮之一,如图 4 所示。

      图 4. 您可以在 Attributes 窗口的 Layout 部分中创建连接。

创建约束条件后,编辑器会为其指定默认外边距来分隔两个视图。

创建约束条件时,请注意以下规则:

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

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

  • 点击某个限制条件将其选中,然后点击删除
  • 在按住 Ctrl 键的同时点击(在 macOS 上,按住 Command 键的同时点击)某个约束条件锚点。该约束条件会变为红色,表示您可以点击将其删除,如图 5 所示。

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

  • Attributes 窗口的 Layout 部分中,点击某个约束定位点,如图 6 所示。

    图 6. 点击约束定位点将其删除。

视频 2. 添加与现有约束条件相反的限制条件。

如果您在视图上添加相反的约束条件,约束线会像弹簧一样盘旋,以表示相反的力,如视频 2 所示。当视图大小设置为“固定”或“封装内容”时,效果最明显,在这种情况下,视图位于约束条件之间的中心。如果您希望视图拉伸其尺寸以满足约束条件,将尺寸切换为“match constraints”。如果要保持当前大小,但移动视图使其不居中,请调整约束偏差

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

父级位置

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

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

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

顺序位置

定义两个视图的垂直或水平显示顺序。

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

图 8. 水平和垂直约束条件。

对齐方式

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

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

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

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

图 9. 水平对齐约束条件。

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

基线对齐

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

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

如需创建基准约束条件,请右键点击要约束的文本视图,然后点击 Show Baseline。然后点击文本基线,并将线条拖动到另一基线。

图 11. 基线对齐约束条件。

引导线约束

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

如需创建引导线,请点击工具栏中的 Guidelines 图标 ,然后点击 Add Vertical GuidelineAdd Horizontal Guideline

拖动虚线以调整其位置,然后点击引导线边缘的圆圈以切换测量模式。

图 12. 受引导线约束的视图。

屏障约束

与引导线类似,屏障线是您可以约束视图的不可见线,不同之处在于屏障不会定义自己的位置。相反,屏障位置会根据其中包含的视图的位置移动。如果您希望将某个视图约束为一组视图而不是一个特定视图,这会非常有用。

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

如需创建屏障,请按以下步骤操作:

  1. 点击工具栏中的 Guidelines ,然后点击 Add Vertical BarrierAdd Horizontal Barrier
  2. Component Tree 窗口中,选择要放入屏障内的视图,然后将它们拖动到屏障组件中。
  3. Component Tree 中选择屏障,打开 Attributes 窗口,然后设置 fenceDirection

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

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

您还可以在屏障内添加准则,以确保屏障的“最小”位置。

图 13. 视图 C 被限制在屏障内,该屏障将根据视图 A 和视图 B 的位置和大小移动。

调整约束偏差

当您向视图的两侧添加约束条件并且相同维度的视图大小为“固定”或“封装内容”时,视图将居中于两个约束条件之间,默认情况下偏差为 50%。您可以通过拖动 Attributes 窗口中的偏差滑块或拖动视图来调整偏差,如视频 3 所示。

如果您希望视图通过拉伸其尺寸来满足约束条件,将尺寸切换为“match constraints”。

视频 3. 调整约束偏差。

调整视图尺寸

图 14. 选择视图时,Attributes 窗口会包含以下控件:1 尺寸比率、2 删除约束条件、3 高度或宽度模式、4 个外边距以及 5 约束条件偏差。您还可以在布局编辑器中突出显示各个约束条件,只需在 6 约束条件列表中点击相应约束条件即可。

您可以使用角手柄调整视图的大小,但这会对大小进行硬编码,即视图不会根据不同的内容或屏幕尺寸调整大小。如需选择其他大小调整模式,请点击视图,然后打开编辑器右侧的 Attributes 窗口。

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

您可以通过点击图 14 中的标注 3 所指示的符号,更改高度和宽度的计算方式。这些符号代表如下尺寸模式。点击该符号即可在以下设置之间切换:

  • Fixed:在下列文本框中指定具体尺寸,或在编辑器中调整视图大小。
  • Wrap Content:视图仅在必要时扩展以适应其内容。
    • layout_constraintWidth
    • 将此属性设置为 true 可使水平尺寸发生变化以遵循约束条件。默认情况下,设置为 WRAP_CONTENT 的 widget 不受约束条件限制。

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

      该视图的最小宽度采用 dp 维度。

    • layout_constraintWidth_max

      该视图的最大宽度采用 dp 维度。

    不过,如果给定维度只有一个约束条件,视图就会扩展以适应其内容。针对高度或宽度使用此模式还可以设置尺寸比

将尺寸设置为比率

图 15. 视图设置为 16:9 的宽高比,其宽度基于高度的比率。

如果至少有一个视图维度设置为“match constraints” (0dp),您可以将视图尺寸设置为某种比例,例如 16:9。如需启用该比例,请点击 Toggle Aspect Ratio Constraint(图 14 中的标注 1),然后在显示的输入文本中输入 width:height 比例。

如果宽度和高度都设置为“match constraints”,您可以点击 Toggle Aspect Ratio Constraint,选择基于另一个维度的比率。视图检查器通过用实线连接相应的边缘来指明哪个维度被设置为比率。

例如,如果您将两侧都设置为“match constraints”,请点击两次 Toggle Aspect Ratio Constraint,将宽度设置为与高度的比率。整个尺寸由视图的高度决定,视图的高度可以任意定义,如图 15 所示。

调整视图外边距

如需让视图均匀间隔,请点击工具栏中的 Margin ,为您添加到布局中的每个视图选择默认外边距。您对默认外边距所做的任何更改只会应用于您在此后添加的视图。

您可以在 Attributes 窗口中控制每个视图的外边距,只需点击表示每个约束条件的线条上的数字即可。在图 14 中,标注 4 显示下外边距设置为 16dp。

图 16. 工具栏的 Margin 按钮。

该工具提供的所有外边距均为 8dp 的系数,以帮助您的视图与 Material Design 的 8dp 方形网格建议保持一致。

使用链控制线性组

图 17. 包含两个视图的水平链。

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

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

链可以采用以下几种样式之一:

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

链的“头”视图(水平链中最左边的视图(采用从左到右的布局)和垂直链中的最顶部的视图)以 XML 格式定义链的样式。不过,您可以通过选择链中的任意视图并点击该视图下方显示的链按钮 ,在 spreadspread Internalpacked 之间切换。

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

  1. 选择要包含在链中的所有视图。
  2. 右键点击其中一个视图。
  3. 选择
  4. 选择 Center HorizontallyCenter Vertically

视频 4. 创建水平链。

使用链时,请注意以下几点:

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

自动创建约束条件

您可以将每个视图移到布局编辑器中的所需位置,然后点击 Infer Constraints 以自动创建约束条件,而不是在将视图放置到布局中时为其添加约束条件。

Infer Constraints 会扫描布局,以确定所有视图的最有效约束条件集。它会将视图约束到其当前位置,同时提供灵活性。您可能需要进行调整,让布局能够按预期响应不同的屏幕尺寸和屏幕方向。

Autoconnect to Parent 是一项您可以启用的单独功能。启用此功能并将子视图添加到父视图后,当您将视图添加到布局中时,此功能会自动为每个视图创建两个或更多约束条件,但仅在适合将视图约束到父布局时。Autoconnect 不会为布局中的其他视图创建约束条件。

Autoconnect 在默认情况下处于停用状态。点击布局编辑器工具栏中的 Enable Autoconnection to Parent ,即可启用该功能。

关键帧动画

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>

其他资源

Sunflower 演示版应用中使用了 ConstraintLayout