ConstraintLayout 和 RelativeLayout 相似,其中所有的視圖均根據(jù)同級(jí)視圖與父布局之間的關(guān)系進(jìn)行布局,但其靈活性要高于 RelativeLayout,適合創(chuàng)建復(fù)雜的大型布局。
官方教程地址:https://developer.android.google.cn/training/constraint-layout
一、基本使用
1.1 添加到項(xiàng)目中
-
在項(xiàng)目根目錄的
build.gradle文件中聲明:repositories { google() } -
將該庫作為依賴項(xiàng)添加到項(xiàng)目的
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 基本使用
約束條件如下:

例如下面這個(gè)簡單的布局,有個(gè)按鈕位于屏幕左上角

對(duì)應(yīng)的布局代碼如下:
<?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 代表的是父布局。
注意:在約束性布局中,除非寬高設(shè)置成 match_parent,否則其他場景都需要設(shè)置 (上 || 下)&& (左 || 右) 的約束條件, 不然編譯器會(huì)出現(xiàn)報(bào)錯(cuò)警告。
二、布局居中
水平居中:

設(shè)置 constraintLeft 和 constraintRight,根據(jù)左右兩邊約束的視圖居中。
<?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>
垂直居中:

設(shè)置 constraintTop 和 constraintBottom,根據(jù)上下約束的視圖居中。
<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"/>
居中設(shè)置偏移比例
設(shè)置 layout_constraintVertical_bias 和 layout_constraintHorizontal_bias
layout_constraintVertical_bias:垂直偏移,范圍 0 ~ 1 ,0代表最上方,1代表最下方
layout_constraintHorizontal_bias:水平偏移,范圍 0 ~ 1,0代表最左邊,1代表最右邊


三、鏈條控制線性組
設(shè)置 layout_constraintHorizontal_chainStyle 或 layout_constraintVertical_chainStyle 通過鏈條方式控制一組控件,例如:

1 Spread:視圖均勻分布,默認(rèn)該屬性
2 Packed:視圖打包一起
3 Spread inside:第一個(gè)和最后一個(gè)視圖固定在鏈條兩端
4 Weighted:權(quán)重布局,layout_width = 0dp,layout_constraintHorizontal_weight設(shè)置權(quán)重
鏈?zhǔn)且唤M視圖,這些視圖通過 雙向位置約束條件相互鏈接到一起,即上圖 鏈中的視圖可以垂直或水平分布。
由鏈條的第一個(gè)視圖設(shè)置 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>
四、引導(dǎo)線約束
可以添加垂直或水平的引導(dǎo)線來約束視圖,并且應(yīng)用用戶看不到該引導(dǎo)線。 可以根據(jù)相對(duì)于布局邊緣的 dp 單位或百分比在布局中定位引導(dǎo)線。

<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"/>
引導(dǎo)線也是一個(gè)控件,只是不會(huì)顯示出來,orientation 設(shè)置垂直或水平方向的引導(dǎo)線,layout_constraintGuide_percent 支持百分比定位引導(dǎo)線,范圍 0 ~ 1。
由于 layout_margin 無法設(shè)置百分比,我們可以通過 Guideline 替代實(shí)現(xiàn)。
五、實(shí)現(xiàn)覆蓋布局效果

上圖這種覆蓋方式通過布局順序?qū)崿F(xiàn)的,btn2 控件布局中在 btn1 控件的下方。
由于 ConstraintLayout 布局中無法設(shè)置負(fù)數(shù)的 margin ,所以如果想覆蓋控件的上半部分,可以通過引導(dǎo)線等輔助實(shí)現(xiàn)。

<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_percent 和 layout_constraintWidth_percent 支持通過百分比設(shè)置控件的寬高,范圍 0 ~ 1 。

<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_width 和 layout_height 必須設(shè)置成 0dp,才可以生效。
七、約束失效問題
當(dāng)設(shè)置 wrap_content 再設(shè)置約束條件時(shí)會(huì)發(fā)現(xiàn)約束條件失效了,例如:

<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"/>
雖然設(shè)置了約束條件 app:layout_constraintRight_toLeftOf="@id/btn1",但實(shí)際并沒有生效。
需要設(shè)置 layout_constrainedWidth 為true,修改后效果如下:

<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"/>
八、設(shè)置視圖最大、最小尺寸
有時(shí)候適配不同屏幕時(shí),我們不希望尺寸隨著屏幕變大而無限拉伸,這時(shí)候可以通過 layout_constraintWidth_max ,layout_constraintHeight_max,layout_constraintWidth_min,layout_constraintHeight_min 這幾個(gè)設(shè)置寬高的最大或最小值。
需要配合百分比布局 layout_constraintWidth_percent 才可以生效。

<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"
/>
設(shè)置寬度最大值為 200dp,修改后顯示如下:

<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"
/>