ConstraintLayout 用法

當前描述是基於constraint-layout:1.1.2。android

1、前言

在之前,android是使用佈局如LinearLayout 、RelativeLayout等來構建頁面,但這些佈局使用起來很麻煩,而且常常須要一層一層嵌套,寫一個簡單的頁面就須要費很大的勁。因此在16年I/O大會上,google發佈了全新的佈局-ConstraintLayout,其餘佈局和ConstraintLayout比起來,根本就沒有存在的必要了...
ConstraintLayout具備如下優點:api

  1. 較高的性能優點。
    佈局嵌套層次越高,性能開銷越大。而使用ConstraintLayout,常常就一層嵌套就搞定了,因此其性能要好不少。app

  2. 完美的屏幕適配
    ConstraintLayout的大小、距離均可以使用比例來設置,因此其適配性更好。編輯器

  3. 書寫簡單ide

  4. 可視化編輯。
    ConstraintLayout也有十分方便完善的可視化編輯器,不用寫xml也基本上能實現大部分功能。但我的仍是比較喜歡寫xml,因此本篇文章主要介紹如何使用代碼控制。若是想看如何使用可視化編輯器,能夠參考郭霖大神的這篇文章佈局

1 引入:api 'com.android.support.constraint:constraint-layout:1.1.2'

2、ConstraintLayout

1. 定位位置

肯定位置的屬性提供了下面13個屬性,其實本質上都是同樣的,看名字應該基本上都知道怎麼用了(就是哪一條邊和哪一條邊對齊)性能

  • 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

來看個例子:動畫

實現上述UI的相關代碼以下:
 1 <android.support.constraint.ConstraintLayout 
 2     ...>
 3 
 4     <Button
 5         android:id="@+id/a"
 6          ....
 7         app:layout_constraintLeft_toLeftOf="parent"
 8         app:layout_constraintTop_toTopOf="parent"
 9         android:text="A" />
10 
11     <Button
12         android:id="@+id/b"
13         ....
14         app:layout_constraintLeft_toRightOf="@id/a"
15         app:layout_constraintTop_toTopOf="@id/a"
16         android:text="B" />
17 
18     <Button
19         android:id="@+id/c"
20          ....
21         app:layout_constraintLeft_toLeftOf="@id/a"
22         app:layout_constraintTop_toBottomOf="@id/a"
23         android:text="C" />
24 
25     <Button
26         android:id="@+id/d"
27          ....
28         app:layout_constraintLeft_toRightOf="@id/a"
29         app:layout_constraintTop_toTopOf="@id/c"
30         android:text="D" />
31 </android.support.constraint.ConstraintLayout>

從中能夠看到,ui

  • layout_constraint*屬性的值能夠是某個id或者parent(父佈局)
  • B要位於A的右邊,則使用app:layout_constraintLeft_toRightOf="@id/a",C位於A的下邊,則使用app:layout_constraintTop_toBottomOf="@id/a"

對於一個View的邊界界定,官方給了下面這張圖:google

 

2. margin

設置margin仍是繼續用之前的屬性layout_margin* 。
不過須要注意,要使margin生效,必須具備對應方向的layout_constraint*,不然margin不生效.

3. 關於view gone

假如如今有以下佈局:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout 
 3     ...>
 4 
 5     <Button
 6         android:id="@+id/a"
 7         ...
 8         android:layout_marginLeft="100dp"
 9         android:layout_marginTop="20dp"
10         app:layout_constraintLeft_toLeftOf="parent"
11         app:layout_constraintTop_toTopOf="parent" />
12 
13     <Button
14         android:id="@+id/b"
15         ...
16         android:layout_marginLeft="20dp"
17         android:layout_marginTop="20dp"
18         app:layout_constraintLeft_toRightOf="@id/a"
19         app:layout_constraintTop_toTopOf="@id/a"
20          />
21 
22     <Button
23         android:id="@+id/c"
24        ....
25         android:layout_marginLeft="20dp"
26         android:layout_marginTop="20dp"
27         app:layout_constraintLeft_toRightOf="@id/b"
28         app:layout_constraintTop_toTopOf="@id/b" />
29 </android.support.constraint.ConstraintLayout>

