因文章發(fā)布年代久遠(yuǎn),下面放出更好更新教程的地址:
ConstraintLayout 約束布局,是16年 google 新出的,經(jīng)過(guò)一年的發(fā)展,在17年中旬大量曝光,好多大神的微信訂閱號(hào)和 google 官方都在推薦使用,為啥,真的好用唄。
簡(jiǎn)單來(lái)說(shuō)下ConstraintLayout ,其實(shí)他是 RelativeLayout 的進(jìn)化版,把線性布局的一些特點(diǎn)融合進(jìn)了相對(duì)布局中,比如weight,提供了更多的控件排列方式,性能上也有優(yōu)勢(shì),RelativeLayout 天然的會(huì)執(zhí)行2遍 measu,ConstraintLayout 最大的好處也是我們最看中的就是可以減少布局層級(jí),提高頁(yè)面繪制效率,官方測(cè)試的 demo 控件很少還可以提高40%的性能呢。
ConstraintLayout 的特點(diǎn)
有必要在想大家介紹如何使用 ConstraintLayout 之前,讓大家了解下 ConstraintLayout 的新東西都有啥:
- 支持控件的 W/H 按照比例顯示,比如圖片按4:3顯示
- 在相對(duì)布局中支持 weight 比重
- 優(yōu)化 view 在 gone 后不會(huì)丟位置,以防顯示錯(cuò)亂
- chain 鏈,支持指定的 view 組成橫向/縱向的顯示排列
- 支持view 按照輔助線 guideView 定位顯示
- 支持 view 上下邊距按照比例顯示
來(lái)看下 ConstraintLayout 的屬性及和 RelativeLayout 的對(duì)比
ConstraintLayout 的屬性如下:
和 RelativeLayout 的屬性對(duì)比
既然都說(shuō)了ConstraintLayout 是 RelativeLayout 的進(jìn)化版,那么基礎(chǔ)屬性使用那肯定和之前的一樣,就是名字改了下,注意一下規(guī)律即可完成轉(zhuǎn)換:
- 使用 parent 代替父布局
- app:layout_constraintLeft_toLeftOf="parent" / "@+id/view_text13"
to左右邊兩邊一樣,參數(shù)填入parent表示在父布局的那邊,也就是和父布局的那邊對(duì)齊,參數(shù)填入具體的 view id 表示和目標(biāo)的 view 的那邊對(duì)齊,比如這個(gè)就表示我的左側(cè)和 view 的左側(cè)對(duì)齊 - app:layout_constraintLeft_toRightOf="@+id/view_text11"
to左右邊兩邊不一樣,看右邊的是哪個(gè)方便,就表示我在 view 的那邊,比如這個(gè)就表示我在 view 的右邊 - 注意橫向居中,和縱向居中和以前寫法和以前完全不同,看上圖屬性對(duì)照,其實(shí)很好理解的,不細(xì)說(shuō)了
- match_parent 這個(gè)參數(shù)無(wú)效了,雖然還能使用,但是沒(méi)用了,0dp 表示match_parent,但是也表示 W/H 由具體的約束條件來(lái)計(jì)算,這個(gè)具體要結(jié)合下面的講解來(lái)看,這里要特別注意,0dp 有時(shí)也是還是表示原始0dp 的意思的,要注意和約束配合使用
好了,看完屬性的轉(zhuǎn)換,基本我們就可以使用 ConstraintLayout 了,下面我們正式開(kāi)始
添加依賴
ConstraintLayout 是以依賴包的方式添加進(jìn)來(lái)的
compile 'com.android.support.constraint:constraint-layout:1.0.2'
先寫一個(gè)簡(jiǎn)單的 ConstraintLayout 示例
上圖這個(gè)簡(jiǎn)單的布局用 ConstraintLayout 怎么寫,還是得先再熟悉熟悉從 RelativeLayout 到 ConstraintLayout 的轉(zhuǎn)換
<View
android:id="@+id/view_x01"
android:layout_width="200dp"
android:layout_height="0dp"
android:layout_marginTop="50dp"
android:background="@color/colorAccent"
app:layout_constraintDimensionRatio="H,4:3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"></View>
<TextView
android:id="@+id/view_x02"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="!我是測(cè)試啊測(cè)試!!我是測(cè)試啊測(cè)試!!我是測(cè)試啊測(cè)試!!我是測(cè)試啊測(cè)試!!我是測(cè)試啊測(cè)試!!"
app:layout_constraintLeft_toRightOf="@+id/view_x01"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/view_x01"/>
<TextView
android:id="@+id/view_x03"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="我是大圣!!"
app:layout_constraintBottom_toBottomOf="@+id/view_x01"
app:layout_constraintLeft_toRightOf="@+id/view_x01"
app:layout_constraintRight_toRightOf="parent"/>
還是很簡(jiǎn)單的,大家回味一下,下面開(kāi)始 ConstraintLayout 的新特征了
按寬高比例顯示
舉個(gè)例子,我想讓一個(gè)圖片按照固定的例子顯示,比如 4:3的比例,先看代碼
<ImageView
android:id="@+id/view_top"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="centerCrop"
android:src="@drawable/x01"
app:layout_constraintDimensionRatio="H,4:3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
效果:
這個(gè)設(shè)置有點(diǎn)繞,需要 W/H 設(shè)置和約束條件密切配合,缺一個(gè)都出不來(lái)效果:
- 約束屬性:
app:layout_constraintDimensionRatio="H,4:3",就是這個(gè)啦,打擊一看也能發(fā)現(xiàn)他不是,我來(lái)說(shuō)下這個(gè)設(shè)置的意思啊,有點(diǎn)不好理解,你去別的帖子找絕對(duì)沒(méi)我說(shuō)的到點(diǎn)上: - 首先注意這個(gè) "H,4:3" ,這個(gè)view 寬高比是 4:3,以 W 為基準(zhǔn)動(dòng)態(tài)設(shè)置 H 高這個(gè)屬性,就是 "H,4:3" 這個(gè)里面,我們寫 W 和 H 誰(shuí),就表示以另外一個(gè)邊為基準(zhǔn),動(dòng)態(tài)計(jì)算修改我們寫的這個(gè)邊的值,我們寫的這個(gè)邊一定要設(shè)置成0dp 表示由約束條件來(lái)計(jì)算具體值才行,寫 warpcontent 不行,不信的可以試試
- 我們需要注意這個(gè)基準(zhǔn)邊,基準(zhǔn)邊必須是一個(gè)具體值,要不基準(zhǔn)邊取不到一個(gè)具體( 如100dp),后面怎么計(jì)算
- 這個(gè)基準(zhǔn)邊我們要是填滿父布局的寬怎么辦,match_parent 不能用了,寫0dp 的話打擊可以自己試試顯示不出來(lái),這時(shí)寫0dp 就表示沒(méi)有寬高的意思了,但是 ConstraintLayout 用0dp 代替了match_parent 了,那怎么辦啊,經(jīng)過(guò)調(diào)試我們加上
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
就行了,為啥啊,可以這樣解釋:在有約束條件時(shí) 0dp 表示控件的具體寬高由約束條件計(jì)算而來(lái),這里我們控件設(shè)置成橫向居中就是代替了 match_parent 了,在 0dp 不能表示 match_parent 時(shí),我們都可以這樣些,主力 千萬(wàn)要注意,縱向也是同理的
- 結(jié)合上面的代碼,我們圖片想占滿屏幕的款,然后按4:3的比例來(lái)顯示。可以看到imageview的寬高都設(shè)置成0dp,然后添加了橫向居中條件,大家可以自己試試,少一個(gè)看看會(huì)是什么樣的。
chain 鏈
chain 鏈這是新加入的東西,其實(shí)也是很好理解,就是讓一組 view l定位,形成橫向/縱向的的排列,其實(shí)就是類似于線性布局的方向。
解釋下兩兩相互依賴:A 在 B 的左面,B 在A的右邊,這就是組成了 chain ,chain 可以多個(gè) view 相互組合依賴,數(shù)量不限
需要注意的是,view 組成 chain 鏈之后,首位置的 view 的 magin,padding 會(huì)失效,這時(shí)我們可以使用 layout_editor_absoluteY / layout_editor_absoluteX 來(lái)替代 magin。
<TextView
android:id="@+id/view_text11"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name11"
app:layout_constraintBottom_toTopOf="@+id/view_text21"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_text12"
app:layout_constraintVertical_chainStyle="packed"/>
<TextView
android:id="@+id/view_text12"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="name12"
app:layout_constraintLeft_toRightOf="@+id/view_text11"
app:layout_constraintRight_toLeftOf="@+id/view_text13"
app:layout_constraintTop_toTopOf="@+id/view_text11"/>
<TextView
android:id="@+id/view_text13"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name13"
app:layout_constraintLeft_toRightOf="@+id/view_text12"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/view_text12"/>
<TextView
android:id="@+id/view_text21"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name21"
app:layout_constraintBottom_toTopOf="@+id/view_text31"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_text22"
app:layout_constraintTop_toBottomOf="@+id/view_text11"/>
<TextView
android:id="@+id/view_text22"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="name22"
app:layout_constraintLeft_toRightOf="@+id/view_text21"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/view_text21"
/>
<TextView
android:id="@+id/view_text31"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name31"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view_text21"/>
<TextView
android:id="@+id/view_text32"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="name32"
app:layout_constraintLeft_toRightOf="@+id/view_text31"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/view_text31"
/>
效果圖:
可以看到有3行,縱向方向是組成一個(gè) chain,第一行橫向也是一個(gè) chain。每一個(gè) chain 的第一個(gè) view 有個(gè)參數(shù)可以設(shè)置整個(gè) chain 的樣式, app:layout_constraintHorizontal_chainStyle="packed" 就是這個(gè)參數(shù),這個(gè)參數(shù)就是設(shè)置這組 chain 的排列樣式,我覺(jué)得沒(méi)啥實(shí)際意義,需求場(chǎng)景很少碰到這種樣式,其他的樣式效果如下:
官網(wǎng)有個(gè)圖:
weight 比重
weight 就是線性布局的那一套,用法也一樣,比如一組橫向的 view 直接用 weight 和組成 chain 再使用 weight 效果都一樣,代碼簡(jiǎn)單一看就會(huì)
<TextView
android:id="@+id/view_text11"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name11"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_text12"/>
<TextView
android:id="@+id/view_text12"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="name12"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/view_text11"
app:layout_constraintRight_toLeftOf="@+id/view_text13"
app:layout_constraintTop_toTopOf="@+id/view_text11"/>
<TextView
android:id="@+id/view_text13"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name13"
app:layout_constraintLeft_toRightOf="@+id/view_text12"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/view_text12"/>
效果圖:
Guideline 輔助線
輔助線就是用來(lái)定位的,幫助 view 定位顯示的,有橫線和豎線,可以部分用來(lái)做屏幕適配,看需求,屬性如下:
- layout_constraintGuide_begin
- layout_constraintGuide_end
- layout_constraintGuide_percent
可以通過(guò)上面3個(gè)屬性其中之一來(lái)確定屬性值位置。
begin=30dp,即可認(rèn)為距離頂部30dp的地方有個(gè)輔助線,根據(jù)orientation來(lái)決定是橫向還是縱向。
end=30dp,即為距離底部。 percent=0.8即為距離頂部80%。
比如我要讓 view 左上角位于屏幕的中間處顯示
<android.support.constraint.Guideline
android:id="@+id/guide_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"
/>
<android.support.constraint.Guideline
android:id="@+id/guide_v"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"
/>
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/x01"
app:layout_constraintLeft_toRightOf="@+id/guide_v"
app:layout_constraintTop_toBottomOf="@+id/guide_h"
/>
先聲明2個(gè)輔助線,然后就可以在輔助線的上下左右做顯示,用法簡(jiǎn)單,一看就會(huì),注意是作用于 view 的四周邊緣,而不是中心點(diǎn)啊。
bia 邊距
這個(gè)也是一個(gè)比例,不過(guò)不是 view 的 W/H 了,而是view上下/左右邊距的比例,參數(shù)如下
app:layout_constraintHorizontal_bias="0.8"
這表示做邊距占空余控件的80%,看圖就好理解了:
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/x01"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.8"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.8"
/>
這是一個(gè)比例,比較適合FloatingActionButton
Guideline 和 bias 的區(qū)別
剛看這2個(gè)有些混,記住 Guideline 是絕對(duì)位置,bias 是相對(duì)位置,集合一張對(duì)比圖來(lái)看,Guideline 和 bias 都設(shè)置0.9
看到對(duì)比就好理解了,圖右下角出去的那個(gè)就是 Guideline 輔助線,能顯示全的那個(gè)是 bias
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/x01"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.9"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.9"
/>
<android.support.constraint.Guideline
android:id="@+id/guide_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.9"
/>
<android.support.constraint.Guideline
android:id="@+id/guide_v"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.9"
/>
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/x01"
app:layout_constraintLeft_toRightOf="@+id/guide_v"
app:layout_constraintTop_toBottomOf="@+id/guide_h"
/>
view gone 的優(yōu)化
在相對(duì)布局時(shí),如果用于定位錨點(diǎn)的 view 我們 gone 不顯示了,那么參照這個(gè)錨點(diǎn)的 view 都在在左上角顯示,造成顯示錯(cuò)亂,這次在ConstraintLayout中,google 應(yīng)該是優(yōu)化了這一點(diǎn),經(jīng)過(guò)測(cè)試,錨點(diǎn) view 在 gone 后,實(shí)際上是寬高都會(huì)被置為0,magin 和 paddi哪個(gè)會(huì)無(wú)效,但是位置還在,依賴于這個(gè)錨點(diǎn)的 view 的顯示不會(huì)想相對(duì)布局一樣錯(cuò)亂,還專門有一組 gone 打頭的參數(shù),在 錨點(diǎn)view gone 之后,依賴于錨點(diǎn) view 的其他 view 以 goneMagin 作為新的 magin 用于重新定位,常用與橫向的一組 view,比如設(shè)置頁(yè)面,每行的右邊根據(jù)狀態(tài)不同顯示不同的按鈕
還是用說(shuō) chain 時(shí)使用的那3行 view,我們讓中間那行的第一個(gè) gone,看看效果
看到?jīng)],第三行顯示沒(méi)有錯(cuò)位,第二行右邊的 view 實(shí)際上還是顯示在第二行,只不過(guò)占全屏的寬度,并被第三行擋住了,而我們 gone 的 view 呢,看我畫紅圈的位置,實(shí)際不是不顯示了,是寬高為置為0從而造成的不顯示,這樣比相對(duì)布局真是好的太多了,相對(duì)布局因?yàn)閼?yīng)付這種情況,我是會(huì)使用好幾層用來(lái)占位的無(wú)用的 layout 的
<TextView
android:id="@+id/view_text11"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name11"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_text12"/>
<TextView
android:id="@+id/view_text12"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="name12"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/view_text11"
app:layout_constraintRight_toLeftOf="@+id/view_text13"
app:layout_constraintTop_toTopOf="@+id/view_text11"/>
<TextView
android:id="@+id/view_text13"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name13"
app:layout_constraintLeft_toRightOf="@+id/view_text12"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/view_text12"/>
<TextView
android:visibility="gone"
android:id="@+id/view_text21"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name21"
app:layout_constraintBottom_toTopOf="@+id/view_text31"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/view_text22"
app:layout_constraintTop_toBottomOf="@+id/view_text11"/>
<TextView
android:id="@+id/view_text22"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="name22"
app:layout_constraintLeft_toRightOf="@+id/view_text21"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/view_text21"
/>
<TextView
android:id="@+id/view_text31"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:text="name31"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view_text21"/>
<TextView
android:id="@+id/view_text32"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="name32"
app:layout_constraintLeft_toRightOf="@+id/view_text31"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/view_text31"
/>