ConstraintLayout 約束佈局 (1)

一 約束佈局簡介

A ConstraintLayout is a android.view.ViewGroup which allows you to position and size widgets in a flexible wayandroid

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3'bash

約束佈局做爲Google大力推廣的佈局方式,可謂集萬千寵愛於一身:app

  • 高性能:界面基本能夠一層佈局搞定,不用嵌套多層佈局。
  • 功能強大:有百分比佈局、設置自身寬高比例,各類輔助組件。
  • 可拖拽編輯:這就不推薦了,須要的可查看郭神文章Android新特性介紹,ConstraintLayout徹底解析

缺點就是,用了約束佈局以後,再也切不回之前的佈局方式了,重度依賴ide

TODO: 2.0的自定義Helper和motionLayout還未明白,先不記錄,後期學完再補進來。佈局

二 基本佈局方式

2.1 基本佈局屬性

從名字上看,可能大體明白其對齊方式(就是自身的哪一邊對齊目標view的哪一邊),就不詳細列出所有了。性能

ConstraintLayout 對齊方式
app:layout_constraintStart_toStartOf 自身左邊與目標左邊對齊
app:layout_constraintStart_toEndOf 自身左邊與目標右邊對齊
... ...

對於約束佈局的定位對齊方式,能夠將其想象成高中物理的拉力app:layout_constraintStart_toStartOf表示自身的左邊被目標view的左邊拉住;app:layout_constraintEnd_toEndOf表示自身的右邊被目標view的右邊拉住flex

假設一個button要在父佈局居中顯示,則只須要它的上下左右被父佈局的上下左右拉住便可:ui

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我要居中顯示"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        />
</androidx.constraintlayout.widget.ConstraintLayout>
複製代碼

2.2 偏移量bias

約束佈局還提供了一個設置偏移量的功能。好比,如今居中顯示煩了,要往左邊靠一點。能夠理解爲左邊的拉力我要大一點,則能夠經過設置bias來實現。spa

  • app:layout_constraintHorizontal_bias:水平方向上的偏移,取值範圍:0f-1.0f(0爲靠向最左邊,1爲靠到最右邊)
  • app:layout_constraintVertical_bias:垂直方向上的偏移,取值範圍:0f-1.0f(0爲靠在最上邊,1爲最下邊)
...
app:layout_constraintHorizontal_bias="0.2"
...

複製代碼

2.3 margin

設置margin與以前方式同樣,都是android:layout_marginXXX,不一樣的是,要使margin生效,必須有同一方向上的約束拉力。即要marginBottom就必須有一個app:layout_constraintBottom_toXXXOf.net

三 大小設置

3.1 約束佈局下的三種大小設置模式

約束佈局下,不使用MATCH_PARENT,保留WRAP_CONTENT,同時還可使用約束佈局特有的三種大小設置模式:spread,wrap,percent;三者能夠經過app:layout_constraintWidth_defaultapp:layout_constraintHeight_default設置。

:使用約束佈局的大小設置,須要先將要設置的寬或高指定爲0dp

3.1.1 spread:默認

app:layout_constraintXXX_default="spread",是默認的,表示在約束條件下的最大尺寸;

一樣能夠將其想象成拉力,假設view寬爲0dp,左邊與ViewA右邊對齊,右邊與ViewB左邊對齊,則左邊會被拉伸至ViewA的右邊,右邊會被拉伸至ViewB的左邊:(下面的圖爲了顯示清楚,故意加了margin)

...
 <TextView
        android:id="@+id/viewA"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:gravity="center"
        android:textSize="20sp"
        android:text="ViewA"
        android:textColor="@android:color/white"
        android:background="@android:color/holo_red_dark"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:id="@+id/viewB"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:gravity="center"
        android:textSize="20sp"
        android:text="viewB"
        android:textColor="@android:color/white"
        android:background="@android:color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />
    <Button
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:text="我要被拉伸"
        android:textSize="20sp"
        android:layout_marginStart="30dp"
        android:layout_marginEnd="30dp"
        app:layout_constraintStart_toEndOf="@id/viewA"
        app:layout_constraintEnd_toStartOf="@id/viewB"
        app:layout_constraintTop_toTopOf="parent"
        />
    ...
複製代碼

3.1.2 wrap模式

app:layout_constraintWidth_default="wrap":自適應大小,但不超過約束條件下的最大尺寸。

仍是上面的例子,若是是傳統的WRAP_CONTENT,當內容較大時,會直接超過約束條件,如圖:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拉伸我要被要被拉伸我要被拉伸"
        android:textSize="20sp"
        app:layout_constraintWidth_default="wrap"
        app:layout_constraintStart_toEndOf="@id/viewA"
        app:layout_constraintEnd_toStartOf="@id/viewB"
        app:layout_constraintTop_toTopOf="parent"
        />
