Android ConstraintLayout從入門到精通

一、前言

    大家都知道,在Android中是通過佈局來定義應用中的界面結構,隨着版本的更新迭代,有些佈局類型因爲適用性退出了歷史的舞臺,例如:AbsoluteLayout。也增了一些佈局類型,例如本文提到的 ConstraintLayout (ConstraintLayout 是 Android Jetpack 的一部分)。

    在 Android 開發過程中,針對複雜的佈局,以往的實現方式只能通過多種類型的佈局疊加組合實現,但是這樣存在一個問題,就是佈局的嵌套層次過多,會影響UI的繪製性能,降低應用的品質。 ConstraintLayout 可以在無嵌套視圖組的前提下,實現扁平視圖層次結構,創建複雜的大型佈局。它與 RelativeLayout 相似,內部所有的視圖均根據同級視圖與父佈局之間的關係進行佈局,但其靈活性要遠高於 RelativeLayout。

二、ConstraintLayout 的使用

2.1 引入依賴

    ConstraintLayout 是 Android Jetpack 的一部分,需要額外引入依賴包才能使用,在項目程序模塊下的 build.gradle 文加下增加依賴。

// support包引入,如果項目使用其他support包,使用這個
implementation 'com.android.support.constraint:constraint-layout:1.1.3'

// androidx包引入,如果項目使用androidx時使用,跟support包引入只能選其一,否則會衝突
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta7'

注意事項:確保您的 maven.google.com 代碼庫已在模塊級 build.gradle 文件中聲明(較新版的AndroidStudio新建項目都會自動加入,如果是很老的舊項目,請檢查代碼庫配置)。

2.2 在佈局中使用

    因爲 ConstraintLayout 是 Android Jetpack 的一部分,所以引用以及屬性使用,都跟系統控件有些不一樣

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

</android.support.constraint.ConstraintLayout>

說明:

  1. 非系統控件,在佈局文件中使用需要使用類名全名(這跟自定義控件一樣);
  2. 非系統控件,自定義的屬性需要添加 xmlns:app="http://schemas.android.com/apk/res-auto" 引入屬性(其中 app 是可以任意命名,但是不能用 android,因爲這個已被系統使用。)

2.3 ConstraintLayout 的約束詳解

    ConstraintLayout 佈局主要通過相互約束來控制佈局的,下面將會詳細介紹下這些約束。

2.3.1 相對位置約束

    相對位置約束是指一個控件跟父級容器或者其他同級控件之間的相對位置,相對位置有以下幾個要素(如下圖所示):

  • 水平方向:left(左邊)、right(右邊)、start(起始邊)、end(結束邊)
  • 垂直方向:top(上邊)、bottom(下邊)、text baseline(文字基線,這個只對有文本的控件有效)

相對位置要素

說明:其實 leftstartrightend 是相同的。



控制相對位置約束的屬性有以下這些:

  • layout_constraintLeft_toLeftOf:控件左邊與目標控件左邊的約束條件
  • layout_constraintLeft_toRightOf:控件左邊與目標控件右邊的約束條件
  • layout_constraintRight_toLeftOf:控件右邊與目標控件左邊的約束條件
  • layout_constraintRight_toRightOf:控件右邊與目標控件右邊的約束條件
  • layout_constraintTop_toTopOf:控件上邊邊與目標控件上邊邊的約束條件
  • layout_constraintTop_toBottomOf:控件上邊與目標控件下邊的約束條件
  • layout_constraintBottom_toTopOf:控件下邊與目標控件上邊的約束條件
  • layout_constraintBottom_toBottomOf:控件下邊與目標控件下邊的約束條件
  • layout_constraintBaseline_toBaselineOf:控件文字基線與目標控件文字基線的約束條件
  • layout_constraintStart_toEndOf:控件開始邊與目標控件結束邊的約束條件
  • layout_constraintStart_toStartOf:控件開始邊與目標控件開始邊的約束條件
  • layout_constraintEnd_toStartOf:控件結束邊與目標控件開始邊的約束條件
  • layout_constraintEnd_toEndOf:控件結束邊與目標控件結束邊的約束條件

