《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的右側(下圖)
實現可以如下
<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軸上具有相同的位置。
此類相對位置屬性還有很多,具體如下:
- 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)
如果設定了邊距,則它們將應用於相應的約束,將邊距強制設定為目標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完全相同的大小,否則兩個約束不能同時滿足(雙方都不能成為我們想要它們的位置)。
在這種情況下,約束的作用就像是相反的力量將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/>
</>
使用偏差屬性,你可以製作更好地適應螢幕尺寸變化的使用者介面。
環形定位(Circular positioning (Added in 1.1))
你可以以距離或者角度來約束一個Widget中心相對於另一個Widget的中心。這樣我們就可以將Widget放置到一個圓環上。具體屬性如下:
- layout_constraintCircle : 引用widget ID
- layout_constraintCircleRadius : 到另一個widget的距離
- layout_constraintCircleAngle : widget需要擺放在哪個角度 (0-360)
<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有限制,這些限制仍然會得到體現,但任何邊距都會等於零
這種特定的行為可以滿足你在不破壞佈局的情況下構建佈局,只需要暫時將對應的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_width和android:layout_height來明確。
- 使用一個明確的尺寸大小 (數字值如123dp或者是一個尺寸引用)
- 使用WRAP_CONTENT, 讓控制元件自己計算其大小
- 使用0dp, 等價於 “MATCH_CONSTRAINT”
前兩種設定的效果和其他佈局一樣。最後一個將以匹配所設定的約束的方式調整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組成的最小的鏈)
鏈頭
鏈由在鏈的第一個元素上設定的屬性控制(我們可以將其視為鏈的“頭”)
頭部是水平鏈的最左側Widget,垂直鏈的最頂部Widget。
鏈間邊距
如果在雙向連線上指定了邊距,則他們將會被計算在內。在擴散鏈的情況下,將從分配的空間中減去這些邊距
鏈Style
在鏈的第一個元素上設定屬性layout_constraintHorizontal_chainStyle或layout_constraintVertical_chainStyle時,鏈的行為將根據指定的樣式更改(預設為CHAIN_SPREAD)
- CHAIN_SPREAD – 元素將等間距散開(預設style)
- Weighted鏈 – CHAIN_SPREAD 模式, 當一些Widgets設定為MATCH_CONSTRAINT,他們將被分配所有可用空間。
- CHAIN_SPREAD_INSIDE – 相似地, 但是鏈的兩端不會被分配空間。
- CHAIN_PACKED – 鏈上元素會被打包在一起。子項的水平或垂直偏差屬性將影響整個打包元素的定位。
權重鏈
鏈的預設行為是在可用空間中平均分佈元素。如果一個或多個元素使用MATCH_CONSTRAINT,則它們將使用可用的空白空間(他們之間平等分配)。 layout_constraintHorizontal_weight和layout_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"