android ConstraintLayout布局 实战:打造复杂界面的最佳实践
ConstraintLayout 的核心思想是通过 约束(Constraints) 来定义视图之间的关系。每个视图都需要在水平和垂直方向上至少有一个约束,否则视图会默认放置在左上角(0,0)位置。
约束的类型
相对于父布局的约束:将视图的边与父布局的边对齐。
加粗样式相对于其他视图的约束:将视图的边与其他视图的边对齐。
引导线(Guideline):一种不可见的辅助线,用于创建动态约束。
屏障(Barrier):根据一组视图的动态位置自动调整约束。
链(Chain):将多个视图在水平或垂直方向上连接起来,形成一种特殊的关系。
在 build.gradle 中添加 ConstraintLayout 的依赖:
dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
}
下面我会通过 详细的示例 来讲解 ConstraintLayout 的使用,涵盖基本布局、引导线、链、屏障、组以及动态修改约束等内容。每个示例都会附带代码和说明。
1. 基本布局示例
目标:实现一个简单的界面,包含两个按钮和一个文本框。
按钮1位于左上角。
按钮2位于右上角。
文本框位于按钮1和按钮2的下方,水平居中。
代码:
<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">
<!-- 按钮1:左上角 -->
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"/>
<!-- 按钮2:右上角 -->
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"/>
<!-- 文本框:水平居中,位于按钮1和按钮2的下方 -->
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Hello, ConstraintLayout!"
android:gravity="center"
android:padding="16dp"
app:layout_constraintTop_toBottomOf="@id/button1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
说明:
按钮1和按钮2分别通过 app:layout_constraintTop_toTopOf 和 app:layout_constraintStart_toStartOf(或 **app:layout_constraintEnd_toEndOf)**约束到父布局的顶部和左右边缘。
文本框通过 app:layout_constraintTop_toBottomOf 约束到按钮1的下方,并通过 app:layout_constraintStart_toStartOf 和 app:layout_constraintEnd_toEndOf 实现水平居中。
2. 引导线(Guideline)示例
目标:使用垂直引导线将界面分为左右两部分,左侧放置一个按钮,右侧放置一个文本框。
代码:
<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">
<!-- 垂直引导线,位于父布局的50%位置 -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"/>
<!-- 按钮:位于引导线左侧 -->
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/guideline"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"/>
<!-- 文本框:位于引导线右侧 -->
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="This is the right side."
android:gravity="center"
android:padding="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
说明:
引导线通过 app:layout_constraintGuide_percent=“0.5” 将界面分为左右两部分。
按钮的 app:layout_constraintEnd_toStartOf 约束到引导线,文本框的 app:layout_constraintStart_toEndOf 约束到引导线。
3. 链(Chain)示例
目标:创建水平链,包含三个按钮,均匀分布。
代码:
<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">
<!-- 按钮1:链的起始 -->
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 1"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/button2"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"/>
<!-- 按钮2:链的中间 -->
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 2"
app:layout_constraintStart_toEndOf="@id/button1"
app:layout_constraintEnd_toStartOf="@id/button3"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"/>
<!-- 按钮3:链的结束 -->
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 3"
app:layout_constraintStart_toEndOf="@id/button2"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
说明:
通过 app:layout_constraintHorizontal_chainStyle=“spread” 设置链的类型为均匀分布。
每个按钮通过 app:layout_constraintStart_toEndOf 和 app:layout_constraintEnd_toStartOf 连接到下一个按钮。
4. 屏障(Barrier)示例
目标:根据两个按钮的动态宽度,创建一个屏障,使文本框始终位于较长的按钮下方。
代码:
<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">
<!-- 按钮1 -->
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Short Button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"/>
<!-- 按钮2 -->
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Longer Button Text"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/button1"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"/>
<!-- 屏障:位于按钮1和按钮2的结束边 -->
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="button1,button2"/>
<!-- 文本框:位于屏障下方 -->
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="This is below the longer button."
android:gravity="center"
android:padding="16dp"
app:layout_constraintTop_toBottomOf="@id/barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
说明:
屏障通过 app:barrierDirection=“end” 动态调整位置,始终位于按钮1和按钮2的结束边。
文本框通过 app:layout_constraintTop_toBottomOf 约束到屏障的下方。
5. 动态修改约束
目标:在代码中动态修改按钮的位置。
代码:
ConstraintLayout constraintLayout = findViewById(R.id.constraintLayout);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
// 将按钮1移动到父布局的右下角
constraintSet.connect(R.id.button1, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 16);
constraintSet.connect(R.id.button1, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 16);
constraintSet.applyTo(constraintLayout);
说明:
使用 ConstraintSet 动态修改按钮的约束,将其移动到右下角。
通过这些示例,你可以掌握 ConstraintLayout 的核心用法和高级功能。