考慮一個問題,若是B動態設爲gone了,C會怎麼顯示呢?
真實狀況以下:

爲何會這樣顯示呢?看他的藍圖應該會好理解些:

能夠看出,b設爲gone以後,他的寬、高、margin都失效了,變爲一個點了,但它的constrain還生效,位於指定的位置。c仍是能夠繼續以他爲錨點。
那麼如何解決關於View gone引發的非預期的佈局變化呢?
  1. 若是能夠,儘可能使用invisible
  2. 儘可能其餘view的佈局不依賴會gone的view
  3. google也提供了屬性layout_goneMargin*="xdp",意思是好比當constrainleft的錨點gone時,layout_goneMarginLeft將生效。但由於這個只能設置固定的距離,我的感受靈活性不是很高。

4. 居中及bias

一個view如何設置爲居中呢?若是查找屬性,會發現並無如RelativeLayout相似的layout_centerVertical屬性,那如何設置居中呢?constraint的思想很巧妙。
根據第一節的知識,你們知道若是設置app:layout_constraintLeft_toLeftOf="parent",則view會貼着父view的左邊,設置app:layout_constraintRight_toRightOf="parent" 則會貼着右邊,那若是兩個都設置,效果會怎樣呢?

如圖,兩個都設置,view則會居中。
至此能夠看出,對constraint的理解其實能夠當作是像兩個彈簧同樣,若是隻在左邊加一個彈簧,右邊沒有,那左邊的勢必會把view拉到左邊去,若是在右邊也加一根彈簧,兩個彈簧力相互平衡,則view就居中了。
上面是view居中,若是我想讓view向左偏一些,或者位於1/3處該怎麼處理?其實也是同樣的,想象一下,若是左邊的彈簧力大一些,view不是就天然往左偏了嘛。如何使力大一些呢?使用以下屬性

 

  • layout_constraintHorizontal_bias
  • layout_constraintVertical_bias

bias即偏移量,他們的取值範圍從0~1,0即挨着左邊,1是挨着右邊,因此要使處於1/3處,能夠設置以下屬性app:layout_constraintHorizontal_bias="0.33",效果圖以下:

5.view的尺寸

設置view的大小除了傳統的wrap_content、指定尺寸、match_parent(雖然官方不推薦使用match_parent)外,還能夠設置爲0dp(官方取名叫MATCH_CONSTRAINT),0dp在constraint可不是指大小是0dp,而是有特殊含義的。他的做用會隨着不一樣的設置有不一樣的含義:

  1. layout_constraintWidth_default
    layout_constraintWidth_default有三個取值,做用以下:
  • spread,默認值,意思是佔用全部的符合約束的空間
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout 
 3   ...>
 4 
 5     <Button
 6         android:id="@+id/a"
 7         android:layout_width="0dp"
 8         ...
 9         android:layout_marginLeft="20dp"
10         android:layout_marginRight="20dp"
11         app:layout_constraintRight_toRightOf="parent"
12         app:layout_constraintLeft_toLeftOf="parent"/>
13 
14 </android.support.constraint.ConstraintLayout>

能夠看到layout_width爲0dp,實際的效果則是寬度和約束同樣,左右兩邊的留白是margin的效果。

  • percent,意思是按照父佈局的百分比設置,須要layout_constraintWidth_percent設置百分比例

 

 

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout >
 3 
 4     <android.support.constraint.ConstraintLayout
 5         android:layout_width="300dp"
 6         android:layout_height="400dp"
 7         app:layout_constraintHorizontal_bias="0.3"
 8         >
 9 