複製代碼

當指定爲wrap模式,則會保留約束條件:

...
android:layout_width="0dp"
app:layout_constraintWidth_default="wrap"
...
複製代碼

另外,針對傳統的WRAP_CONTENT模式,約束佈局有額外的屬性,使得它也能保留約束條件:app:layout_constrainedWidth="true"此時與wrap模式同樣的效果。

3.1.3 percent模式

app:layout_constraintWidth_default="percent":以父佈局的百分比做爲自身的大小,即百分比佈局。(利用次特性可解決部分常見適配),經過app:layout_constraintWidth_percent設置比例大小,0f-1.0f

...
android:layout_width="0dp"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.8"
...
複製代碼

3.1.4 指定寬高比(ratio)

app:layout_constraintDimensionRatio:約束佈局還能夠指定view自身寬高比例。

其值能夠爲「width:height」或者「width/height」,如寬是高的2倍:app:layout_constraintDimensionRatio="2:1"app:layout_constraintDimensionRatio="2"

要使其生效的話,必須將對應的寬或高設置爲0dp。例如寬爲0dp,高爲10dp,app:layout_constraintDimensionRatio=2:1以後,寬最終爲20dp;

若是寬高兩項都爲0dp的話,則最終尺寸會設置爲符合約束的最大尺寸,同時保持設置的比例。這樣有時結果不是咱們所想要的,咱們能夠指定寬或高(H,W)哪一邊約束條件來肯定尺寸,哪一邊經過比例來肯定尺寸;

app:layout_constraintDimensionRatio="W,2:1"則指明寬按照比例來計算最終尺寸,而高則根據約束條件;app:layout_constraintDimensionRatio="H,2:1"則相反。可自行體驗下。

3.1.5 最大和最小值(max/min)

這個就和屬性名稱同樣的意思啦:

  1. app:layout_constraintHeight_min
  2. app:layout_constraintWidth_min
  3. app:layout_constraintHeight_max
  4. app:layout_constraintWidth_max

四 鏈(chain)佈局

鏈式佈局是約束佈局經常使用的另外一個強大功能,能夠快速實現等分佈局等,還能夠實現相似LinearLayout佈局的weight比重功能。

約束鏈具備三種模式:

  • spread:view之間均勻分佈
  • spread_inside:除了約束鏈的頭部和尾部貼在兩邊,其他均勻分佈
  • packed:全部view緊貼在一塊兒,默認居中

(gif是在medium上看到的,原連接一會兒沒找到)

4.1 構建約束鏈

約束鏈具備水平方向和垂直方向的。

將多個view構建成一個水平方向的鏈,則須要多個view在水平方向上兩兩約束

<ImageView
        android:id="@+id/img1"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/img1"
        android:scaleType="centerCrop"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/img2"
        android:layout_marginTop="20dp"
        />

    <ImageView
        android:id="@+id/img2"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/img2"
        android:scaleType="centerCrop"
        app:layout_constraintStart_toEndOf="@id/img1"
        app:layout_constraintTop_toTopOf="@id/img1"
        app:layout_constraintEnd_toStartOf="@+id/img3"
        />

    <ImageView
        android:id="@+id/img3"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/img3"
        android:scaleType="centerCrop"
        app:layout_constraintStart_toEndOf="@id/img2"
        app:layout_constraintTop_toTopOf="@id/img1"
        app:layout_constraintEnd_toEndOf="parent"
        />
複製代碼

這就建成了一條水平線上的約束鏈。垂直方向上的也相似,保持view 之間在垂直方向上兩兩約束便可。

4.2 改變鏈的模式

鏈的第一個view,即img1,稱爲鏈頭。在鏈頭上設置app:layout_constraintHorizontal_chainStyleapp:layout_constraintVertical_chainStyle可分別設置水平或垂直鏈的模式。

  1. spread(默認):
app:layout_constraintVertical_chainStyle="spread"
複製代碼

  1. spread_inside:

  1. packed:

4.3 在鏈上設置權重(weight)

app:layout_constraintHorizontal_weight/app:layout_constraintVertical_weight分別設置水平和垂直鏈上的權重

注意:要使其生效,必須將大小指定爲0dp

<ImageView
        android:id="@+id/img1"
        android:layout_width="0dp"
        android:layout_height="100dp"
        app:layout_constraintHorizontal_weight="1"
        ...
        />

    <ImageView
        android:id="@+id/img2"
        android:layout_width="0dp"
        android:layout_height="100dp"
        app:layout_constraintHorizontal_weight="2"
        ...
        />

    <ImageView
        android:id="@+id/img3"
        android:layout_width="0dp"
        android:layout_height="100dp"
        app:layout_constraintHorizontal_weight="2"
       ...
        />
