使用 ConstraintLayout 构建自适应界面 Android Jetpack 的一部分。

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

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

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

本页介绍了如何在 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 和 Infer 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. 为布局文件输入名称,并为 Root Tag 输入“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 所示。当视图大小设置为“fixed”或“wrap content”时,效果最明显,在这种情况下,视图在约束条件之间居中。如果您希望视图扩展其尺寸以满足约束条件,请将尺寸切换为“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 窗口,然后设置 请稍后再试。

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

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

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

图 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_boundWidth
    • 将此项设为 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 here:第一个和最后一个视图固定在链两端的约束条件上,其余视图均匀分布。
  3. Weighted:当链设置为 spreadspread into 时,您可以通过将一个或多个视图设置为“match constraints” (0dp) 来填充剩余空间。默认情况下,空格会在设置为“match constraints”的每个视图之间均匀分布,但您可以使用 layout_constraintHorizontal_weightlayout_constraintVertical_weight 属性为每个视图分配重要性权重。其工作原理与线性布局中的 layout_weight 相同:权重值最高的视图将获得最多空间,而权重相同的视图将获得相同量的空间。
  4. Packed:在考虑外边距后,视图会打包在一起。您可以通过更改链的“头”视图偏差来调整整个链的偏差(向左或向右,向上或向下)。

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

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

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

视频 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