约束布局ConstraintLayout 使用

1,333 阅读3分钟

ConstraintLayoutRelativeLayout 相似,其中所有的视图均根据同级视图与父布局之间的关系进行布局,但其灵活性要高于 RelativeLayout,适合创建复杂的大型布局。

官方教程地址:developer.android.google.cn/training/co…

一、基本使用

1.1 添加到项目中

  1. 在项目根目录的 build.gradle文件中声明:

        repositories {
            google()
        }
        
    
  2. 将该库作为依赖项添加到项目的 build.gradle 文件中,如以下示例所示

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.0.4"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha07"
    }
    

1.2 基本使用

约束条件如下:

未命名文件.png

例如下面这个简单的布局,有个按钮位于屏幕左上角

布局1.png

对应的布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <Button
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="test"/>
</androidx.constraintlayout.widget.ConstraintLayout>

其中 parent 代表的是父布局。

注意:在约束性布局中,除非宽高设置成 match_parent,否则其他场景都需要设置 (上 || 下)&& (左 || 右) 的约束条件, 不然编译器会出现报错警告。

二、布局居中

水平居中:

水平居中.png

设置 constraintLeftconstraintRight,根据左右两边约束的视图居中。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <Button
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="test"/>
</androidx.constraintlayout.widget.ConstraintLayout>

垂直居中:

竖直居中.png

设置 constraintTopconstraintBottom,根据上下约束的视图居中。

  <Button
    android:id="@+id/btn"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="test"/>

居中设置偏移比例

设置 layout_constraintVertical_biaslayout_constraintHorizontal_bias

layout_constraintVertical_bias:垂直偏移,范围 0 ~ 1 ,0代表最上方,1代表最下方

layout_constraintHorizontal_bias:水平偏移,范围 0 ~ 1,0代表最左边,1代表最右边

水平偏移.png

垂直偏移.png

三、链条控制线性组

设置 layout_constraintHorizontal_chainStylelayout_constraintVertical_chainStyle 通过链条方式控制一组控件,例如:

链条.png

1 Spread:视图均匀分布,默认该属性

2 Packed:视图打包一起

3 Spread inside:第一个和最后一个视图固定在链条两端

4 Weighted:权重布局,layout_width = 0dp,layout_constraintHorizontal_weight设置权重

链是一组视图,这些视图通过 双向位置约束条件相互链接到一起,即上图 链中的视图可以垂直或水平分布。 由链条的第一个视图设置 chainStyle 即可。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <Button
    android:id="@+id/btn1"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="btn1"/>

  <Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn1"
    app:layout_constraintRight_toLeftOf="@id/btn3"
    app:layout_constraintTop_toTopOf="@id/btn1"
    android:text="btn2"/>

  <Button
    android:id="@+id/btn3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn2"
    app:layout_constraintTop_toTopOf="@id/btn1"
    app:layout_constraintRight_toRightOf="parent"
    android:text="btn3"
    />

  <Button
    android:id="@+id/btn4"
    android:layout_marginTop="64dp"
    app:layout_constraintTop_toBottomOf="@id/btn1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/btn5"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="btn4"
    app:layout_constraintHorizontal_chainStyle="packed"/>

  <Button
    android:id="@+id/btn5"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn4"
    app:layout_constraintRight_toLeftOf="@id/btn6"
    app:layout_constraintTop_toTopOf="@id/btn4"
    android:text="btn5"/>

  <Button
    android:id="@+id/btn6"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn5"
    app:layout_constraintTop_toTopOf="@id/btn4"
    app:layout_constraintRight_toRightOf="parent"
    android:text="btn6"/>

  <Button
    android:id="@+id/btn7"
    android:layout_marginTop="64dp"
    app:layout_constraintTop_toBottomOf="@id/btn4"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/btn8"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="btn7"
    app:layout_constraintHorizontal_chainStyle="spread_inside"/>

  <Button
    android:id="@+id/btn8"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn7"
    app:layout_constraintRight_toLeftOf="@id/btn9"
    app:layout_constraintTop_toTopOf="@id/btn7"
    android:text="btn8"/>

  <Button
    android:id="@+id/btn9"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/btn8"
    app:layout_constraintTop_toTopOf="@id/btn7"
    app:layout_constraintRight_toRightOf="parent"
    android:text="btn9"/>

  <Button
    android:id="@+id/btn10"
    android:layout_marginTop="64dp"
    app:layout_constraintTop_toBottomOf="@id/btn7"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/btn11"
    android:layout_width="0dp"
    app:layout_constraintHorizontal_weight="1"
    android:layout_height="wrap_content"
    android:text="btn10"
    app:layout_constraintHorizontal_chainStyle="spread_inside"/>

  <Button
    android:id="@+id/btn11"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintHorizontal_weight="2"
    app:layout_constraintLeft_toRightOf="@id/btn10"
    app:layout_constraintRight_toLeftOf="@id/btn12"
    app:layout_constraintTop_toTopOf="@id/btn10"
    android:text="btn11"/>

  <Button
    android:id="@+id/btn12"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintHorizontal_weight="2"
    app:layout_constraintLeft_toRightOf="@id/btn11"
    app:layout_constraintTop_toTopOf="@id/btn10"
    app:layout_constraintRight_toRightOf="parent"
    android:text="btn12"/>