複製代碼

五 圓形佈局

約束佈局還提供一種炫酷的圓形佈局:能夠以角度和距離約束某個view中心相對於另外一個view的中心。

實際開發沒怎麼用過,實現相似鐘錶效果就很不錯。

  • app:layout_constraintCircle : 圓心,值是某個view的id

  • app:layout_constraintCircleRadius : 半徑

  • app:layout_constraintCircleAngle :角度,值是從0-360,0是指整上方

<ImageView
        android:id="@+id/img4"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/img3"
        android:scaleType="centerCrop"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        />

    <ImageView
        android:id="@+id/img5"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/img5"
        android:scaleType="centerCrop"
        app:layout_constraintCircle="@id/img4"
        app:layout_constraintCircleAngle="45"
        app:layout_constraintCircleRadius="180dp"
        />
複製代碼

六 輔助組件

約束佈局還提供了一系列組件使得能夠更靈活地佈局。如Group能夠同時控制一堆view的可見性,GuideLine能夠虛擬一條輔助線等等。

6.1 Group:控制可見性

Group是一個虛擬視圖,能夠經過把viewapp:constraint_referenced_ids放到裏面,統一同時控制這些view的可見性。

<androidx.constraintlayout.widget.Group
        android:id="@+id/mGroup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="img1,img2"
        />
複製代碼

Group也是繼承自view的,直接在代碼中像普通view同樣設置可見性,就能同時控制到img1,img2的可見性了。

mGroup.visibility = View.VISIBLE
複製代碼

6.2 Guideline:輔助線

Guideline至關於一條虛擬輔助線,可分爲水平、垂直線,幫助定位的。

考慮一個需求,兩個view,要在屏幕中間一左一右,如果傳統定位,就弄一個線性佈局放兩個view,而後再把這個線性佈局居中。使用約束佈局,能夠在屏幕正中間放一條虛擬垂直輔助線,而後兩個view分別放在這條線左右便可。

  • android:orientation設置垂直仍是水平
  • app:layout_constraintGuide_percent經過百分比設置位置,取值能夠是0f-1.0f或0%-100%
  • app:layout_constraintGuide_begin設置相對start/top的偏移量,dp
  • app:layout_constraintGuide_end設置相對end/bottom的偏移量,dp
<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"
        />

    <ImageView
        android:id="@+id/img1"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/img1"
        android:scaleType="centerCrop"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="50dp"
        />

    <ImageView
        android:id="@+id/img2"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/img2"
        android:scaleType="centerCrop"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toEndOf="@+id/guideline"
        android:layout_marginTop="20dp"
        android:layout_marginStart="50dp"
        />
複製代碼

6.3 Barrier:獲取邊界範圍

Barrier能夠獲取多個約束view的邊界。能夠得到所包含的多個view的最左最右等邊界。

考慮這樣一個需求,左邊是text1和text2,另外有一個view,必須放在這兩個text的右邊

<TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"
        android:text="text1"
        android:background="@android:color/black"
        android:textColor="@android:color/white"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="10dp"
        />
    <TextView
        android:id="@+id/text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"
        android:text="text2"
        android:background="@android:color/holo_red_dark"
        android:textColor="@android:color/white"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/text1"
        android:layout_marginTop="20dp"
        />

        <ImageView
            android:id="@+id/img1"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@drawable/img1"
            android:scaleType="centerCrop"
            app:layout_constraintTop_toTopOf="@id/text1"
            app:layout_constraintBottom_toBottomOf="@id/text2"
            app:layout_constraintStart_toEndOf="@id/text1"
            android:layout_marginStart="50dp"
            />
複製代碼

由於img1只能有一個描點,因此以text1爲約束點。但text一、text2寬度時動態的,當text2文本過長時,會發生遮擋。

此時barrier就派上用場了,它能夠動態獲取text一、text2的最右側,這樣只要img1依賴改成Barrier的最右側,就能夠解決問題了

  • app:barrierDirection,取值有top、bottom、left、right、start、end,用於設置獲取的是哪側,好比上面的例子,獲取的是最右側start/right。
  • app:constraint_referenced_ids設置須要包含的view的id
...
    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="text1,text2"
        app:barrierDirection="end"
        />
    <ImageView
        android:id="@+id/img1"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/img1"
        android:scaleType="centerCrop"
        app:layout_constraintTop_toTopOf="@id/text1"
        app:layout_constraintBottom_toBottomOf="@id/text2"
        app:layout_constraintStart_toEndOf="@id/barrier"
        android:layout_marginStart="50dp"
        />
    ...
複製代碼

未完待續...

約束佈局2.0增長了Layer、State、Flow等高效率組件,因文章篇幅,下一篇補上。

相關文章
相關標籤/搜索