10         <Button
11             android:id="@+id/a"
12             android:layout_width="0dp"
13             ...
14             app:layout_constraintRight_toRightOf="parent"
15             app:layout_constraintWidth_default="percent"
16             app:layout_constraintWidth_percent="0.4" />
17     </android.support.constraint.ConstraintLayout>
18 
19 </android.support.constraint.ConstraintLayout>

A的寬度設爲0.4,則其寬度爲父佈局的0.4倍。另外,設置了layout_constraintWidth_percent屬性,能夠不用指定layout_constraintWidth_default,他會自動設置爲percent

  • wrap,意思匹配內容大小但不超過約束限制,注意和直接指定寬度爲wrap_content的區別就是不超過約束限制,以下:

 

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout 
 3     ...>
 4 
 5 
 6     <Button
 7         android:id="@+id/a"
 8         ...
 9         app:layout_constraintLeft_toLeftOf="parent" />
10     <Button
11         android:id="@+id/c"
12         ...
13         app:layout_constraintRight_toRightOf="parent" />
14 
15     <Button
16         android:id="@+id/b"
17         android:layout_width="0dp"
18          ...
19         app:layout_constraintWidth_default="wrap"
20         app:layout_constraintLeft_toRightOf="@id/a"
21         app:layout_constraintRight_toLeftOf="@id/c"/>
22 
23     <Button
24         android:id="@+id/d"
25         android:layout_width="wrap_content"
26         ...
27         app:layout_constraintTop_toBottomOf="@id/b"
28         app:layout_constraintLeft_toRightOf="@id/a"
29         app:layout_constraintRight_toLeftOf="@id/c"/>
30 
31 </android.support.constraint.ConstraintLayout>

能夠看到雖然文字很長,但第一行的綠色button寬度達到約束時,就不在增長,而第二行的button顯示了完整的內容,超過約束的限制。
在1.1上 對於wrap_content會超過約束限制,谷歌又新增了以下屬性

  • app:layout_constrainedWidth=」true|false」
  • app:layout_constrainedHeight=」true|false」

設置爲true也能夠限制內容不超過約束(這樣感受layout_constraintWidth_default這個屬性已經沒什麼用了)

  1. ratio
    layout_constraintDimensionRatio,即寬和高成必定的比例,其值能夠是"width:height"的形式,也能夠是width/height的值。該屬性生效的前提:寬和高其中有一項爲0dp,有constraint。下面按照有幾個0dp來分別介紹下:
  • 若是隻有一項爲0dp,則該項值按照比例計算出來。好比高爲20dp,寬爲0dp,radio爲"2:1",則最終寬爲40dp
  • 若是兩項都爲0dp,則尺寸會設置爲知足約束的最大值並保持比例。由於這是系統計算的,有的時候不是咱們想要的,咱們也能夠經過在前面加H、W來指定是哪個邊須要計算。例如"H,2:1",則是指寬度匹配約束,高度是寬度的1/2
  1. max min
    有以下屬性能夠設置其的最大最小值,含義如字面值同樣:
  • layout_constraintWidth_min
  • layout_constraintWidth_max
  • layout_constraintHeight_max
  • layout_constraintHeight_min
  1. weight
    該屬性在下面講解

6. 鏈

如圖,在一個水平或者豎直方向上,一排view兩兩互相約束,即爲鏈

 

鏈的第一個元素稱爲鏈頭,能夠經過設置layout_constraintHorizontal_chainStyle來控制鏈的分佈形式

 

    • spread
      默認模式,分佈樣式如上圖

    • spread_inside
      如圖,和spread的區別是沒算兩端的約束

鏈的第一個元素稱爲鏈頭,能夠經過設置layout_constraintHorizontal_chainStyle來控制鏈的分佈形式

 

    • spread
      默認模式,分佈樣式如上圖

    • spread_inside
      如圖,和spread的區別是沒算兩端的約束