示例

<?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"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintTop_toBottomOf="@+id/btn1"/>

</androidx.constraintlayout.widget.ConstraintLayout>

效果:
相對位置約束

注意事項:相對位置約束的屬性在使用過程中,部分屬性之間是衝突的,在使用過程中要特別注意。例如:layout_constraintLeft_toLeftOflayout_constraintLeft_toRightOf,一個控件的左邊不可能同時跟目標控件的左邊和右邊存在約束。

2.3.2 相對間距

    ConstraintLayout 的間距爲什麼說是相對的呢?因爲設置的間距都是相對約束的目標控件的間距,如下圖所示:Button2 左邊對齊 Button1 的右邊,所以,當 Button2 設置左間距 30dp 時,是相對 Button1 的右邊。

相對間距

注意:如果控件居中顯示,設置了間距之後,居中的位置也會相對變化(會線除去間距的位置之後再居中),關於在 ConstraintLayout 中居中的相關內容參考: 居中對齊

設置間距屬性如下:

  • android:layout_marginStart
  • android:layout_marginEnd
  • android:layout_marginLeft
  • android:layout_marginTop
  • android:layout_marginRight
  • android:layout_marginBottom

說明:設置間距的屬性使用的是框架自帶屬性,不是 ConstraintLayout 自定義屬性,所以以 android 爲命名空間。

2.3.3 關聯控件隱藏(View.GONE)時的間距

    關聯控件隱藏(View.GONE)時的間距,跟相對間距類似,不同的是,他是在關聯控件隱藏(View.GONE)之後纔會生效。

示例:

<?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"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintTop_toBottomOf="@+id/btn1"
        android:layout_marginLeft="30dp"
        android:visibility="visible"/>
    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintRight_toLeftOf="@+id/btn2"
        app:layout_constraintTop_toBottomOf="@+id/btn2"
        android:layout_marginTop="30dp"
        app:layout_goneMarginTop="100dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

效果:
關聯控件隱藏間距

以上示例中,當 Button2 隱藏(gone)之後,Button3的上間距由原來的30dp變爲了50dp,至於爲什麼Button3的位置會發生改變,這個參考:當依賴的控件隱藏(View.GONE)之後

2.3.4 居中對齊

    在ConstraintLayout裏面的控件都是靠關聯約束進行控制的,那麼如何實現居中對齊呢?實現起來其實也很簡單,只需兩個方向同時設置關聯控件就可以實現在兩個關聯點之間居中。

  • 水平居中:同時設置左、右兩邊的關聯約束
  • 垂直居中: 同時設置上、下兩邊的關聯約束

示例:

<?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"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

效果:
居中對齊

說明:如果控件設置了間距,那麼居中對齊的時候會除去設置的間距之後再居中,以上的例子中,雖然Button1的左邊是跟父容器的左邊關聯,但是如果設置了左間距爲100dp的話,那麼Button1水平方向是父容器左邊100dp處跟右邊居中。

2.3.5 偏斜對齊

    偏斜對齊是當控件同時設置了兩邊的關聯之後,控件要往某一邊進行偏斜(靠近),偏斜值是一個 float 類型數字,取值 0 ~ 1.0 之間(其實 居中對齊 就是偏斜值爲 0.5 的特例)。偏斜分爲水平和垂直兩個方向。

  • layout_constraintHorizontal_bias:水平偏斜
  • layout_constraintVertical_bias:垂直偏斜

示例:

<?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"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.3"
        app:layout_constraintVertical_bias="0.4"/>
</androidx.constraintlayout.widget.ConstraintLayout>

效果:
偏斜對齊

