1. 程式人生 > >《Android 基礎(四十八)》ConstrainLayout【譯】

《Android 基礎(四十八)》ConstrainLayout【譯】

原文地址

介紹

ConstrainLayout是一個允許開發者靈活地設定控制元件的位置和大小的ViewGroup。 只要你使用的Android系統版本在9以上,你便可以通過新增依賴的方式來使用ConstrainLayout.

使用

新增依賴

implementation ‘com.android.support.constraint:constraint-layout:1.1.3’

常用約束

相對定位(Relative positioning)

相對定位是ConstrainLayout構建佈局的一種基本方式。相對定位約束可以幫助我們通過一個給定的Widget來擺放另外一個Widget。我們可以在橫縱兩個方向上約束Widget。

橫向: left, right, start and end sides 縱向: top, bottom sides and text baseline

一般思路是將一個 Widget的給定側約束到其他Widget的另一側。

比如,把button B放置到button A的右側(下圖)

alt

實現可以如下

<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
 app:layout_constraintLeft_toRightOf="@+id/buttonA" />

上述程式碼就是告訴系統,我們想讓button B的左側約束到button A的右側。這樣一個位置約束意味著,系統將讓button A的右側和button B的左側在X軸上具有相同的位置。

此類相對位置屬性還有很多,具體如下: alt

  • 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

這些屬性通過引用id來代表一個Widget,或者使用parent來代表父佈局

<Button android:id="@+id/buttonB" ...
  app:layout_constraintLeft_toLeftOf="parent" />
         

邊距(Margins)

alt

如果設定了邊距,則它們將應用於相應的約束,將邊距強制設定為目標Widget和源Widget之間的空間。通常的佈局邊距屬性就可以達到此效果。

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

注意: 邊距只能是正數或等於零,採用Dimension形式。

約束目標GONE後邊距處理 (Margins when connected to a GONE widget)

當約束目標的可見性為View.GONE時,你還可以使用以下屬性指示要使用的不同邊距值:

  • layout_goneMarginStart
  • layout_goneMarginEnd
  • layout_goneMarginLeft
  • layout_goneMarginTop
  • layout_goneMarginRight
  • layout_goneMarginBottom

居中定位和位置偏差

ConstraintLayout的一個有用方面是它如何處理“不可能”的約束。例如,如果我們有如下程式碼:

<android.support.constraint.ConstraintLayout ...>
    <Button android:id="@+id/button" ...
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"/>
         </>

除非ConstraintLayout恰好具有與Button完全相同的大小,否則兩個約束不能同時滿足(雙方都不能成為我們想要它們的位置)。

alt

在這種情況下,約束的作用就像是相反的力量將Widget拉平(上圖);這樣Widget最終將會在父容器中居中顯示。這種使用方式在縱軸方向上同樣適用。

偏差(Bias)

遇到這種相反的約束時的預設設定是使Widget居中;但是你可以使用偏差屬性調整定位以使一側偏向另一側。

  • layout_constraintHorizontal_bias
  • layout_constraintVertical_bias

例如,如下程式碼將使左側具有30%的偏差而不是預設的50%,使得左側將更短,Widget更傾向於左側(具體效果如下圖):

<android.support.constraint.ConstraintLayout ...>
       <Button android:id="@+id/button" ...
           app:layout_constraintHorizontal_bias="0.3"
           app:layout_constraintLeft_toLeftOf="parent"
           app:layout_constraintRight_toRightOf="parent/>
         </>

alt

使用偏差屬性,你可以製作更好地適應螢幕尺寸變化的使用者介面。

環形定位(Circular positioning (Added in 1.1))

你可以以距離或者角度來約束一個Widget中心相對於另一個Widget的中心。這樣我們就可以將Widget放置到一個圓環上。具體屬性如下:

  • layout_constraintCircle : 引用widget ID
  • layout_constraintCircleRadius : 到另一個widget的距離
  • layout_constraintCircleAngle : widget需要擺放在哪個角度 (0-360)

alt alt

<Button android:id="@+id/buttonA" ... />
  <Button android:id="@+id/buttonB" ...
      app:layout_constraintCircle="@+id/buttonA"
      app:layout_constraintCircleRadius="100dp"
      app:layout_constraintCircleAngle="45" />

可見性行為(Visibility behavior)

ConstraintLayout對於可見性為View.GONE的Widget有明確的處理方式。

GONE widget,一般情況下,將不會顯示在介面上,同時也不再是佈局中的一部分。