packed
全部元素擠在中間,也能夠配合使用bias來改變位置偏移

能夠看出,鏈與LinearLayout效果大體同樣。和LinearLayout同樣,鏈也可使用layout_constraintHorizontal_weight,來分割剩餘空間。但又和 android:layout_weight不太同樣,不同的地方以下:

  • layout_weight ,無論當前view的大小設的是多大,都會繼續佔據剩餘空間
  • layout_constraintHorizontal_weight,這個只對0dp而且layout_constraintWidth_default爲spread的view生效,使其大小按比例分割剩餘空間,對於已經設定大小的view不生效

以下面的示例:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout 
 3     ...>
 4 
 5     <LinearLayout
 6         ...
 7         android:orientation="horizontal">
 8         <Button
 9             android:layout_width="10dp"
10             android:layout_height="50dp"
11             android:layout_weight="1"
12             ... />
13         <Button
14             android:layout_width="wrap_content"
15             android:layout_height="50dp"
16             android:layout_weight="1"
17             ... />
18         <Button
19             android:layout_width="0dp"
20             android:layout_height="50dp"
21             android:layout_weight="1"
22             ... />
23     </LinearLayout>
24 
25     <android.support.constraint.ConstraintLayout
26         ....>
27 
28         <Button
29             android:id="@+id/a"
30             android:layout_width="10dp"
31             android:layout_height="50dp"
32             ....
33             app:layout_constraintHorizontal_weight="1"
34             app:layout_constraintLeft_toLeftOf="parent"
35             app:layout_constraintRight_toLeftOf="@id/b" />
36         <Button
37             android:id="@+id/b"
38             android:layout_width="wrap_content"
39             android:layout_height="50dp"
40             ....
41             app:layout_constraintHorizontal_weight="1"
42             app:layout_constraintLeft_toRightOf="@id/a"
43             app:layout_constraintRight_toLeftOf="@id/c" />
44 
45         <Button
46             android:id="@+id/c"
47             android:layout_width="0dp"
48             android:layout_height="50dp"
49             ...
50             app:layout_constraintHorizontal_weight="1"
51             app:layout_constraintLeft_toRightOf="@id/b"
52             app:layout_constraintRight_toRightOf="parent" />
53 
54         />
55     </android.support.constraint.ConstraintLayout>
56 
57 </LinearLayout>

能夠看出,LinearLayout和ConstraintLayout雖然三個子view的layout_width值是同樣的,weight也都設置了1,但效果徹底不同

7. 圓形佈局

ConstraintLayout還提供了一種比較炫酷的圓形佈局,這是以往的佈局所作不到的。涉及到的屬性也很簡單,就下面三個:

  • layout_constraintCircle : 圓心,值是某個view的id
  • layout_constraintCircleRadius : 半徑
  • layout_constraintCircleAngle :角度,值是從0-360,0是指整上方

示例以下:

 

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3    ...>
 4 
 5    <Button
 6        android:id="@+id/a"
 7        ...
 8        />
 9 
10    <Button
11        android:id="@+id/b"
12        ...
13        app:layout_constraintCircle="@id/a"
14        app:layout_constraintCircleAngle="300"
15        app:layout_constraintCircleRadius="100dp" />
16 
17    <Button
18        android:id="@+id/c"
19        ...
20        app:layout_constraintCircle="@id/a"
21        app:layout_constraintCircleAngle="45"
22        app:layout_constraintCircleRadius="200dp" />
23    />
24 </android.support.constraint.ConstraintLayout>

3、輔助組件

除了ConstraintLayout自身屬性以外,谷歌還提供了不少輔助佈局(只是在佈局中起輔助做用,並不會在界面真正顯示),來使ConstraintLayout的功能更增強大。下面,咱們就一一來了解下這些佈局

1. GuideLine