2.3.6 圓圈佈局

    圓圈佈局,就是空間圍繞關聯控件,以指定的半徑、角度進行佈局。例如:你可以用圓圈佈局輕鬆實現一個組合控件圓形錶盤(而不是自定義控件)。

圓圈佈局主要有三個屬性:

  • layout_constraintCircle :關聯的控件(也就是圓心所在的控件)
  • layout_constraintCircleRadius: 圓形半徑(控件中心點與所關聯控件中心點之間的距離)
  • layout_constraintCircleAngle: 角度(0~360度,正上方爲0度)

示例(圓形錶盤):

<?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"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintVertical_bias="0.5"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="12"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="0"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="1"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="30"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="2"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="60"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="3"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="90"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="4"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="120"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="5"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="150"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="6"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="180"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="7"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="210"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="8"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="240"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="9"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="270"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="10"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="300"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="11"
        app:layout_constraintCircle="@+id/btn1"
        app:layout_constraintCircleRadius="100dp"
        app:layout_constraintCircleAngle="330"
        android:textSize="16sp"
        android:textColor="@android:color/black"
        android:visibility="visible"/>
</androidx.constraintlayout.widget.ConstraintLayout>

效果:
圓圈佈局實現圓形錶盤

2.3.7 當依賴的控件隱藏(View.GONE)之後

    在 ConstraintLayout 中,對控件的隱藏(View.GONE)有着獨特的處理。通常,如果一個控件隱藏(View.GONE)之後,它不會在展示,並且不在屬於佈局的一部分。但是,在佈局的計算過程中,隱藏的控件仍舊是佈局中的一部分,但是它又有着重要的區別。

  • 在整個佈局中,隱藏的控件尺寸會被當做0(基本上可以當做是一個點);
  • 如果隱藏的控件關聯了其他控件,關聯關係依舊存在,但是所有的間距會變爲0;
  • 如果隱藏的控件被其他控件關聯,關聯關係和間距都會不變,但是關聯改隱藏控件的的控件,顯示位置會發生變化。

控件隱藏行爲

講解:當Button1 隱藏時,在佈局計算時,隱藏的 Button1 相當於一個點,並且跟父容器的關聯關係不變,如果 Button1 設置了間距,也將會不起作用,對於關聯了 Button1 的 Button2,間距和關聯不會改變,但是由於 Button1 的計算屬性發生變化,Button2 的位置會發生相應的變化。

2.3.8 像素約束

2.3.8.1 ConstraintLayout 裏的控件尺寸

    在 ConstraintLayout 中,控件的尺寸通過 android:layout_widthandroid:layout_height 兩個屬性設置。

  • 固定數值
  • 使用 WRAP_CONTENTE,讓控件計算出自身的尺寸
  • 使用 “0dp”(等同於使用 MATCH_CONSTRAINT),鋪滿父容器

2.3.8.2 ConstraintLayout 裏的控件最大、最小尺寸

  • android:minWidth:設置控件在佈局中的最小寬度
  • android:minHeight:設置控件在佈局中的最小高度
  • android:maxWidth:設置控件在佈局中的最大寬度
  • android:maxHeight:設置控件在佈局中的最大高度

說明:設置控件在佈局中的最大、最小尺寸,只有在尺寸設置爲自適應(WRAP_CONTENT)時有效。

2.3.8.3 WRAP_CONTENTE 的強制約束(1.1版本庫開始纔有效)

    在 ConstraintLayout 中,如果控件的尺寸設置爲 WRAP_CONTENT ,他將被視爲“文字”尺寸,如果控件內部的文字內容很長,將控件撐開的時候,就會出現控件設置的間距出現異位甚至被擠出到父容器範圍外,這時,你需要使用強制執行約束來限制控件的最終尺寸,可以使用以下屬性設置強制執行約束:

  • app:layout_constrainedWidth=”true|false” :水平方向強制執行約束
  • app:layout_constrainedHeight=”true|false” :垂直方向強制執行約束

