Android 減少佈局層次—— ConstraintLayout 約束佈局 的使用
一. 背景
當前手機APP專案中,有些介面比較複雜,導致佈局層次很深,View的數量也相當多,對效能產生了一定影響;複雜的佈局,同時也增大了程式碼維護的難度。
ConstraintLayout 正是為了解決這個問題,它支援以下幾類強大的特性:
- 相對定位
Margins
中心定位
角度定位
Visibility behavior
尺寸約束
鏈(Chains)
Virtual Helpers objects
在組合使用這些特性以後,我們可以達到明顯降低佈局層次的目的。
二. 可行性
- ConstraintLayout 是以一個單獨的support library 的形式提供的,他支援API 9以上的版本。
- Android Studio 2.2以上的版本
- 支援將舊的佈局檔案一鍵替換為ConstraintLayout,工作量較小(不支援將已有的多個層級的佈局直接轉換為同一層級);
三. 引入步驟
在module的build.gradle中插入:
dependencies {
...
//引入ConstraintLayout
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
...
}
目前最新版本為1.1.0-beta3
四. 特性詳解
1. 相對定位
相對定位是ConstrainLayout中最為經常使用的一項屬性,它支援
水平方向:
- left
app:layout_constraintLeft_toLeftOf
app:layout_constraintLeft_toRightOf
- right
app:layout_constraintRight_toLeftOf
app:layout_constraintRight_toRightOf
- start
app:layout_constraintStart_toEndOf
app:layout_constraintStart_toStartOf
- end
app:layout_constraintEnd_toStartOf
app:layout_constraintEnd_toEndOf
豎直方向:
- top
app:layout_constraintTop_toTopOf
app:layout_constraintTop_toBottomOf
- bottom
app:layout_constraintBottom_toTopOf
app:layout_constraintBottom_toBottomOf
- text baseline
app:layout_constraintBaseline_toBaselineOf
這些屬性都需要引用到另一個控制元件,或者parent(指代他們的父容器,也就是ConstraintLayout)
2. Margins
支援設定Margin(僅支援0或正數):
跟其它佈局所不同的是,ConstraintLayout還支援連結到Visibility為Gone時的Margin:
layout_goneMarginLeft
layout_goneMarginRight
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginTop
layout_goneMarginBottom
五. 中心定位和偏移
1. 中心定位
我們看一下這種情況:
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
</>
Button兩端都被連結到了父容器的兩端,如果
1. Button剛好和父容器相同寬度時,Button就相當於寬度設為了match_parent;
- Button小於父容器寬度時,約束就像兩個相等的力,把Button往兩邊拉,這時,Button正好位於父容器水平方向的正中;
2. 偏移(bias)
在中心定位時,我們可以對這兩個約束力進行控制,使其偏向某一側。例如,我們使控制元件偏向左側:
<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/>
</>
所產生的效果是:
正如你所見,這有點類似於百分比佈局或LinearLayout的weight屬性,這使得我們可以更好地適應不同的螢幕尺寸。
六. 角度定位(在支援庫版本1.1中加入)
角度定位是ConstraintLayout所特有的一種佈局方式,它可以約束一個控制元件的中心相對於另一個的角度和距離。你可以通過使用這些屬性達到目的:
layout_constraintCircle: 角度定位相對的另一個控制元件的id
layout_constraintCircleRadius: 到相對控制元件中心的距離
layout_constraintCircleAngle: 控制元件相對於另一個控制元件的角度
示例程式碼:
<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
如果控制元件與控制元件間存在約束關係,當其中的一個控制元件Visibility變為GONE時,ConstraintLayout仍然會保留這個約束關係,但這個View的Margin都會失效;這樣可以確保不會影響到已有的佈局。
八. 尺寸約束
1. 設定佈局寬高
ConstraintLayout可以為佈局設定寬高(android:layout_width 、android:layout_height),需要特殊注意的是,當設為0dp時,它的作用相當於MATCH_CONSTRAINT,而MATCH_PARENT在ConstraintLayout中是不被推薦使用的。
2. 設定佈局最大最小尺寸
ConstraintLayout可以對佈局的高度和寬度進行限制:
- android:minWidth:最小寬度
android:minHeight:最小高度
android:maxWidth:最大寬度
android:maxHeight:最大高度
注意:這些屬性僅在寬高設為WRAP_CONTENT時有效!
3. WRAP_CONTENT
在ConstraintLayout中,仍然可以對WRAP_CONTENT的最大尺寸進行約束:
- app:layout_constrainedWidth=”true|false”
- app:layout_constrainedHeight=”true|false”
當其中一個被設定為true時,控制元件的最大寬高任然可以被約束鏈約束,需要注意的是,這樣做會使佈局變慢一些。
4. MATCH_CONSTRAINT(在支援庫版本1.1中加入)
當寬高被設為MATCH_CONSTRAINT,這個控制元件將嘗試佔據佈局上所有可用的地方,但同時會被這些屬性所限制:
- layout_constraintWidth_min/layout_constraintHeight_min:最小寬高
layout_constraintWidth_max/layout_constraintHeight_max:最大寬高
layout_constraintWidth_percent/layout_constraintHeight_percent:寬高相對於父容器的百分比。
注意:
1. 這些屬性同時可以設定為wrap;
當使用百分比尺寸的時候,應當設定寬高為MATCH_CONSTRAINT;
父容器需要設定app:layout_constraintWidth_default=”percent”或app:layout_constraintHeight_default=”percent”(在1.1-beta2以後不再必須設定)
5. 設定比例
constraintLayout支援子控制元件設定其寬高比,要使該特性生效至少需要將寬高中的一個設定為0dp(MATCH_CONSTRAINT)
<Button android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />
app:layout_constraintDimensionRatio可以設定為兩種格式:
1. 一個float型別的數值,代表寬與高之間的比例;
- 寬:高
注意:
當寬高均被設為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"/>
九. 鏈(Chains)
1. 概念
鏈使我們能夠對一組在水平或豎直方向互相關聯的控制元件的屬性進行統一管理。成為鏈的條件是:一組控制元件他們通過一個雙向的約束關係連結起來。
注意:鏈的屬性是由一條鏈的頭結點控制的。而頭結點的定義是一條鏈中位於最左端的控制元件。
2. 鏈的Margins
ConstraintLayout支援對鏈新增Margin,如果這條鏈的控制元件是分散分佈的,將會從已分配給鏈的空間中減去設定的鏈邊距。
3. 鏈的Style
鏈支援設定他們的Style,只需在頭結點指定layout_constraintHorizontal_chainStyle或layout_constraintVertical_chainStyle。共支援五種型別:
- CHAIN_SPREAD(預設值):鏈中的元素將分散分佈;
Weighted chain:在CHAIN_SPREAD模式中,如果某些元件被設定成MATCH_CONSTRAINT,他們將會佔據所有空餘空間並分散分佈;
CHAIN_SPREAD_INSIDE:類似於CHAIN_SPREAD,但鏈的兩端不會分散;
CHAIN_PACKED:鏈中的元素將會緊密相連在一起,偏移(bias)的設定將會影響他們在容器中所處的位置
Weighted chains:當鏈處在預設的模式(CHAIN_SPREAD)且其中一個或多個元素被設定為MATCH_CONSTRAINT時,他們將平均佔據剩下的空餘空間;如果其中的結點同時設定了
layout_constraintHorizontal_weight或layout_constraintVertical_weight屬性,那麼他們將根據所設定的比重來分配剩下的空間。
十. Virtual Helper objects
虛擬輔助類部件它們最終不會在介面上呈現出來,但可以幫助我們更好更精細地控制佈局。目前。所支援的這類部件包括:
1. Guideline
Guideline可以放在豎直方向或水平方向,水平Guideline的高為0,寬度與父容器一致;豎直Guideline同理。
Guideline 具有三類特殊的屬性:
- layout_constraintGuide_begin:設定Guideline 距離父容器起始位置的距離(left或top);
layout_constraintGuide_end:設定Guideline 距離父容器尾部的距離(right或bottom);
layout_constraintGuide_percent:設定Guideline 相對於父容器寬度/高度的百分比位置。
例如,設定一條豎直方向的Guideline:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"/>
</android.support.constraint.ConstraintLayout>
2. Barrier(在支援庫版本1.1中加入)
Barrier 使用多個控制元件作為參考,在這些控制元件中,選取在特定方向最邊緣的的控制元件建立一條Guideline。
constraint_referenced_ids用來設定要參考的控制元件id,多個控制元件id間以逗號的形式隔開。
例如,假設已有兩個按鈕:
Barrier 設定為:
<android.support.constraint.Barrier
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="bt_1,bt_2"/>
此時Guideline 的位置為:
3. Group(在支援庫版本1.1中加入)
Group 用於控制所引用的一組控制元件的可見性(Visibility),constraint_referenced_ids用來設定要參考的控制元件id,多個控制元件id間以逗號的形式隔開。
<android.support.constraint.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:constraint_referenced_ids="button4,button9" />
注意:多個Group 部件可以引用相同的控制元件,這時Group 在xml中的定義順序將決定這個控制元件最終的可見性。