## 參考
[官方文檔——使用 ConstraintLayout 構建自適應界面](https://developer.android.google.cn/training/constraint-layout?hl=zh_cn)
[GitHub 上的約束布局示例項目](https://github.com/android/views-widgets-samples/tree/master/ConstraintLayoutExamples)
[約束布局ConstraintLayout用法全解析](https://www.jianshu.com/p/502127a493fb)
[帶你了解 Android 約束布局 ConstraintLayout](https://mp.weixin.qq.com/s?__biz=MzIwMTAzMTMxMg==&mid=2649493172&idx=1&sn=70d1ff879246fc49dcb90bc387eef37b&chksm=8eec854bb99b0c5d43fb3b945d4394dd67e3c09916e3e5c6a7ca98525aede0ddd3945ad1882a&scene=38#wechat_redirect)
[ConstraintLayout 約束布局](https://juejin.im/post/5cf868f2e51d45775e33f529#heading-17)
[布局優化:9種讓你不得不使用約束布局Constraintlayout的場景](https://juejin.im/post/5e7eb53451882573866b7f2d)
[布局之ConstraintLayout 約束布局](https://blog.csdn.net/weixin_41620505/article/details/99688871)
## 要點摘要
[`ConstraintLayout`](https://developer.android.google.cn/reference/androidx/constraintlayout/widget/ConstraintLayout?hl=zh_cn)可讓您使用扁平視圖層次結構(無嵌套視圖組)創建復雜的大型布局。它與[`RelativeLayout`](https://developer.android.google.cn/reference/android/widget/RelativeLayout?hl=zh_cn)相似,其中所有的視圖均根據同級視圖與父布局之間的關系進行布局,但其靈活性要高于`RelativeLayout`,并且更易于與 Android Studio 的布局編輯器配合使用。
`ConstraintLayout`的所有功能均可直接通過布局編輯器的可視化工具來使用,因為布局 API 和布局編輯器是專為彼此構建的。 因此,您完全可以使用`ConstraintLayout`通過拖放的形式(而非修改 XML)來構建布局。
### 約束條件概覽
要在`ConstraintLayout`中定義某個視圖的位置,您**必須為該視圖添加至少一個水平約束條件和一個垂直約束條件**。每個約束條件均表示與**其他視圖、父布局或隱形引導線**之間連接或對齊方式。每個約束條件均定義了視圖在豎軸或者橫軸上的位置;因此**每個視圖在每個軸上都必須至少有一個約束條件**,但**通常情況下會需要更多約束條件**。**如果視圖沒有任何約束條件,則會在位置 \[0,0\](左上角)處進行繪制。**
示例如下:
:-: 
**圖 1**.編輯器將視圖 C 顯示在視圖 A 下方,但它并沒有垂直約束條件
:-: 
**圖 2**.視圖 C 現垂直約束在視圖 A 下方
在圖 1 中,布局在編輯器中看起來很完美,但視圖 C 上卻沒有垂直約束條件。在設備上繪制此布局時,雖然視圖 C 與視圖 A 的左右邊緣水平對齊,但由于沒有垂直約束條件,它會顯示在屏幕頂部。**一旦設置C的垂直約束條件,比如在A的正下方,那么這時C會隨著A的移動而移動。**
盡管缺少約束條件不會導致出現編譯錯誤,但布局編輯器會將缺少約束條件作為錯誤顯示在工具欄中。要查看錯誤和其他警告,請點擊**Show Warnings and Errors**。 為幫助您避免出現缺少約束條件這一問題,布局編輯器會使用[Autoconnect 和 Infer Constraints](https://developer.android.google.cn/training/constraint-layout?hl=zh_cn#use-autoconnect-and-infer-constraints)功能自動為您添加約束條件,如下圖3、4所示


### 添加或移除約束條件
創建約束條件時,請注意以下規則:
* 每個視圖都必須至少有兩個約束條件:一個水平約束條件,一個垂直約束條件。
* 您只能在共用同一平面的約束手柄與定位點之間創建約束條件。因此,視圖的垂直平面(左側和右側)只能約束在另一個垂直平面上;而基準線則只能約束到其他基準線上。
* 每個約束句柄只能用于一個約束條件,但您可以在同一定位點上創建多個約束條件(從不同的視圖)。
您可以通過執行以下任一操作來刪除約束條件:
* 點擊某個約束條件將其選中,然后按`Delete`。
* 按住`Control`(在 macOS 上為`Command`),然后點擊某個約束定位點。請注意,該約束條件變為紅色即表示您可以點擊將其刪除,如圖5 所示。

**圖 5**.紅色約束條件表示您可以點擊將其刪除。
* 在**Attributes**窗口的 Layout 部分中,點擊某個約束定位點,如圖 6 所示

**圖6**.點擊約束定位點將其刪除。
* 當然也可以通過工具欄的**Clear All Constraints**按鈕,注意這樣會清除所有控件的約束條件,如圖7所示
:-: 
**圖7**.清除約束條件

### 父級位置
將視圖的一側約束到布局的相應邊緣。
在圖 8 中,視圖的左側連接到父布局的左邊緣。您可以使用外邊距來定義距離邊緣的距離。
:-: 
**圖 8**.對父級的水平約束
### 順序位置
定義兩個視圖的顯示順序(垂直或水平方向)。
:-: 
**圖 9**.水平和垂直約束方式
在圖 9 中,B 被約束為始終位于 A 的右側,而 C 被約束在 A 的下方。 不過,這些約束條件并不意味著對齊,因此 B 仍然可以上下移動。
### 對齊方式
將一個視圖的邊緣與另一視圖的同一邊對齊。
:-: 
**圖 10**.水平對齊約束
:-: 
**圖 11**.偏移水平對齊約束
在圖 10 中,B 的左側與 A 的左側對齊。 如果要與視圖中心對齊,請對兩側創建約束條件。
您可以通過從約束布局向內拖動視圖來偏移對齊量。例如,圖 11 顯示 B 的偏移對齊為 24dp。 偏移量由受約束視圖的外邊距定義。
您還可以選擇要對齊的所有視圖,然后點擊工具欄中的**Align**以選擇對齊類型。
### 基線對齊
將一個視圖的文本基線與另一視圖的文本基線對齊。
:-: 
**圖 12**.基線對齊約束
在圖 12 中,B 的第一行與 A 中的文本對齊。
要創建基線約束條件,請右鍵點擊要約束的文本視圖,然后點擊**Show Baseline**。接著點擊文本基線并將其拖到另一基線上。
### 引導線約束
您可以添加垂直或水平的引導線來約束視圖,并且應用用戶看不到該引導線。 您可以根據相對于布局邊緣的 dp 單位或百分比在布局中定位引導線。
要創建引導線,請點擊工具欄中的**Guidelines**,然后點擊 Add Vertical Guideline 或 Add Horizontal Guideline。
拖動虛線將其重新定位,然后點擊引導線邊緣的圓圈以切換測量模式。
:-: 
**圖 13**.受引導線約束的視圖
### 屏障約束(Barrier)
與引導線類似,屏障是一條隱藏的線,您可以用它來約束視圖。屏障不會定義自己的位置;相反,屏障的位置會隨著其中所含視圖的位置而移動。如果您希望將視圖限制到一組視圖而不是某個特定視圖,這就非常有用。
例如,圖 14 顯示視圖 C 被約束在屏障的右側。該屏障設置為視圖 A 和視圖 B 的“end”側(或從左至右布局中的右側)。因此,屏障根據視圖 A 或視圖 B 的右側是否為最右側來移動。
要創建屏障,請按以下步驟操作:
1. 點擊工具欄中的**Guidelines**,然后點擊 Add Vertical Barrier 或 Add Horizontal Barrier。
2. 在**Component Tree**窗口中,選擇要放入屏障內的視圖,然后將其拖動到屏障組件中。
3. 在**Component Tree**中選擇障礙,打開 Attributes窗口,然后設置 barrierDirection。
現在,您可以從另一個視圖創建屏障約束。
您還可以將屏障*內*的視圖約束到屏障。這樣,您就可以確保屏障中的所有視圖始終相互對齊,即使您并不知道哪個視圖最長或最高。
您還可以在屏障內添加引導線,以確保屏障的位置“最小”。
:-: 
**圖 14**.視圖 C 約束在屏障內,該屏障會根據視圖 A 和視圖 B 的位置/尺寸而移動

## 調整視圖尺寸
您可以使用角手柄來調整視圖的尺寸,但這會對尺寸進行硬編碼,從而使視圖不會針對不同的內容或屏幕尺寸進行調整。要選擇不同的尺寸模式,請點擊視圖,然后打開編輯器右側的**Attributes**窗口。
:-: 
**圖 15**.在選擇視圖時,**Attributes**窗口會包含如下控件:**①** 尺寸比、**②** 刪除約束條件、**③** 高度/寬度模式、**④** 外邊距和**⑤** 約束偏差。您還可以通過點擊**⑥** 約束列表中的各個約束條件來突出顯示布局編輯器中的各個約束條件。

**Attributes**窗口頂部附近的視圖檢查器中包括若干布局屬性的控件,如圖 15 所示(僅適用于約束布局中的視圖)。
您可以通過點擊圖 15 中標注**③**所指示的符號來更改高度和寬度的計算方式。這些符號代表如下所示的尺寸模式(點擊符號即可切換這些設置):
* **Fixed**:您可以在下面的文本框中指定具體維度(固定大小長度寬度),也可以在編輯器中調整視圖尺寸。
* **Wrap Content**:視圖僅在需要時擴展以適應其內容。
* **Match Constraints**:視圖會盡可能擴展,以滿足每側的約束條件(在考慮視圖的外邊距之后)。不過,您可以使用以下屬性和值修改該行為(這些屬性僅在您將視圖寬度設置為“match constraints”時才會生效):
* **layout\_constraintWidth\_default**
* **spread**:盡可能擴展視圖以滿足每側的約束條件。這是默認行為。
* **wrap**:僅在需要時擴展視圖以適應其內容,但如有約束條件限制,視圖仍然可以小于其內容。因此,它與使用**Wrap Content**(上面)之間的區別在于,將寬度設為**Wrap Content**會強行使寬度始終與內容寬度完全匹配;而使用**layout\_constraintWidth\_default**設置為**wrap**的**Match Constraints**時,視圖可以小于內容寬度。
* **layout\_constraintWidth\_min**
該視圖的最小寬度采用`dp`維度。
* **layout\_constraintWidth\_max**
該視圖的最大寬度采用`dp`維度。
**注意**:您無法將`match_parent`用于`ConstraintLayout`中的任何視圖。請改用“match constraints” (`0dp`)。
### 將尺寸設置為比率
如果至少有一個視圖尺寸設置為“match constraints”`0dp`,那么您可以將視圖尺寸設置為 16:9。要啟用該比率,請點擊**Toggle Aspect Ratio Constraint**(圖 15 中的標注①),然后在出現的輸入框中輸入width:height比率。
如果寬度和高度都設置為“match constraints”,您可以點擊**Toggle Aspect Ratio Constraint**,選擇哪個維度基于與另一個維度的比率。 視圖檢查器通過用實線連接相應的邊緣來指明哪個被設為比率。
例如,如果您將兩側都設置為“match constraints”,請雙擊**Toggle Aspect Ratio Constraint**,將寬度設置為與高度的比率。現在整個尺寸由視圖的高度決定(可以以任意方式定義),如圖 16 所示。
:-: 
**圖 16**.該視圖的寬高比設置為 16:9,其寬度基于與高度的比率
## 使用鏈控制線性組
鏈是一組視圖,這些視圖通過雙向位置約束條件相互鏈接到一起。鏈中的視圖可以垂直或水平分布。
鏈可以采用以下幾種樣式之一:
1. **Spread**:視圖是均勻分布的(在考慮外邊距之后)。這是默認值。
2. **Spread inside**:第一個和最后一個視圖固定在鏈兩端的約束邊界上,其余視圖均勻分布。
3. **Weighted**:當鏈設置為 spread 或 spread inside 時,您可以通過將一個或多個視圖設置為“match constraints”(`0dp`) 來填充剩余空間。默認情況下,設置為“match constraints”的每個視圖之間的空間均勻分布,但您可以使用`layout_constraintHorizontal_weight`和`layout_constraintVertical_weight`屬性為每個視圖分配重要性權重。如果您熟悉[線性布局](https://developer.android.google.cn/guide/topics/ui/layout/linear?hl=zh_cn)中的`layout_weight`的話,就會知道該樣式與它的原理是相同的。因此,權重值最高的視圖獲得的空間最大;相同權重的視圖獲得同樣大小的空間。
4. **Packed**:視圖打包在一起(在考慮外邊距之后)。 然后,您可以通過更改鏈的頭視圖偏差調整整條鏈的偏差(左/右或上/下)。
鏈的“頭”視圖(水平鏈中最左側的視圖以及垂直鏈中最頂部的視圖)以 XML 格式定義鏈的樣式。不過,您可以通過右鍵選擇任意一個組件,右鍵選擇Cycle Chain mode,在**spread**、**spread inside**和**packed**之間進行切換。或者選擇任意一個組件,在編輯器最右邊的Attributes屬性編輯區域,搜索框輸入chainstyle,在`layout_constraintHorizontal_chainStyle`選擇具體的stylemode,如下動圖所示

:-: 
**圖 19**.具有兩個視圖的水平鏈
:-: 
**圖 17**.每種鏈樣式的示例
要創建鏈,請選擇要包含在鏈中的所有視圖,右鍵點擊其中一個視圖,選擇**Chains**,然后選擇 Center Horizontally 或 Center Vertically,如動圖 中所示:
:-: 
**動圖**.創建水平鏈
以下是使用鏈時需要考慮的其他事項:
* 視圖可以是水平鏈和垂直鏈的一部分,因此可以輕松構建靈活的網格布局。
* 只有當鏈的每一端都被約束到同一軸上的另一個對象時,鏈才能正常工作,如圖 15 所示。
* 雖然鏈的方向為垂直或水平,但使用其中一個方向不會沿該方向與視圖對齊。因此,請務必包含其他約束條件,以便使鏈中的每個視圖都能正確定位,例如[對齊約束](https://developer.android.google.cn/training/constraint-layout?hl=zh_cn#alignment)。
- 前言
- Android系統的體系結構
- Dalvik VM 和 JVM 的比較
- Android 打包應用程序并安裝的過程
- Android ADB工具
- Android應用開發
- Android UI相關知識總結
- Android 中window 、view、 Activity的關系
- Android應用界面
- Android中的drawable和bitmap
- AndroidUI組件adapterView及其子類和Adapter的關系
- Android四大組件
- Android 數據存儲
- SharedPreference
- Android應用的資源
- 數組資源
- 使用Drawable資源
- Material Design
- Android 進程和線程
- 進程
- 線程
- Android Application類的介紹
- 意圖(Intent)
- Intent 和 Intent 過濾器(Google官網介紹)
- Android中關于任務棧的總結
- 任務和返回棧(官網譯文)
- 總結
- Android應用安全現狀與解決方案
- Android 安全開發
- HTTPS
- 安卓 代碼混淆與打包
- 動態注入技術(hook技術)
- 一、什么是hook技術
- 二、常用的Hook 工具
- Xposed源碼剖析——概述
- Xposed源碼剖析——app_process作用詳解
- Xposed源碼剖析——Xposed初始化
- Xposed源碼剖析——hook具體實現
- 無需Root也能Hook?——Depoxsed框架演示
- 三、HookAndroid應用
- 四、Hook原生應用程序
- 五、Hook 檢測/修復
- Android 應用的逆向與加固保護技術
- OpenCV在Android中的開發
- Android高級開發進階
- 高級UI
- UI繪制流程及原理
- Android新布局ConstraintLayout約束布局
- 關鍵幀動畫
- 幀動畫共享元素變換
- Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解
- Android中為什么主線程不會因為Looper.loop()里的死循環卡死?
- 為什么 Android 要采用 Binder 作為 IPC 機制?
- JVM 中一個線程的 Java 棧和寄存器中分別放的是什么?
- Android源碼的Binder權限是如何控制?
- 如何詳解 Activity 的生命周期?
- 為什么Android的Handler采用管道而不使用Binder?
- ThreadLocal,你真的懂了嗎?
- Android屏幕刷新機制