示例:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1222222222222222222222222222222222222222222222222222222222"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintVertical_bias="0.5"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginLeft="50dp"/>
</android.support.constraint.ConstraintLayout>

效果:
在這裏插入圖片描述

2.3.8.4 MATCH_CONSTRAINT 尺寸的妙用(1.1版本開始)

    在設置尺寸時,對於 MATCH_PARENTWRAP_CONTENT 我們特別熟悉,但是 MATCH_CONSTRAINT 是什麼鬼?在 XML 佈局文件中,也並不能使用這個類型。我們查看 ConstraintLayout.LayoutParams 類的時候就可以看到這個字段 ConstraintLayout.LayoutParams.MATCH_CONSTRAINT = 0,在 XML 佈局文件中,使用這個類型的尺寸只需要設置爲 0dp,這個是不是很熟悉呢?在 LinearLayout 中,使用 android:layout_weight 屬性的時候是不是也這麼幹過。

2.3.8.4.1 如何讓控件鋪滿父容器

    當筆者第一次接觸 ConstraintLayout 的時候,我有一個疑問,就是如何讓一個在其中的控件不慢整個容器呢?顯然通過四邊添加跟父容器的約束是不可行的(這種做法只是居中),知道發現了 MATCH_CONSTRAINT 這個尺寸類型,才豁然開朗,對於 MATCH_CONSTRAINT 而言,它的默認值就是鋪滿父容器。

示例:

<?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"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="Button122"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginLeft="50dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

效果:
鋪滿全屏

注意:如果你使用了邊距(margin),鋪滿全屏的時候會保持着邊距。

2.3.8.4.2 MATCH_CONSTRAINT 約束模式切換

    前面說到,對於 MATCH_CONSTRAINT 而言,它默認值是鋪滿父容器的,其實,ConstraintLayout 還提供了非常靈活的約束模式切換,通過切換不同的模式和屬性組合,可以滿足不同的使用場景要求。 約束模式的切換使用以下屬性:

  • layout_constraintWidth_default :橫向約束模式
  • layout_constraintHeight_default :縱向約束模式

約束模式的取值有:

取值 說明
spread 鋪開全屏(默認)
wrap 根據內容變化控件大小
percent 按比例控制控件大小(默認1,鋪滿父容器)
2.3.8.4.3 約束模式與不同屬性組合的神奇效果

    MATCH_CONSTRAINT 約束可以使用的屬性除了前面介紹的之外,還有以下屬性:

  • 最大與最小

    • layout_constraintWidth_min:橫向最小尺寸
    • layout_constraintHeight_min:縱向最小尺寸
    • layout_constraintWidth_max:橫向最大尺寸
    • layout_constraintHeight_max:縱向最大尺寸
  • 父容器百分比

    • layout_constraintWidth_percent:橫向父容器百分佔比(取值0~1)
    • layout_constraintHeight_percent:縱向父容器百分佔比(取值0~1)
  • 橫縱比

    • layout_constraintDimensionRatio:橫縱尺寸比例(如 1:1 表示橫向和縱向尺寸比例爲1:1,即正方形)
  • 約束模式與屬性的搭配

約束類型 有效的屬性 效果描述 備註
spread layout_constraintWidth_max
layout_constraintHeight_max
layout_constraintDimensionRatio
控制控件橫向和縱向的大小 -
wrap layout_constraintWidth_min
layout_constraintHeight_min
layout_constraintWidth_max
layout_constraintHeight_max
layout_constraintDimensionRatio
分別控制橫向和縱向的最大與最小 如果橫向和縱向都是 wrap,使用 layout_constraintDimensionRatio 屬性時,必須至少指定 layout_constraintWidth_maxlayout_constraintHeight_max 中的一個
percent layout_constraintWidth_percent
layout_constraintHeight_percent
layout_constraintWidth_min
layout_constraintHeight_min
layout_constraintWidth_max
layout_constraintHeight_max
這個百分比的類型,幾乎所有屬性都可用,最大最小值只會在百分比超過靈界點之後生效,例如:如果百分佔比的尺寸小於最小值,則取最小值,如果百分佔比的尺寸大於最大值,則取最大值,但是需要注意的是,如果使用這個類型的約束,最大和最小隻能使用一個,否則就會出現意想不到的問題,如果不是特殊需要,在使用百分比的時候,儘量不要配合最大和最小值。 -

