本文是 ConstraintLayout 小課堂系列第 2 講,課程目錄:
平均間隔
先看一個(gè)需求:存在多個(gè) TextView褪测,他們的寬度是可變的速警,但它們之間的間隔是相等的专筷,并且要平均分配整個(gè)屏幕的寬度涵卵。
使用原生的 LinearLayout 也能做到這點(diǎn)夏跷,簡單看一下如何實(shí)現(xiàn):
添加若干不可見的 View 來填充 TextView 之間的間隙,通過設(shè)置相同的 layout_weight 使得這些 View 的尺寸相同算撮,這樣 TextView 的間隔就是相同的了疚脐。
按照套路應(yīng)該說這樣做的缺點(diǎn)了:
- 可能產(chǎn)生 ViewGroup 嵌套(沒錯(cuò)亿柑,用 ConstraintLayout 就是為了不嵌套)
- 多余的 View 在繪制過程中浪費(fèi) CPU(沒錯(cuò),不嵌套的終極目的就是提高繪制速度)
- 想動(dòng)態(tài)操作這些 TextView 以及間隔棍弄,需要給這些間隔 View 添加引用望薄,手動(dòng)設(shè)置 visibility疟游,使用起來很不方便。
那么用 ConstraintLayout 怎么實(shí)現(xiàn)這個(gè)功能呢痕支?
使用 ConstraintLayout 中的 Chain(鏈)可以更簡單地實(shí)現(xiàn)這個(gè)需求颁虐,不用添加任何輔助對象,只需要在這些 TextView 之間形成鏈卧须,默認(rèn)就是平均分配間隔的樣子另绩。
Chain(鏈)
什么是 Chain?
兩個(gè) View 在一個(gè)方向上互相約束對方就形成了鏈故慈,多個(gè) View 兩兩成鏈,就形成了更長的鏈框全,見下圖(圖來自官網(wǎng)):
這里吐個(gè)槽察绷,本文寫作時(shí)最新 Android Studio 3.3.2 版本無法通過拖拽創(chuàng)建鏈,需要手寫代碼津辩。
代碼這么寫拆撼,舉個(gè)栗子:
<Button
android:id="@+id/buttonOK"
app:layout_constraintEnd_toStartOf="@+id/buttonCancel" ?? 左邊的右手牽左手
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/buttonCancel"
app:layout_constraintStart_toEndOf="@+id/buttonOK" ?? 右邊的左手牽右手
app:layout_constraintEnd_toEndOf="parent" />
形成鏈的一系列 View 會(huì)表現(xiàn)出一些整體特征,可以設(shè)置一些特殊的布局效果喘沿,見下圖(圖來自官網(wǎng)):
- Spread Chain:伸展鏈闸度,默認(rèn)設(shè)置,形成鏈的 View 分散排列蚜印,間隔相等莺禁。
- Spread Inside Chain:內(nèi)部伸展鏈,也是平均分配間隔窄赋,但 View 和 parent 直接沒有間隔哟冬。
- Weighted Chain:權(quán)重鏈,與 LinearLayout 的 weight 相似忆绰,按比例分配空間大小浩峡。
- Packed Chain:打包鏈,將所有 View 打包在一起错敢,當(dāng)做整體翰灾,居中。
- Packed Chain with Bias:帶偏斜的打包鏈稚茅。
如何設(shè)置不同的布局效果
數(shù)據(jù)結(jié)構(gòu)里的鏈表都有個(gè)頭節(jié)點(diǎn)纸淮,它的引用就是整個(gè)鏈表的引用。在 ConstraintLayout 中亚享,左側(cè)第一個(gè)或者上方第一個(gè)成鏈的 View 就是頭節(jié)點(diǎn)萎馅。給這個(gè)頭節(jié)點(diǎn)設(shè)置鏈屬性,就相當(dāng)于給整個(gè)鏈設(shè)置了鏈屬性虹蒋。實(shí)現(xiàn)以上各種效果需要設(shè)置哪些鏈屬性呢糜芳,我們一個(gè)一個(gè)看飒货。
Spread Chain
這個(gè)就是開篇提到的平均間隔布局了,只要結(jié)成鏈峭竣,默認(rèn)就是這個(gè)效果塘辅。
設(shè)置方法:
- 鏈頭節(jié)點(diǎn)設(shè)置 chainStyle
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintVertical_chainStyle="spread"
- 所有 View 設(shè)置 wrap_content
android:layout_width="wrap_content"
android:layout_height="wrap_content"
與 LinearLayout 的實(shí)現(xiàn)相比:
- 沒有多余的 View,不用維護(hù)多個(gè)間隔 View 的引用
- 如果要隱藏某個(gè) TextView皆撩,直接設(shè)置為 gone 就行了扣墩,剩余的 TextView 仍然是平均分隔的。
通過設(shè)置 margin 可以微調(diào)一下間隔扛吞,margin 相當(dāng)于擴(kuò)大了 TextView 的占用空間呻惕,剩余的空間再平分給間隔,有 margin 的 View 看起來間隔會(huì)大一些滥比。注意 ConstraintLayout 中使用的 margin 只能是非負(fù)數(shù)亚脆,設(shè)置成負(fù)數(shù)無效。
Spread Inside Chain
設(shè)置方法:
- 鏈頭節(jié)點(diǎn)設(shè)置 chainStyle
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintVertical_chainStyle="spread_inside"
- 所有 View 設(shè)置 wrap_content
android:layout_width="wrap_content"
android:layout_height="wrap_content"
這個(gè)設(shè)置成兩邊貼著 parent盲泛,如果要求左側(cè)貼著 parent濒持,右側(cè)不貼呢?
如果是 LinearLayout寺滚,去掉左側(cè)貼邊的輔助 View 即可柑营,比較方便。用 ConstraintLayout 的 Chain 怎么做呢村视?
直接去掉左側(cè)第一個(gè) TextView 對 第二個(gè) TextView 的約束官套,也就是第一個(gè)的右手放開第二個(gè)的左手。這樣整個(gè)鏈就少了一個(gè) View蚁孔,第一個(gè) View 沒有了右側(cè)約束虏杰,直接靠在了左側(cè) parent 上,剩下的 View 仍然是一個(gè)鏈勒虾,分配剩余的空間纺阔。
Weighted Chain
設(shè)置方法:
- chainStyle:設(shè)置成任何值都沒區(qū)別,不用設(shè)置
- 至少有一個(gè) View 設(shè)置成
match_constraint
android:layout_width="0dp"
android:layout_height="0dp"
- 可選的 weight
app:layout_constraintHorizontal_weight="1"
app:layout_constraintVertical_weight="1"
只要在鏈上有一個(gè) View 設(shè)置為了 match_constraint
修然,這些 View 之間的剩余空間都會(huì)被占用笛钝,因此 chainStyle 屬性設(shè)置為任何一個(gè)都沒有區(qū)別。
如果不設(shè)置 weight 屬性愕宋,就有點(diǎn)復(fù)雜了:
- 所有
match_constraint
的 View 都不設(shè)置玻靡。大家平分,與都設(shè)置了weight="1"
的效果相同中贝。 - 一些 View 設(shè)置了 weight 屬性囤捻,一些 View 沒設(shè)置。沒設(shè)置的 View 完全消失了邻寿,所占空間被分配給剩余的 View 了蝎土。
- 設(shè)置了
weight="0"
的 View 完全消失视哑,剩余空間被其他 View 瓜分。 - 有趣的是誊涯,如果只有不設(shè)置的和設(shè)置為
weight="0"
的挡毅,則相當(dāng)于都不設(shè)置,大家平分暴构。
Packed Chain & Packed Chain with Bias
設(shè)置方法:
- 鏈頭節(jié)點(diǎn)設(shè)置 chainStyle
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintVertical_chainStyle="packed"
- 所有 View 設(shè)置為 wrap_content
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- 鏈頭節(jié)點(diǎn)設(shè)置 bias
app:layout_constraintHorizontal_bias="0.1"
打包鏈將整個(gè)鏈中的所有 View 打包成一個(gè)整體跪呈,可以與其他 View 關(guān)聯(lián)約束∪∮猓可能的應(yīng)用場景是多個(gè)可變長的 TextView 緊挨在一起耗绿,并且它們的整體要在某個(gè)范圍內(nèi)居中。如果不用 ConstraintLayout砾隅,就只能將它們嵌套在一個(gè)單獨(dú)的 View 中來做到了误阻,你看,這就是 ConstraintLayout 能減少嵌套的原因????琉用。
Bias 屬性是用來設(shè)置一個(gè) View 被居中時(shí)堕绩,偏向某一側(cè)的程度策幼。通常取值范圍是 [0, 1]邑时,表示左側(cè)或上側(cè)的空白區(qū)域占所有空白區(qū)域的范圍。有趣的是特姐,這個(gè)值也可以取 [0, 1] 之外的值晶丘,這個(gè) View 就超出了約束的范圍,小于 0 向左移唐含,大于 0 向右移浅浮。
回到 Packed Chain with Bias,由于打包效果捷枯,整個(gè)鏈上的 View 對于其他 View 來說相當(dāng)于一個(gè) View滚秩,設(shè)置的 bias 屬性也與一個(gè) View 的 bias 相同。
總結(jié)
經(jīng)過上面的講解可以看到 ConstraintLayout 的鏈布局非常強(qiáng)大淮捆,可以實(shí)現(xiàn)的布局效果非常豐富郁油,如果有什么難度較高的布局普通方法實(shí)現(xiàn)不了,可以考慮用鏈?zhǔn)讲季謥硭伎肌?/p>
(ole)