使用 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 和 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-alpha07"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha07"
    }
    

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha07")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha07")
    }
    
  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 connect 按钮之一,如图 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 的“end”处(或在从左到右的布局中位于右侧)。屏障根据视图 A 或视图 B 的右侧是最右侧来移动。

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

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

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

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

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

图 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 Internal 时,您可以通过将一个或多个视图设置为“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