示例:

<?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"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="Button1222222222222222222222222222222222222222222222222"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintWidth_default="wrap"
        app:layout_constraintWidth_min="200dp"
        app:layout_constraintWidth_max="300dp"

        app:layout_constraintHeight_default="percent"
        app:layout_constraintHeight_percent="0.5"
        app:layout_constraintHeight_max="200dp"

        android:layout_marginLeft="50dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

效果:
約束類型跟屬性組合

注意事項:各個屬性的使用千變萬化,對於橫向和縱向使用不同的約束模式也會有不一樣的效果。

2.3.9 鏈控制

    鏈控制線性約束是指在單個方向上(水平或者垂直)實現類似組的行爲,另一個方向可以獨立約束。鏈控制線性約束,實際上就是將一組控件放在一起,相互依賴形成一條鏈。

示例:

<?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/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btn2"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="Button1" />
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintStart_toEndOf="@+id/btn1"
        app:layout_constraintEnd_toStartOf="@+id/btn3"
        app:layout_constraintRight_toLeftOf="@+id/btn3"
        app:layout_constraintTop_toTopOf="@+id/btn1"
        android:visibility="visible"/>
    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintStart_toEndOf="@+id/btn2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@+id/btn2"/>
</androidx.constraintlayout.widget.ConstraintLayout>

效果:
鏈控制線性約束

2.3.9.1 鏈頭

    鏈控制線性約束有一個鏈頭,它是整個鏈的第一個元素(水平是最左一個元素、垂直是最上上的一個元素),在鏈頭上設置屬性可控制鏈條的樣式。

2.3.9.2 鏈的樣式

    通過在鏈頭上設置鏈樣式屬性,可以改變鏈的樣式。

  • layout_constraintHorizontal_chainStyle:水平方向鏈的樣式(默認爲CHAIN_SPREAD)
  • layout_constraintVertical_chainStyle:垂直方向鏈的樣式(默認爲CHAIN_SPREAD)
樣式取值 說明 備註
CHAIN_SPREAD 元素將散佈,每個元素佔有效空間的一個比重並居中 默認樣式,XML中對應 spread ,間距會影響元素的位置
CHAIN_SPREAD_INSIDE 內部元素散佈,前後兩個元素緊靠有效空間的邊沿 XML中對應 spread_inside ,間距會影響元素的位置
CHAIN_PACKED 將鏈中的元素打包在一起居中對齊 XML中對應 packed,間距和偏斜對齊會影響元素的位置

l鏈控制約束

注意事項:鏈會在父容器的有效空間中進行佈局,如果設置了邊距,將會先去掉間距,間距屬性在每個元素都可以生效。對於 CHAIN_PACKED 樣式,偏斜對齊起作用,雖然是在鏈頭中配置,但是生效的效果確實整個打包起來的鏈整體。

2.3.9.3 帶權重的鏈

    鏈的默認行爲是將元素平均分佈在可用空間中。如果一個或多個元素使用MATCH_CONSTRAINT (0dp),則這些元素將使用可用的空白空間(在它們之間平均分配)。元素的權重可以通過以下屬性進行設置:

  • layout_constraintHorizontal_weight:水平方向權重

  • layout_constraintVertical_weight: 垂直方向權重

  • 示例

