1、介紹
在Android 開發(fā)最常用的是:線性布局LinearLayout和相對(duì)布局RelativeLayout,在使用的過(guò)程中都是各種嵌套,嚴(yán)重影響了代碼的可讀性。
約束布局ConstraintLayout的出現(xiàn)主要是為了解決布局嵌套過(guò)多的問(wèn)題,以靈活的方式定位和調(diào)整小部件。可以在Api9以上的Android系統(tǒng)使用它,從Android Studio 2.3起,官方的模板默認(rèn)使用ConstraintLayout。
2、如何使用ConstraintLayout
對(duì)于剛上手的童鞋,看著各種不習(xí)慣,等使用熟練以后就會(huì)發(fā)現(xiàn)它的各種好處了,代碼終于不用在像以前那樣嵌套到惡心的地步。廢話不多說(shuō),下面看看具體怎么用的。
當(dāng)然英語(yǔ)不錯(cuò)的也可以去讀讀官方文檔,介紹的還是很詳細(xì)的:ConstraintLayout官方文檔
(1)相對(duì)定位,我個(gè)人理解用法上和相對(duì)布局也差不多吧

<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintLeft_toRightOf="@+id/buttonA" />
約束條件如下:
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
這里的屬性對(duì)比上面的一個(gè)例子,相信大家都看的懂了,其中有一個(gè)比較特殊:
layout_constraintBaseline_toBaselineOf,指的是文本基線對(duì)齊

(2)邊距,其實(shí)這個(gè)就沒(méi)什么好說(shuō)的了,和之前的相對(duì)布局和線下布局都一樣,然后就列舉一下吧!
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
(3)goneMargin,主要用于約束的控件可見(jiàn)性被設(shè)置為gone的時(shí)候使用的margin值,這個(gè)就比較人性化了,雖然不是很常用,但是用起來(lái)確實(shí)方便太多了,具體屬性如下:
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
舉個(gè)栗子:如果Button2的左面約束在Button1的右面,Button1設(shè)有10dp的左邊距,Button2沒(méi)有左邊距,但是給Button2設(shè)置10dp的goneMarginStart,現(xiàn)在把Button1可見(jiàn)性設(shè)置為GONE,就會(huì)如下圖顯示Button2會(huì)有10dp的左邊距。

(4)居中和偏移
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
</>
這里是左右居中,上下居中也同理。

然后就是偏移,可以設(shè)置邊距同樣也能達(dá)到偏移的效果,這個(gè)也是大家都知道的,所以再多介紹也沒(méi)什么意義,這里要介紹的是另外兩個(gè)新的屬性:
layout_constraintHorizontal_bias
layout_constraintVertical_bias
例如,下面將使左側(cè)具有30%的偏移而不是默認(rèn)的50%,這樣左側(cè)將更短,小部件將更傾向于左側(cè)。上下偏移這里就不再舉例介紹了。
<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/>
</>

(5)圓形定位,這個(gè)就顯得就比較高端了,雖然感覺(jué)用到的地方不是很多,但就是感覺(jué)很吊的樣子??梢砸砸粋€(gè)角度和一個(gè)距離約束一個(gè)小部件中心相對(duì)于另一個(gè)小部件中心。這允許您在一個(gè)圓上定位一個(gè)小部件??梢允褂靡韵聦傩裕?/p>
layout_constraintCircle : 引用另一個(gè)小部件ID
layout_constraintCircleRadius : 到其他窗口小部件中心的距離
layout_constraintCircleAngle : 小部件應(yīng)該處于哪個(gè)角度(度,從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" />
(6)可見(jiàn)性行為,其實(shí)就是View.GONE,和之前的一樣,需要注意的就是當(dāng)有控件設(shè)置了goneMargin時(shí)的情況,這個(gè)上面有說(shuō)過(guò)。
(7)限制尺寸,約束布局的最小或最大尺寸,可以為約束布局本身定義最小和最大大小,一般設(shè)置寬度使用WRAP_CONTENT :
android:minwidth 設(shè)置布局的最小寬度
android:minheight 設(shè)置布局的最小高度
android:maxwidth 設(shè)置布局的最大寬度
android:maxheight 設(shè)置布局的最大高度
當(dāng)ConstraintLayout為1.1版本以下時(shí),使用以上屬性需要設(shè)置:
app:layout_constrainedWidth=”true|false”
app:layout_constrainedHeight=”true|false”
官方不推薦使用MATCH_CONSTRAINT,建議使用0dp去代替

a、是包裹內(nèi)容,b、設(shè)置了0dp,c、設(shè)置了0dp并設(shè)置了左邊距
下面是c的例子代碼:
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
在約束布局中寬高的維度 match_parent 被 0dp 代替,默認(rèn)生成的大小占所有的可用空間。那么有以下幾個(gè)屬性可以使用:
layout_constraintWidth_min and layout_constraintHeight_min //設(shè)置最小尺寸
layout_constraintWidth_max and layout_constraintHeight_max //設(shè)置最大尺寸
layout_constraintWidth_percent and layout_constraintHeight_percent //設(shè)置相對(duì)于父類的百分比
(8)寬高比,當(dāng)寬或高至少有一個(gè)尺寸被設(shè)置為0dp時(shí),可以通過(guò)屬性layout_constraintDimensionRatio設(shè)置寬高比。
<Button android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />
在設(shè)置寬高比的值的時(shí)候,還可以在前面加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"/>
app:layout_constraintDimensionRatio="H,16:9"指的是 高:寬=16:9
app:layout_constraintDimensionRatio="W,16:9"指的是 寬:高=16:9
(9)鏈,如果兩個(gè)或以上控件通過(guò)下圖的方式約束在一起,就可以認(rèn)為是他們是一條鏈(圖為橫向的鏈,縱向同理)。

鏈頭,鏈由鏈的第一個(gè)元素(鏈的“頭”)上設(shè)置的屬性控制:

<TextView
android:id="@+id/TextView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/TextView2" />
<TextView
android:id="@+id/TextView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/TextView1"
app:layout_constraintRight_toLeftOf="@+id/TextView3" />
<TextView
android:id="@+id/TextView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/TextView2"
app:layout_constraintRight_toRightOf="parent" />
在鏈的第一個(gè)元素上設(shè)置屬性layout_constrainthorizontal_chain style或layout_constraintvertical_chainstyle時(shí),鏈的行為將根據(jù)指定的樣式更改(默認(rèn)為chain_spread)。
1、CHAIN_SPREAD — 元素將展開(默認(rèn)樣式)
2、CHAIN_SPREAD_INSIDE —— 展開元素,但鏈的兩端貼近parent;
3、通過(guò)layout_constrainthorizontal_weight屬性設(shè)置了權(quán)重
4、CHAIN_PACKED —— 鏈的元素將被打包在一起。
5、如果設(shè)置了邊距,排列鏈的時(shí)候?qū)目偟姆峙涞目臻g中扣除。