即參考線的意思,有水平參考線和豎直參考線兩種。他的做用就像是一個虛擬的參考線,只是用來方便其餘View以他爲錨點來佈局。
如上一篇所瞭解到的,ConstraintLayout 的定位原則就是一個View參考其餘View的相對佈局,若是有的時候當前佈局沒有合適的參考View,而建一個專門用於定位的View又會過重,這種狀況正是GuideLine的用武之地。
例如:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout 
 3     ...>
 4 
 5     <android.support.constraint.Guideline
 6         android:id="@+id/guideline"
 7         ...
 8         android:orientation="vertical"
 9         app:layout_constraintGuide_percent="0.33" />
10 
11     <android.support.constraint.Guideline
12         android:id="@+id/guideline2"
13         ...
14         android:orientation="horizontal"
15         app:layout_constraintGuide_begin="130dp" />
16 
17     <Button
18         ...
19         app:layout_constraintLeft_toLeftOf="@id/guideline"
20         app:layout_constraintTop_toTopOf="@id/guideline2" />
21 
22 
23 </android.support.constraint.ConstraintLayout>

能夠看到我分別添加了一個水平參考線和豎直參考線,以後的Button的佈局就參考與這兩個參考線,而在佈局中並不會顯示。
Guideline的大部分的屬性如layout_width都是不會生效的,而他的位置的肯定是由下面三個屬性之一來肯定的:

  • layout_constraintGuide_begin:距離父佈局的左邊或者上邊多大距離
  • layout_constraintGuide_end:距離父佈局的右邊或者下邊多大距離
  • layout_constraintGuide_percent:百分比,0~1,距離父佈局的左邊或者上邊佔父佈局的比例

2. Group

Group是一個能夠同時控制多個view 可見性的虛擬View。
例如:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout 
 3   ...>
 4 
 5     <android.support.constraint.Group
 6        ...
 7         android:visibility="invisible"
 8         app:constraint_referenced_ids="a,c" />
 9 
10     <android.support.constraint.Group
11         ...
12         android:visibility="visible"
13         app:constraint_referenced_ids="b,d" />
14 
15     <Button
16         android:id="@+id/a"
17         ... />
18 
19     <Button
20         android:id="@+id/b"
21         ... />
22 
23     <Button
24         android:id="@+id/c"
25        ... />
26 
27     <Button
28         android:id="@+id/d"
29         .../>
30 </android.support.constraint.ConstraintLayout>

能夠看到,第一個Group經過app:constraint_referenced_ids指定了a、c兩個控件,這樣當該Group可見性爲invisible時,a、c的可見性都會變爲invisible,爲gone則都爲gone。因此Group很適合處理有網無網之類的場景,再也不須要像以前那樣一個一個view控制可見性,經過Group就能夠統一處理了。
Group有一些注意事項:

  • xml中,可見性配置的優先級:Group優先於View,下層Group優先於上層。
  • Group只能夠引用當前ConstraintLayout下的View,子Layout 下的View不能夠。
  • app:constraint_referenced_ids裏直接寫的是id的字符串,初始化後會經過getIdentifier來反射查找叫該名字的id。因此若是你的項目用了相似AndResGuard的混淆id名字的功能,切記不要混淆app:constraint_referenced_ids裏的id,不然在release版本就會因找不到該id而失效。或者也能夠經過代碼setReferencedIds來設置id。

3. Placeholder

佔位佈局。他本身自己不會繪製任何內容,但他能夠經過設置app:content="id",將id View的內容繪製到本身的位置上,而原id的 View就像gone了同樣。
以下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout 
 3 ...>
 4     <Button
 5         android:id="@+id/a"
 6         android:layout_width="50dp"
 7         android:layout_height="50dp"
 8         android:layout_marginLeft="30dp"
 9        ...