</androidx.constraintlayout.widget.ConstraintLayout>

四、引导线约束

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

引导线.png

  <androidx.constraintlayout.widget.Guideline
    android:id="@+id/guideline"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintGuide_percent="0.5"/>

  <Button
    android:id="@+id/btn1"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    android:layout_width="wrap_content"
    app:layout_constraintLeft_toRightOf="@id/guideline"
    android:layout_height="wrap_content"
    android:text="btn1"/>

引导线也是一个控件,只是不会显示出来,orientation 设置垂直或水平方向的引导线,layout_constraintGuide_percent 支持百分比定位引导线,范围 0 ~ 1。

由于 layout_margin 无法设置百分比,我们可以通过 Guideline 替代实现。

五、实现覆盖布局效果

覆盖1.png

上图这种覆盖方式通过布局顺序实现的,btn2 控件布局中在 btn1 控件的下方。

由于 ConstraintLayout 布局中无法设置负数的 margin ,所以如果想覆盖控件的上半部分,可以通过引导线等辅助实现。

覆盖2.png

<androidx.constraintlayout.widget.Guideline
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.5"
    android:id="@+id/space"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />

  <Button
    android:background="@color/colorAccent"
    android:layout_marginTop="32dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/btn1"
    android:text="btn1"
    app:layout_constraintTop_toBottomOf="@id/space"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"/>

  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/btn2"
    android:text="btn2"
    android:layout_marginStart="64dp"
    android:background="@color/reset_totp_indicator_color"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@id/space"/>

六、百分比布局

layout_constraintHeight_percentlayout_constraintWidth_percent 支持通过百分比设置控件的宽高,范围 0 ~ 1 。

百分比.png

  <Button
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    android:text="btn1"
    app:layout_constraintHeight_percent="0.3"
    app:layout_constraintWidth_percent="0.5"
    android:background="@color/reset_totp_indicator_color"/>

layout_widthlayout_height 必须设置成 0dp,才可以生效。

七、约束失效问题

当设置 wrap_content 再设置约束条件时会发现约束条件失效了,例如:

约束失效.png

 <TextView
    android:text="sdfsfdsdfsdfssdfsdfsdfdsfdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdf"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/tv_username"
    android:textColor="@color/find_pass_text_color"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/btn1"
    android:maxLines="1"
    android:ellipsize="end"
    app:layout_constraintHorizontal_chainStyle="packed"
    app:layout_constraintHorizontal_bias="0"
    />

  <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    android:text="btn1"
    app:layout_constraintHeight_percent="0.3"
    app:layout_constraintWidth_percent="0.5"
    android:background="@color/colorAccent"/>

虽然设置了约束条件 app:layout_constraintRight_toLeftOf="@id/btn1",但实际并没有生效。

需要设置 layout_constrainedWidth 为true,修改后效果如下:

约束失效2.png

  <TextView
    android:text="sdfsfdsdfsdfssdfsdfsdfdsfdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdf"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/tv_username"
    android:textColor="@color/find_pass_text_color"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/btn1"
    android:maxLines="1"
    android:ellipsize="end"
    app:layout_constraintHorizontal_chainStyle="packed"
    app:layout_constraintHorizontal_bias="0"
    app:layout_constrainedWidth="true"
    />

  <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    android:text="btn1"
    app:layout_constraintHeight_percent="0.3"
    app:layout_constraintWidth_percent="0.5"
    android:background="@color/colorAccent"/>

八、设置视图最大、最小尺寸

有时候适配不同屏幕时,我们不希望尺寸随着屏幕变大而无限拉伸,这时候可以通过 layout_constraintWidth_maxlayout_constraintHeight_maxlayout_constraintWidth_minlayout_constraintHeight_min 这几个设置宽高的最大或最小值。

需要配合百分比布局 layout_constraintWidth_percent 才可以生效。

限制宽高1.png

  <Button
    android:id="@+id/btn1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    android:text="btn1"
    android:background="@color/colorAccent"
    app:layout_constraintWidth_percent="0.85"
    />

设置宽度最大值为 200dp,修改后显示如下:

限制宽高2.png

  <Button
    android:id="@+id/btn1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    android:text="btn1"
    android:background="@color/colorAccent"
    app:layout_constraintWidth_percent="0.85"
    app:layout_constraintWidth_max="200dp"
    />