但就佈局計算而言,GONE Widget仍然是其中的一部分,這是有很大區別的。

  • 對於佈局傳遞,它們的尺寸將被視為零(基本上,它們將被解析為一個點)
  • 如果他們對其他Widget有限制,這些限制仍然會得到體現,但任何邊距都會等於零

alt

這種特定的行為可以滿足你在不破壞佈局的情況下構建佈局,只需要暫時將對應的Widget標記為GONE即可,這在構建簡單的佈局動畫時是很有用的。

如上圖,當A GONE後,B使用的左邊距是B相對於A的邊距。在某些情況下,這個邊距可能並不是你所需要的(比如,A相對於容器存在20dp的左邊距,B相對於A有40dp的左邊距,當A被標記為GONE後,B相對於容器就會存在40dp的左邊距)。因此,你可以指定在連線到的Widget標記為GONE時的margin作為備用邊距值。具體看上一章節的【約束目標GONE後邊距處理】。

尺寸約束(Dimensions constraints)

ConstraintLayout最小尺寸和最大尺寸

通過如下屬性你可以給ConstrainLayout定義最大或是最小尺寸。

  • android:minWidth set the minimum width for the layout
  • android:minHeight set the minimum height for the layout
  • android:maxWidth set the maximum width for the layout
  • android:maxHeight set the maximum height for the layout

當其尺寸設定為WRAP_CONTENT時,這些最小和最大尺寸將會被ConstraintLayout使用到。

Widgets尺寸約束

Widgets的尺寸需要通過如下三種方式設定android:layout_widthandroid:layout_height來明確。

  • 使用一個明確的尺寸大小 (數字值如123dp或者是一個尺寸引用)
  • 使用WRAP_CONTENT, 讓控制元件自己計算其大小
  • 使用0dp, 等價於 “MATCH_CONSTRAINT”

alt

前兩種設定的效果和其他佈局一樣。最後一個將以匹配所設定的約束的方式調整Widget的大小(上圖中(a)wrap_content,(b)0dp)。如果設定來邊距,它們也將被計算在內(上圖中(c)0dp with margin)。

重要 針對ConstrainLayout中的Widget,我們不推薦使用MATCH_PARENT。我們可以通過使用MATCH_CONSTRAINT配合約束 Widget 的left/right或者top/bottom 到**“parent”**來定義類似的行為。

WRAP_CONTENT:強制執行約束 (Added in 1.1)

如果一個尺寸設定為WRAP_CONTENT,在1.1版本之前,他們將會被定義為數值類的尺寸大小,約束將不會限制結果尺寸。一般情況下這樣已經足夠滿足需求並且執行速度也是很快的,但是在某些情況下,你可能想用WRAP_CONTENT來設定尺寸,同時也想強制約束來限制結果佈局尺寸。這種情況下,你可以使用如下屬性:

  • app:layout_constrainedWidth=”true|false”
  • app:layout_constrainedHeight=”true|false”

MATCH_CONSTRAINT(Added in 1.1)

當Widget的尺寸被設定為MATCH_CONSTRAINT時,預設的結果就是使用所有可用的空間。如下幾個額外的屬性可以被使用到:

  • layout_constraintWidth_min and layout_constraintHeight_min : 將為這個尺寸設定最小值
  • layout_constraintWidth_max and layout_constraintHeight_max : 將為這個尺寸設定最大值
  • layout_constraintWidth_percent and layout_constraintHeight_percent : 將按相對父佈局的百分比來設定尺寸

最小值和最大值(Min and Max)

為min和max指示的值可以是Dp單位,也可以是“wrap”,它將使用與WRAP_CONTENT將執行的值相同的值

百分比尺寸(Percent dimension)

要使用百分比,你需要做如下配置:

  • 尺寸設定為MATCH_CONSTRAINT(0dp)
  • 預設值設定為percent** app:layout_constraintWidth_default=“percent”** 或者app:layout_constraintHeight_default="percent" (備註: 在版本1.1-beta1 and 1.1-beta2中這些配置是需要的, 但是在後續的版本中只要百分比屬性被定義,便不再需要這些配置)
  • 然後設定layout_constraintWidth_percent或者 layout_constraintHeight_percent屬性值,範圍0-1

比率

你可以定義Widget的寬高比例。為了實現這個效果,寬高中至少又一個設定為0dp(i.e.,MATCH_CONSTRAINT), 然後給** layout_constraintDimensionRatio**指定一個比例值,如下

 <Button android:layout_width="wrap_content"
     android:layout_height="0dp"
     app:layout_constraintDimensionRatio="1:1" />