10         app:layout_constraintLeft_toLeftOf="parent"
11         app:layout_constraintTop_toTopOf="parent" />
12 
13     <Button
14         android:id="@+id/b"
15         android:layout_width="50dp"
16         android:layout_height="50dp"
17         android:layout_marginLeft="20dp"
18         ...
19         app:layout_constraintLeft_toRightOf="@+id/a"
20         app:layout_constraintTop_toTopOf="@+id/a" />
21 
22     <android.support.constraint.Placeholder
23         android:id="@+id/place"
24         android:layout_width="200dp"
25         android:layout_height="200dp"
26         app:content="@+id/a"
27         app:layout_constraintBottom_toBottomOf="parent"
28         app:layout_constraintLeft_toLeftOf="parent"/>
29 
30     <Button
31         ...
32         app:layout_constraintBottom_toBottomOf="@+id/place"
33         app:layout_constraintLeft_toRightOf="@+id/place" />
34 </android.support.constraint.ConstraintLayout>

效果如圖:

能夠看到,本來B是位於A的右邊而且頂部對齊的,但由於A被Placeholder引用,使A 至關於Gone了。而Placeholder的位置則顯示了A的內容,而且大小也和A相符,Placeholder的大小設置並無生效。
大概總結能夠認爲,Placeholder引用A後的效果是,本來位置的A gone,本來位置的Placeholder變爲Placeholder的約束屬性+A的內容屬性。另外,Placeholder也支持使用代碼setContentId動態的修改設置內容。

關於Placeholder的應用場景,網上其餘人也都列出了一些例子:好比能夠做爲位置模板,引入後只須要寫內容view;使用代碼動態改變內容,結合TransitionManager能夠作一些有趣的過分動畫等。

4. Barrier

屏障,一個虛擬View。他主要解決下面遇到的問題:

 
如上圖佈局,兩個TextView,一個button位於他們的右邊。如今button設置的是在下面TextView的右邊。假設有時候上面的TextView文本變長了,則佈局會變爲下面這個樣子:

 

上面的TextView和Button重疊了。這時該怎麼解決這個問題呢?Button只能設置一個View做爲錨點,設置了上面就顧不了下面了。。。
因此就誕生了Barrier,他能夠設置N個View做爲錨點,使用方式以下:
1 <android.support.constraint.Barrier
2               android:id="@+id/barrier"
3               android:layout_width="wrap_content"
4               android:layout_height="wrap_content"
5               app:barrierDirection="end"//end,left,right,top,bottom
6               app:constraint_referenced_ids="text1,text2" />

則Barrier始終位於text1,text2兩個View最大寬度的右邊,示意圖以下:

這裏基本的用法就講完了。
下面再考慮一個狀況,假若有以下的佈局:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout 
 3   ...>
 4 
 5     <Button
 6         android:id="@+id/a"
 7         ...
 8         android:layout_marginTop="20dp"
 9          />
10 
11     <Button
12         android:id="@+id/b"
13         ...
14         android:layout_marginTop="40dp"
15          />
16 
17     <android.support.constraint.Barrier
18         android:id="@+id/barrier"
19         ...
20         app:barrierDirection="top"
21         app:constraint_referenced_ids="a,b" />
22 
23     <Button
24         android:id="@+id/c"
25         ...
26         app:layout_constraintTop_toTopOf="@+id/barrier" />
27 </android.support.constraint.ConstraintLayout>

目前Button C和Button a、b的最上值對齊,沒有問題。但若是a Gone了呢?效果以下:

其實也是符合邏輯,a gone後,會變爲一個點,因此C頂齊父佈局也沒問題。但有的時候這不符合咱們的需求,咱們但願Barrier不要關注Gone的View了,因此谷歌提供了屬性 barrierAllowsGoneWidgets,設爲false後,就不在關注Gone的View了,效果以下:

4、結束

本篇已基本上介紹完ConstraintLayout全部的屬性了(除了代碼寫佈局的ConstraintSet類)。相信ConstraintLayout以後會愈來愈強大。

相關文章
相關標籤/搜索