傳統(tǒng)布局缺陷
![]這里寫(xiě)圖片描述
<ScrollView>
<LinearLayout>
...
...
<LinearLayout>
<LinearLayout>
<LinearLayout/>
<LinearLayout/>
<LinearLayout/>
<LinearLayout/>
</LinearLayout>
<LinearLayout>
<LinearLayout/>
</LinearLayout>
<LinearLayout>
<LinearLayout/>
<LinearLayout/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
在這樣場(chǎng)景下發(fā)現(xiàn)最多的時(shí)候用到四層線性布局,共嵌套了五層丹允,即使使用RelativeLayout和LinearLayout結(jié)合使用的方式,最少也需要三層的布局袋倔,在面對(duì)復(fù)雜界面的時(shí)候雕蔽,這樣實(shí)現(xiàn)往往造成布局不夠扁平,繪制性能也低下宾娜。
傳統(tǒng)布局的缺點(diǎn)
- 復(fù)雜布局能力差批狐,需要不同布局嵌套使用。
- 布局嵌套層級(jí)高前塔。不同布局的嵌套使用嚣艇,導(dǎo)致布局的嵌套層級(jí)偏高。
- 頁(yè)面性能低华弓。較高的嵌套層級(jí)食零,需要更多的計(jì)算布局時(shí)間,降低了頁(yè)面性能寂屏。
- 按固定寬高比布局等更高階的布局需求贰谣,原先的各類布局方式都不能很好的支持,可能需要通過(guò)Java代碼迁霎,在運(yùn)行中二次實(shí)現(xiàn)吱抚。
因此我們需要一個(gè)更加優(yōu)雅的實(shí)現(xiàn)方式。
ConstraintLayout的出現(xiàn)
在2016年Google I/O大會(huì)時(shí)提出考廉,2017年2月發(fā)布正式版
A ConstraintLayout is a ViewGroup which allows you to position and size widgets in a flexible way.
Note: ConstraintLayout is available as a support library that you can use on Android systems starting with API level 9
(Gingerbread). As such, we are planning on enriching its API and capabilities over time. This documentation will reflect those changes.
Android Studio雖然提供了可視化編寫(xiě)UI的功能频伤,但是在ConstraintLayout出來(lái)之前,我認(rèn)為它一直是雞肋的存在芝此,好無(wú)體驗(yàn)可言憋肖,開(kāi)發(fā)過(guò)ios的都知道xCode 神器storyboard的便利之處,畫(huà)UI幾乎美工都可以完成婚苹,所見(jiàn)即所得岸更,主要還是依賴了ios布局的大量的約束條件,ConstraintLayout的引入解決了Android界面編寫(xiě)的很多痛點(diǎn)膊升,從名字就可以看出怎炊,約束布局通過(guò)豐富的約束條件可以達(dá)到降低頁(yè)面布局層級(jí)、提升頁(yè)面渲染性能的目的。精心使用完全可以替代其他布局评肆。
ConstraintLayout的使用
引入
-
在項(xiàng)目build.gradle文件中引入maven倉(cāng)庫(kù)
repositories {
maven {
url 'https://maven.google.com'
}
}
```
-
在APP build.gradle中依賴約束布局庫(kù)
compile 'com.android.support.constraint:constraint-layout:1.0.2'
-
在布局文件中ConstraintLayout要使用
xmlns:app="http://schemas.android.com/apk/res-auto"
下面的介紹根據(jù)Android文檔進(jìn)行擴(kuò)展
Relative positioning 相對(duì)定位
Relative positioning is one of the basic building block of creating layouts in ConstraintLayout. Those constraints allow you to position a given widget relative to another one. You can constrain a widget on the horizontal and vertical axis:
- Horizontal Axis: left, right, start and end sides
- Vertical Axis: top, bottom sides and text baseline
屬性名 | 含義 |
---|---|
layout_constraintLeft_toLeftOf | 左邊緣和xx左邊緣對(duì)齊 |
layout_constraintLeft_toRightOf | 左邊緣和xx右邊緣對(duì)齊 |
layout_constraintRight_toLeftOf | 右邊緣和xx左邊緣對(duì)齊 |
layout_constraintRight_toRightOf | 右邊緣和xx右邊緣對(duì)齊 |
layout_constraintTop_toTopOf | 上邊緣和xx上邊緣對(duì)齊 |
layout_constraintTop_toBottomOf | 上邊緣和xx下邊緣對(duì)齊 |
layout_constraintBottom_toTopOf | 下邊緣和xx上邊緣對(duì)齊 |
layout_constraintBottom_toBottomOf | 下邊緣和xx下邊緣對(duì)齊 |
layout_constraintBaseline_toBaselineOf | 基于baseline對(duì)齊 |
layout_constraintStart_toEndOf | 起始邊緣和xx結(jié)束邊緣對(duì)齊 |
layout_constraintStart_toStartOf | 起始邊緣和xx起始邊緣對(duì)齊 |
layout_constraintEnd_toStartOf | 結(jié)束邊緣和xx起始邊緣對(duì)齊 |
layout_constraintEnd_toEndOf | 結(jié)束邊緣和xx結(jié)束邊緣對(duì)其 |
嚯溺蕉,一眼看上去ConstraintLayout的屬性名也太長(zhǎng)了吧骑祟,根本沒(méi)法記啊,我一開(kāi)始也是這么認(rèn)為的,官方給出了解釋:
They all take a reference id to another widget, or the parent (which will reference the parent container, i.e. the ConstraintLayout)
總結(jié)規(guī)律就是屬性名里面constraintxxx就是我當(dāng)前控件的某條邊緣组题,toxxxof是我指定的另一個(gè)布局或控件的某條邊緣则剃,作用就是這兩個(gè)邊緣之間的相對(duì)關(guān)系茵肃。如果參照對(duì)象是parent布局诉儒,則可以完成和父布局邊緣對(duì)齊的操作。例如layout_constraintLeft_toLeftOf = "parent"則是居左淆衷。
-
這里baseline做一個(gè)說(shuō)明缸榄,baseline是文字的基線,可以參照某個(gè)控件(i.e. Button)中文字下邊緣對(duì)齊祝拯。
這里寫(xiě)圖片描述
Margins 邊距
屬性和傳統(tǒng)布局相同甚带,這里就不多解釋
屬性名 |
---|
android:layout_marginStart |
android:layout_marginEnd |
android:layout_marginLeft |
android:layout_marginTop |
android:layout_marginRight |
android:layout_marginBottom |
Margins when connected to a GONE widget 和 隱藏空間設(shè)置邊距
When a position constraint target's visibility is View.GONE, you can also indicate a different margin value to be used using the following attributes:
可以和可見(jiàn)性已被設(shè)為View.GONE的控件設(shè)置邊距,常用于在相對(duì)布局中保持各個(gè)控件的位置佳头。因?yàn)槲覀冊(cè)陂_(kāi)發(fā)中常常遇到一個(gè)空間被設(shè)為GONE之后鹰贵,和他有相對(duì)關(guān)聯(lián)的控件的位置會(huì)發(fā)生改變,影響了原有的布局畜晰,導(dǎo)致UI混亂砾莱,用以下屬性可以保持原有設(shè)計(jì):
屬性名 |
---|
layout_goneMarginStart |
layout_goneMarginEnd |
layout_goneMarginLeft |
layout_goneMarginTop |
layout_goneMarginRight |
layout_goneMarginBottom |
Centering positioning and bias 居中定位和偏移
- center_vertical
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- center_horizontal
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
- center
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
-
bias
有人會(huì)說(shuō)我線性布局一行代碼實(shí)現(xiàn)居中,你這也太太太啰嗦了凄鼻,naive腊瑟,下面才是約束布局的新特性,一行代碼實(shí)現(xiàn)固定比例偏移块蚌。比如下面這個(gè)場(chǎng)景闰非,我要讓A左右兩邊的空檔寬度比例為3:7:
這里寫(xiě)圖片描述在沒(méi)了解ConstraintLayout之前,你一定會(huì)想到用LinearLayout配合layout_weight來(lái)完成:
<LinearLayout android:layout_width="match_parent" android:layout_height="100dp" android:orientation="horizontal"> <View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:background="@color/colorAccent" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按鈕" /> <View android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="7" android:background="@color/colorAccent" /> </LinearLayout>
一頓操作猛如虎峭范,一看代碼卻很俗
怎么更加優(yōu)雅的實(shí)現(xiàn)這樣的場(chǎng)景呢财松,ConstraintLayout給出了答案:
屬性名 |
------------- |
layout_constraintHorizontal_bias|水平便宜比例
layout_constraintVertical_bias|豎直便宜比例
一行代碼便可實(shí)現(xiàn) 666:<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/> </>
牛X的還不僅于此,收起下巴纱控,下面的更強(qiáng)大
Circular positioning (Added in 1.1) 1.1版本特性 圓弧定位(自己翻譯)
You can constrain a widget center relative to another widget center, at an angle and a distance. This allows you to position a widget on a circle.
可以根據(jù)兩個(gè)空間的中心位置形成約束關(guān)系辆毡,以當(dāng)前組件中心為圓心設(shè)置半徑和角度來(lái)設(shè)置約束條件
屬性名 | 含義 |
---|---|
layout_constraintCircle | 引用另一個(gè)控件id |
layout_constraintCircleRadius | 到xx中心位置的距離,即圓周半徑 |
layout_constraintCircleAngle | xx所在圓周的角度(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" />
Dimensions constraints 維度約束
- ConstraintLayout自身約束
屬性名 | 含義 |
---|---|
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 |
注意上述屬性設(shè)置ConstraintLayout本身最大最小尺寸只適用于自身為WRAP_CONTENT下
Percent dimension 百分比維度
可以設(shè)置控件相對(duì)于父布局尺寸的百分比甜害,0-1之間舶掖。需要注意:
- 控件的width和height要被設(shè)置為0dp
- 默認(rèn)寬高屬性要被設(shè)置為percent(1.1版本之后將無(wú)需設(shè)置,自動(dòng)識(shí)別)
app:layout_constraintWidth_default="percent"
app:layout_constraintHeight_default="percent"
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.3"
app:layout_constraintHeight_percent="0.2"
/>
設(shè)置按鈕的寬度為父布局寬度的30%尔店,高度為父布局高度的20%眨攘。
Ratio 比例
可以定義一個(gè)控件寬高尺寸比例主慰,這樣可以固定控件的尺寸關(guān)系,形成一種約束鲫售,減少出現(xiàn)UI混亂被擠壓的情況共螺,可以形成自適應(yīng)。但是要注意的事情竹,控件的兩個(gè)維度height和width中至少要有一個(gè)設(shè)置為0或者M(jìn)ATCH_CONSTRAINT藐不,這個(gè)很好理解,不解釋鲤妥。
<Button android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />
這樣就可以不用指定一個(gè)正方形的寬高佳吞,設(shè)計(jì)合理的話可以根據(jù)屏幕自適應(yīng)拱雏。
同樣棉安,也可以只限制其中一個(gè)維度(height或width)隨另一個(gè)維度的變化的比例。
如果一個(gè)維度固定铸抑,只需要在比例前面加上“H"或者”W"表示想要對(duì)哪個(gè)維度進(jìn)行限制贡耽。
<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"/>
上面代碼中按鈕的width將會(huì)充滿父布局,而高度則會(huì)以16:9的比例進(jìn)行測(cè)量繪制鹊汛。
Chains 鏈
chain是ConstraintLayout的新概念蒲赂,可以讓多個(gè)組件在一個(gè)方向上(水平或豎直)組合成一個(gè)集體,官方稱 provide group-like behavior刁憋。
我們知道滥嘴,以往的控件之間的相對(duì)聯(lián)系是一個(gè)控件A基于另一個(gè)控件B的位置關(guān)聯(lián),控件A隨著B(niǎo)的變動(dòng)變動(dòng)若皱,反之則不行尘颓,但是Chain的存在,可以說(shuō)是一個(gè)組合疤苹,讓多個(gè)控件組成了一個(gè)集體互广,一直保持固定的相對(duì)關(guān)系,這樣做的好處是可以減少布局兼容時(shí)造成的混亂惫皱,方便整體復(fù)用尤莺。
-
創(chuàng)建一個(gè)鏈約束
鏈約束的關(guān)鍵在于需要組合在一起的布局之間在同一個(gè)方向上是雙向連接旅敷,也就是相互進(jìn)行相對(duì)約束。
這里寫(xiě)圖片描述對(duì)于 A缝裁、B兩個(gè)button扫皱,形成鏈約束代碼如下:
<Button android:id="@+id/btn1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="A" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@id/btn2" /> <Button android:id="@+id/btn2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="B" app:layout_constraintLeft_toRightOf="@id/btn1" app:layout_constraintRight_toRightOf="parent" />
這里寫(xiě)圖片描述 -
chain heads 鏈頭
鏈?zhǔn)怯涉溕系谝粋€(gè)元素(鏈頭)上的屬性集控制韩脑,鏈頭是最左邊或最上邊的組件。
這里寫(xiě)圖片描述
-
chain style 鏈內(nèi)布局方式
layout_constraintHorizontal_chainStyle 水平鏈內(nèi)布局方式
layout_constraintVertical_chainStyle 豎直鏈內(nèi)布局方式
只要在鏈頭組件設(shè)置chain style首量,便可以控制整條鏈的內(nèi)部組件排列布局方式进苍。
默認(rèn)style為CHAIN_SPREAD,即組件間隔均分可使用空間觉啊。
這里寫(xiě)圖片描述?
性能比較
由于ConstraintLayout天生扁平的優(yōu)點(diǎn)杠人,在性能上肯定要優(yōu)于傳統(tǒng)布局不少,關(guān)于性能對(duì)比的詳細(xì)情況嗡善,可以查看這篇文章了解使用 ConstraintLayout 的性能優(yōu)勢(shì)
布局編輯器(待更新)
代碼設(shè)置布局(待更新)
源碼解析(待更新)
參考文獻(xiàn)
1、 官方文檔
2各吨、Android新特性介紹袁铐,ConstraintLayout完全解析 郭霖
3、ConstraintLayout入門(mén)指南 QQ音樂(lè)技術(shù)團(tuán)隊(duì)