<?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/btn1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btn2"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintHorizontal_weight="2"
        android:text="Button1" />
    <Button
        android:id="@+id/btn2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintStart_toEndOf="@+id/btn1"
        app:layout_constraintEnd_toStartOf="@+id/btn3"
        app:layout_constraintRight_toLeftOf="@+id/btn3"
        app:layout_constraintTop_toTopOf="@+id/btn1"
        app:layout_constraintHorizontal_weight="3"
        android:visibility="visible"/>
    <Button
        android:id="@+id/btn3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintStart_toEndOf="@+id/btn2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_weight="3"
        app:layout_constraintTop_toTopOf="@+id/btn2"/>
</androidx.constraintlayout.widget.ConstraintLayout>
  • 效果
    Weighted Chain

注意事項:
1. 當且僅當元素的尺寸使用 MATCH_CONSTRAINT (0dp)時,權重屬性才生效;
2. 爲元素添加權重,對鏈樣式沒有要求,因爲鏈樣式只是在元素大小適應內容時的效果,對於權重,是直接鋪滿可用空間。

2.3.10 虛擬輔助對象

    所謂的虛擬輔助對象,就是一種虛擬存在,用來輔助佈局約束的,但是並不會真正在視圖中進行繪製。

2.3.10.1 引導線約束

    引導線(Guideline)就是一種非常廣泛的虛擬輔助對象,引導線不是一個屬性,是一個虛擬輔助對象,添加引導線跟添加控件一樣。引導線分爲水平和垂直兩個方向,使用 android:orietation 屬性控制方向。除了控制方向之外,引導線還有控制位置的的屬性:

  • layout_constraintGuide_begin:距離父容器左側的距離

  • layout_constraintGuide_end:距離父容器右側的距離

  • layout_constraintGuide_percent:在父容器(左側)的百分比例(取值0.0~1.0)

  • 示例

<?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">
    <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.3" />

    <Button
        android:id="@+id/btn4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="30dp"
        android:text="Button4"
        app:layout_constraintLeft_toRightOf=" @+id/guideline"/>
</androidx.constraintlayout.widget.ConstraintLayout>
  • 效果
    引導線

2.3.10.2 屏障約束

    與引導線類似,屏障是一條隱藏的線,可以用它來約束視圖。跟引導線不同的是,屏障不會定義自己的位置;相反,屏障的位置會隨着其中所含視圖的位置而移動。如果您希望將視圖限制到一組視圖而不是某個特定視圖,這就非常有用。屏障約束主要通過 Barrier 類及其屬性進行控制,Barrie 類的屬性有以下幾個:

  • barrierDirection:屏障約束的方向,這個是指約束的內容在屏障的哪個位置

  • constraint_referenced_ids:約束限制的控件id數組,多個用半角逗號隔開

  • 示例代碼:

<?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">
    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierAllowsGoneWidgets="false"
        app:barrierDirection="right"
        app:constraint_referenced_ids="btn5,btn6" />
    <Button
        android:id="@+id/btn5"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:text="Button5"
        app:layout_constraintLeft_toLeftOf="parent"/>
    <Button
        android:id="@+id/btn6"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="Button6"
        app:layout_constrainedWidth="true"
        app:layout_constraintTop_toBottomOf="@+id/btn5" />
    <Button
        android:id="@+id/btn7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button777777777777777777777777777777777777777777777777777777777777777777777777777777777"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/barrier"
        app:layout_constrainedWidth="true"/>
</androidx.constraintlayout.widget.ConstraintLayout>
  • 效果:

屏障約束

屏障約束2

以上的例子中,定義了一個屏障,屏障約束的方向是 right,也就是屏障右邊的內容根據屏障內部的內容變動而動態變化。屏障內部的控件組包含了 button5 和 button6,屏障的位置會根據屏障內部的控件動態變化,從而限制 button7 的佈局變化。

三、編後語

    ConstraintLayout 是一個非常強大的佈局,扁平化的佈局,提高UI的繪製效率,提高性能方面做得非常好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章