這段程式碼將設定button的寬高一樣。比例值可以通過如下兩種方式設定:

  • float值, 代表寬高的比例
  • 比例值按照“width:height”的格式書寫

在寬高都設定為MATCH_CONSTRAINT(0dp)時,我們仍然能夠使用比例屬性。在這種情況下,系統設定滿足所有約束的最大尺寸並保持指定的縱橫比。要根據另一個的尺寸約束一個特定邊,可以預先附加**"W,""H,"分別約束寬度或高度。比如,一個尺寸被兩個目標約束(寬度0dp並且在父佈局居中),你可以通過在比例值前新增“W(約束寬度)”或者“H(約束高度)”**,來指明約束寬度或者高度。

<Button android:layout_width="0dp"
                   android:layout_height="0dp"
       app:layout_constraintDimensionRatio="H,16:9"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintTop_toTopOf="parent"/>

如上程式碼,將按照16:9的比例來設定button的高度,而寬度將匹配父佈局的約束。也就是這種情況下寬度:高度=16:9,由寬度來決定高度。

鏈(chain)

鏈在單個軸(水平或垂直)上提供組群行為。另一個軸可以獨立約束

建立一個鏈

如果一組Widgets通過雙向連線連結在一起,則它們被視為鏈。(下圖展示的是一個由兩個Widget組成的最小的鏈)

alt

鏈頭

鏈由在鏈的第一個元素上設定的屬性控制(我們可以將其視為鏈的“頭”)

alt

頭部是水平鏈的最左側Widget,垂直鏈的最頂部Widget。

鏈間邊距

如果在雙向連線上指定了邊距,則他們將會被計算在內。在擴散鏈的情況下,將從分配的空間中減去這些邊距

鏈Style

在鏈的第一個元素上設定屬性layout_constraintHorizo​​ntal_chainStylelayout_constraintVertical_chainStyle時,鏈的行為將根據指定的樣式更改(預設為CHAIN_SPREAD

  • CHAIN_SPREAD – 元素將等間距散開(預設style)
  • Weighted鏈 – CHAIN_SPREAD 模式, 當一些Widgets設定為MATCH_CONSTRAINT,他們將被分配所有可用空間。
  • CHAIN_SPREAD_INSIDE – 相似地, 但是鏈的兩端不會被分配空間。
  • CHAIN_PACKED – 鏈上元素會被打包在一起。子項的水平或垂直偏差屬性將影響整個打包元素的定位。

alt

權重鏈

鏈的預設行為是在可用空間中平均分佈元素。如果一個或多個元素使用MATCH_CONSTRAINT,則它們將使用可用的空白空間(他們之間平等分配)。 layout_constraintHorizo​​ntal_weightlayout_constraintVertical_weight屬性將控制如何使用MATCH_CONSTRAINT在元素之間分配空間。例如,在使用MATCH_CONSTRAINT的包含兩個元素的鏈上,第一個元素使用權重2,第二個元素使用權重1,第一個元素佔用的空間將是第二個元素佔用的空間的兩倍。

邊距和鏈(1.1版本)

在鏈中的元素上使用邊距時,邊距是相加的 例如,在水平鏈上,如果一個元素定義了10dp的右邊距而下一個元素定義了5dp的左邊距,則這兩個元素之間產生的邊距為15dp。 在計算鏈用於定位專案的剩餘空間時,會同時考慮專案及其邊距。剩餘空間不包含邊距。

虛擬助手(Virtual Helper objects)

除了之前詳述的內在功能外,您還可以使用ConstraintLayout中的特殊幫助程式物件來幫助您進行佈局。現在,Guideline可以幫助你建立相對於ConstraintLayout容器定位的水平和垂直Guideline。然後Widgets可以被約束到這些Guideline上。在1.1版本中,Barrier和Group也別加入進來。

優化器(in 1.1)

在1.1版本中,我們提供約束優化器。你可以通過將標記app:layout_optimizationLevel新增到ConstraintLayout元素來決定應用哪些優化。

  • none : 不做任何優化
  • standard : 預設值. 只優化 direct 和 barrier 約束
  • direct : 優化 direct 約束
  • barrier : 優化 barrier 約束
  • chain : 優化 chain 約束 (試驗版)
  • dimensions : 優化 dimensions 測量 (試驗版), 減少match constrains元素的測量次數。

此屬性是一個掩碼,因此您可以通過列出所需的優化來決定開啟或關閉特定的優化。例如

app:layout_optimizationLevel="direct|barrier|chain"