3、輔助工具
1、Optimizer優(yōu)化器
在1.1中,我們公開了約束優(yōu)化器??梢酝ㄟ^(guò)將layout-optimizationlevel添加到constraintlayout元素來(lái)決定應(yīng)用哪些優(yōu)化。
none:無(wú)優(yōu)化
standard:默認(rèn)。僅優(yōu)化直接約束和屏障約束
direct:優(yōu)化直接約束
barrier:優(yōu)化屏障約束
chain:優(yōu)化鏈約束(實(shí)驗(yàn))
dimensions:優(yōu)化尺寸測(cè)量,減少匹配約束元素的測(cè)量數(shù)
2、Barrier

假設(shè)有3個(gè)控件ABC,C在AB的右邊,但是AB的寬是不固定的,這個(gè)時(shí)候C無(wú)論約束在A的右邊或者B的右邊都不對(duì)。當(dāng)出現(xiàn)這種情況可以用Barrier來(lái)解決。Barrier可以在多個(gè)控件的一側(cè)建立一個(gè)屏障,如下所示:

這個(gè)時(shí)候C只要約束在Barrier的右邊就可以了,代碼如下:
<TextView
android:id="@+id/TextView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/TextView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/TextView1" />
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="TextView1,TextView2" />
<TextView
android:id="@+id/TextView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/barrier" />
app:barrierDirection為屏障所在的位置,可設(shè)置的值有:bottom、end、left、right、start、top
app:constraint_referenced_ids為屏障引用的控件,可設(shè)置多個(gè)(用“,”隔開)
3、Group
Group可以把多個(gè)控件歸為一組,方便隱藏或顯示一組控件,舉個(gè)例子:
<TextView
android:id="@+id/TextView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/TextView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/TextView1" />
<TextView
android:id="@+id/TextView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/TextView2" />
<android.support.constraint.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
app:constraint_referenced_ids="TextView1,TextView3" />
用Group把TextView1和TextView3歸為一組,再設(shè)置這組控件的可見(jiàn)性
4、Placeholder
Placeholder指的是占位符。在Placeholder中可使用setContent()設(shè)置另一個(gè)控件的id,使這個(gè)控件移動(dòng)到占位符的位置。舉個(gè)例子:
<android.support.constraint.Placeholder
android:id="@+id/placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:content="@+id/textview"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#cccccc"
android:padding="16dp"
android:text="TextView"
android:textColor="#000000"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
新建一個(gè)Placeholder約束在屏幕的左上角,新建一個(gè)TextView約束在屏幕的右上角,在Placeholder中設(shè)置 app:content="@+id/textview",這時(shí)TextView會(huì)跑到屏幕的左上角。效果如下:
5、Guideline
Guildline像輔助線一樣,在預(yù)覽的時(shí)候幫助你完成布局(不會(huì)顯示在界面上)。
Guildline的主要屬性:
android:orientation 垂直vertical,水平horizontal
layout_constraintGuide_begin 開始位置
layout_constraintGuide_end 結(jié)束位置
layout_constraintGuide_percent 距離頂部的百分比(orientation = horizontal時(shí)則為距離左邊)
舉個(gè)例子:
<android.support.constraint.Guideline
android:id="@+id/guideline1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="50dp" />
<android.support.constraint.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
guideline1為水平輔助線,開始位置是距離頂部50dp,guideline2位垂直輔助線,開始位置為屏幕寬的0.5(中點(diǎn)位置),效果如下:

4、總結(jié)
本篇文章主要介紹了ConstraintLayout的用法,剛開始可能會(huì)感覺(jué)到用的各種不習(xí)慣,用著用著就習(xí)慣了。ConstraintLayout的初衷是為了實(shí)現(xiàn)托拉拽完成布局,但本人還是比較喜歡直接用代碼寫,所以才專門研究一下,純粹個(gè)人喜好問(wèn)題,至于拖拽大家自己研究了。