想看我更多文章:【張旭童的博客】blog.csdn.net/zxt0601
想來gayhub和我gaygayup:【mcxtzhang的Github主頁】github.com/mcxtzhangjavascript
小夥伴們很久不見,我又回來啦。
說實話這篇文章寫的算是比較晚了,距離ConstraintLayout
出現至今已經有一年了。
且自AS2.3起建立新的Activity
,默認的layout根佈局就是ConstraintLayout
。
因此再不學習就真的晚了。
我也是正式開始學習的道路,先說一下個人學習過程:html
ConstraintLayout
可視化操做(拖拖拽拽)的博客,發現博客中對Chain
的概念沒有說起Chain
以及一些疑點的資料本文的順序,大致按照ConstraintLayout官方文檔的順序依次講解(翻譯)屬性和用法,並對疑難點進行額外說明。
關於可視化操做,可參考我寫的動態圖解&實例 ConstraintLayout Chain和郭神博文可視化操做前端
引入也有坑,無力吐槽。
先放上 截止至20170524,最新版本1.0.1:java
compile 'com.android.support.constraint:constraint-layout:1.0.1'
坑是啥?由於我使用的是最新的release版AndroidStudio2.3.2,新建Activity後,自動幫我引入的是1.0.8-alpha
版本,
開始我就這麼愉快的學習了,但是當我學習到Chain
相關姿式時,特碼的,他竟然報錯。說找不到屬性:android
Error:(10) No resource identifier found for attribute 'layout_constraintHorizontal_chainStyle' in package 'com.mcxtzhang.constraintlayoutdemo'git
ok,那我百度,顯然搜不到的,ok,那我再google,特麼的竟然也搜不到。
震驚,因而機智的我去看源碼,發現我使用的1.0.8-alpha
版本的源碼里根本沒有Chain
相關屬性的支持,因此我就以爲必定是引入的版本有問題,因而我用google搜索"ConstraintLayout last version",發現誒~官方有說最新版連接以下:
tools.android.com/recent/cons…
按照這個連接提示,最新版是1.0.2
,嗯哼,當我換成1.0.2
後,發現沒法download....
不知道是網絡問題仍是什麼問題,提示我沒法下載,具體的錯誤記不清了。反正就是沒法獲取到這個版本。
特麼的機智的我又直接去AndroidStudio的Library Dependency裏去搜索,發現竟然搜不到"ConstraintLayout "的庫。再次懵逼。
後來我進行最後的一次嘗試,由於我看google官方上1.0.2版本的上一個版本是1.0.1.因而我修改版本號,sync gradle,竟然成功了。
總結踩坑歷程:github
Chain
相關屬性報錯Chain
相關屬性ConstraintLayout
對此,我只能說「驚不驚喜! 意不意外!」
canvas
先概況一下,它是一個爲了解決佈局嵌套和模仿前端flexible佈局的一個新佈局。網絡
從字面上理解,ConstraintLayout
是約束佈局。
在我理解,這是一個RelativeLayout
的升級版。
而當初推出RelativeLayout
的目的是爲了在減小多層嵌套佈局,
推出ConstraintLayout
也是一樣的目的,儘量的使佈局 寬而短,而不是 窄而長。
而ConstraintLayout
更增強大,不少須要多層嵌套的佈局,使用ConstraintLayout
只須要一層便可解決。
它的Chain
幾種style方式,和前端的flexbox佈局風格一致,官方文檔中也說了它是flexible方式佈局控件的東西。app
A ConstraintLayout is a ViewGroup which allows you to position and size widgets in a flexible way.
並且搭配可視化的操做,使得佈局也變得更輕鬆。
Google官方推薦全部操做都在"Design"區域搞定,即經過可視化拖拖拽拽生成佈局大體的樣子,而後針對具體屬性、約束 精細修改。
甚至能夠這麼說,你徹底不須要知道ConstraintLayout
的具體屬性值分別是什麼,只經過拖拽和鼠標點擊就能夠實現一些佈局。
我以爲首先是要知其然知其因此然,那些拖拽點擊生成的代碼屬性究竟是什麼意思?經過本文能夠了解。
另外 雖然大部分操做能夠在「Design」區域完成,可是保不齊的須要你切換至「Text」區域,寫上一兩行屬性代碼,瞭解 這些屬性 老是有益無害的。
並且,有一些屬性是沒法簡單經過拖拽點擊完成的,例如Margins when connected to a GONE widget
。
剛纔提到RelativeLayout
,其實RelativeLayout
也是經過約束來佈局子View的呀,
之前RelativeLayout
的約束有兩種:
android:layout_below="@id/title"
)android:layout_alignParentTop="true"
)如今ConstraintLayout
也是相似的,只不過除了以上兩種約束,還多了一種
Guideline
的約束其實關於和Guideline
的約束,也能夠理解成約束1,由於Guideline
其實就是一個在屏幕上不顯示的View罷了。稍後講到Guideline
會帶你們看看它巨簡單的源碼。
下面開始正文,開始屬性的講解
這一節的屬性和相對佈局的很像,
值得注意的是參數取值是 ID(@id/button1
)表明約束一、3, 或者 字符串"parent"
表明約束2:
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
layout_constraintXXX_toYYYOf
,
constraintXXX
裏的
XXX
表明是這個子控件
自身的哪條邊(
Left、Right、Top、Bottom、Baseline
),
toYYYOf
裏的
YYY
表明的是和
約束控件的
哪條邊
發生約束 (取值一樣是
Left、Right、Top、Bottom、Baseline
)。
XXX
和
YYY
相反時,表示控件自身的
XXX
在約束控件的
YYY
的一側,
app:layout_constraintLeft_toRightOf="@id/button1"
,表示的是控件自身的左側在button1的右側。
當XXX
和YYY
相同時,表示控件自身的XXX
和約束控件的YYY
的一側 對齊,
例如:app:layout_constraintBottom_toBottomOf="parent"
,表示控件自身底端和父控件底端對齊。
代碼爲:
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Demo"/>
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button2"
app:layout_constraintLeft_toRightOf="@id/button1"/>
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:text="button3 跳轉match頁"
app:layout_constraintBottom_toBottomOf="parent"/>複製代碼
圖示:
margin和以往的使用一致,注意margin不能爲負值便可。
在上圖中也順便展現了margin的使用。
Margins when connected to a GONE widget
舉例,當A控件 約束 在B控件的左邊,B控件GONE了,此時A會額外擁有一個margin的能力,來「補充」B消失的致使的「位移」。
這就是本節的屬性。
這一節的屬性開始我並無理解,後來是經過寫了一些Demo實驗才明白。奈何官方文檔惜字如金,只有一句話,並無Demo展現:
When a position constraint target's visibility is View.GONE, you can also indicates a different margin value to be used using the following attributes:
先看屬性:
在看Demo:
<Button
android:id="@+id/button4"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="button4"
app:layout_constraintRight_toRightOf="parent"
/>
<!-- android:layout_marginRight="10dp"
配合 app:layout_goneMarginRight="110dp"一塊兒使用,
在約束的佈局gone時,起用goneMargin,
可是必定要預先設置對應方向上的margin -->
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="button5"
app:layout_constraintRight_toLeftOf="@id/button4"
app:layout_goneMarginRight="110dp"/>複製代碼
此時圖示:
會發現Button5紋絲不動,並無收到Button4消失的影響。
這裏咱們再仔細看看button4的android:layout_width="100dp"
,
而button5的android:layout_marginRight="10dp"
,app:layout_goneMarginRight="110dp"
110 = 100 +10 , 這是一道小學計算題。
什麼意思?
幾個注意事項:
app:layout_goneMarginRight
要配合android:layout_marginRight
一塊兒使用。app:layout_goneMarginRight
沒有設置android:layout_marginRight
,則無效。(alpha版本的bug,1.0.1版本已經修復)marginXXX
會被goneMarginXXX
替換掉,以本文Demo爲例,本來button4寬度是100,button5的marginRight
是10, 加起來是110,若是想讓button4隱藏以後,button5仍然紋絲不動,則須要設置goneMarginRight
爲10+100 = 110.約束佈局一個有用的地方是它如何處理「不可能」的約束。
好比你定義以下:
<android.support.constraint.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="button" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> </android.support.constraint.ConstraintLayout>複製代碼
按照咱們第一小節講的屬性值,這個定義的意思是,Button的左邊和父控件的左邊對齊,Button的右邊和父控件的右邊對齊。
但是控件是wrap_content
的,它若是不鋪滿父控件要如何能知足這兩個約束呢?
實際效果以下:
控件會居中顯示,由於這兩個約束做用 相似於 水平方向上,有相反的力 去拉控件,最終控件會居中顯示。
搭配bias,能使約束偏向某一邊,默認是0.5,有如下屬性:
好比上個Demo,我加入app:layout_constraintHorizontal_bias="0.9"
,則會在水平方向上向右偏移至90%。
<android.support.constraint.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button ... app:layout_constraintHorizontal_bias="0.9" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> </android.support.constraint.ConstraintLayout>複製代碼
這一節是對前一節goneMargin
的補充。
重點是Gone隱藏掉的控件,會被解析成一個點,並忽略margin。
ConstraintLayout
能爲View.Gone
的View
特殊處理。
一般,GONE的控件不會被顯示,而且不是佈局自己的一部分(即若是標記爲GONE,則其實際尺寸並不會更改)。
可是在佈局計算方面,GONE的View仍然是其中的一個重要區別:
對於佈局傳遞,它們的維度將被視爲零(基本上它們將被解析爲一個點)
若是他們對其餘小部件有約束力,那麼他們仍然會受到尊重,但任何margin都將等於零
拿上個Demo改一下,爲A 加上一個android:layout_marginRight="10dp"
,
爲了使A 隱藏後,B仍能紋絲不動,則B的app:layout_goneMarginRight="120dp"
。
B goneMarginRight120 = A寬度100 + A marginRight10 +B marginRight10
<Button
android:id="@+id/button4"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="button4"
app:layout_constraintRight_toRightOf="parent"
/>
<Button android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:text="button5" app:layout_constraintRight_toLeftOf="@id/button4" app:layout_goneMarginRight="120dp"/>複製代碼
能夠爲ConstraintLayout
自身定義最小的尺寸,他會在 ConstraintLayout
爲WRAP_CONTENT
時起做用。
● android:minWidth
● android:minHeight
控件的寬高有三種方式爲其設置:
WRAP_CONTENT
0dp
,就等於MATCH_CONSTRAINT
有些人可能有疑問,爲何不用MATCH_PARENT
了。
官方文檔如是說:
MATCH_PARENT is not supported for widgets contained in a ConstraintLayout, though similar behavior can be defined by using MATCH_CONSTRAINT with the corresponding left/right or top/bottom constraints being set to "parent".
意思是MATCH_PARENT
再也不被支持了,經過MATCH_CONSTRAINT
替代。
咱們寫個Demo看一下三種方式設置的效果吧:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="200dp"
android:layout_height="100dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="@+id/button10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toLeftOf="@+id/button"
app:layout_constraintRight_toRightOf="@+id/button"
app:layout_constraintTop_toBottomOf="@+id/button"/>
<Button
android:id="@+id/button11"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toLeftOf="@+id/button10"
app:layout_constraintRight_toRightOf="@+id/button10"
app:layout_constraintTop_toBottomOf="@+id/button10"/>
</android.support.constraint.ConstraintLayout>複製代碼
效果如圖:
MATCH_CONSTRAINT
,應該是撐滿屏幕的呀,
MATCH_CONSTRAINT
屬性。它match的是約束。
MATCH_CONSTRAINT
時,
是和它的約束按鈕,即第二個按鈕同樣寬。
MATCH_CONSTRAINT
屬性.
咱們僅僅將第三個按鈕的屬性修改成
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"複製代碼
則它寬度會撐滿屏幕:
咱們再修改Demo,分別爲後兩個按鈕加上margin:
<android.support.constraint.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="200dp"
android:layout_height="100dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="@+id/button10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toLeftOf="@+id/button"
app:layout_constraintRight_toRightOf="@+id/button"
app:layout_constraintTop_toBottomOf="@+id/button"/>
<Button
android:id="@+id/button12"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
app:layout_constraintLeft_toLeftOf="@id/button10"
app:layout_constraintRight_toRightOf="@id/button10"
app:layout_constraintTop_toBottomOf="@id/button10"/>
<Button
android:id="@+id/button11"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button12"/>
</android.support.constraint.ConstraintLayout>複製代碼
效果如圖:
最後,記住一句話約束要和 0dp 的 方向一致。不然無效。
能夠以比例去定義View的寬高。
爲了作到這一點,須要將至少一個約束維度設置爲0dp
(即MATCH_CONSTRAINT
)
並將屬性layout_constraintDimentionRatio
設置爲給定的比例。
例如:
<Button
android:layout_width="200dp"
android:layout_height="0dp"
android:text="Ratio"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="2:1"
app:layout_constraintTop_toTopOf="parent"/>複製代碼
如圖:
比例值有兩種取值:
若是兩個維度均設置爲MATCH_CONSTRAINT(0dp),也可使用比例。 在這種狀況下,系統會使用知足全部約束條件和比率的最大尺寸。
若是須要根據一個維度的尺寸去約束另外一個維度的尺寸。
則能夠在比率值的前面添加 W 或者 H 來分別約束寬度或者高度。
例如,若是一個尺寸被兩個目標約束(好比寬度爲0,在父容器中居中),可使用 W 或H 來指定哪一個維度被約束。
<Button
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="H,2:1"
app:layout_constraintTop_toTopOf="parent"/>複製代碼
這裏用「H」表示以高度爲約束,高度的最大尺寸就是父控件的高度,「2:1」表示高:寬 = 2 : 1.
則寬度爲高度的一半:
鏈條在同一個軸上(水平或者垂直)提供一個相似羣組的統一表現。另外一個軸能夠單獨控制。
若是一組小部件經過雙向鏈接(見圖,顯示最小的鏈,帶有兩個小部件),則將其視爲鏈條。
鏈條由在鏈的第一個元素(鏈的「頭」)上設置的屬性控制:
若是在鏈接上指定了邊距,則將被考慮在內。
例如
<Button
android:id="@+id/buttonA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/buttonB"/>
<Button android:id="@+id/buttonB" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" app:layout_constraintLeft_toRightOf="@+id/buttonA" app:layout_constraintRight_toRightOf="parent"/>複製代碼
經過app:layout_constraintRight_toLeftOf="@+id/buttonB"
和app:layout_constraintLeft_toRightOf="@+id/buttonA"
就創建了鏈條,(我中有你,你中有我)。
而後它們兩個成了一個總體,因此鏈條左邊設置app:layout_constraintLeft_toLeftOf="parent"
使得和父控件左對齊,
右邊設置app:layout_constraintRight_toRightOf="parent"
使得和父控件右對齊,
這樣整個鏈條就居中了,最後對左控件設置了margin,至關於整個鏈條左邊有了margin
效果:
當在鏈的第一個元素上設置屬性 layout_constraintHorizontal_chainStyle
或layout_constraintVertical_chainStyle
時,鏈的行爲將根據指定的樣式(默認爲CHAIN_SPREAD
)而更改。
看圖這裏就很像JS裏的flexible有木有。由於ConstraintLayout
就是模仿flexible作的。
spread
- 元素將被展開(默認樣式)爲MATCH_CONSTRAINT
,則它們將拆分可用空間spread_inside
- 相似,但鏈的端點將不會擴展packed
- 鏈的元素將被打包在一塊兒。 孩子的水平或垂直誤差屬性將影響包裝元素的定位拿加權鏈舉個例子:
<Button
android:id="@+id/buttonA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:text="Button"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/buttonB"/>
<Button android:id="@+id/buttonB" android:layout_width="0dp" android:layout_height="wrap_content" android:text="Button" app:layout_constraintLeft_toRightOf="@+id/buttonA" app:layout_constraintRight_toRightOf="parent"/>複製代碼
和LinearLayou
t的weight
相似。
鏈的默認行爲是在可用空間中平均分配元素。 若是一個或多個元素使用MATCH_CONSTRAINT
,它們將使用剩餘的空白空間(在它們之間相等)。 屬性layout_constraintHorizontal_weight
和layout_constraintVertical_weight
將決定這些都設置了MATCH_CONSTRAINT
的View如何分配空間。 例如,在包含使用MATCH_CONSTRAINT
的兩個元素的鏈上,第一個元素使用權重爲2,第二個元素的權重爲1,第一個元素佔用的空間將是第二個元素的兩倍
最後關於鏈條,再給你們看一個關於margin
的demo:
<Button
android:id="@+id/buttonA"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:text="Button"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/buttonB"/>
<Button android:id="@+id/buttonB" android:layout_width="0dp" android:layout_height="wrap_content" android:text="Button" app:layout_constraintLeft_toRightOf="@+id/buttonA" app:layout_constraintRight_toRightOf="parent"/>複製代碼
Guideline
只能用於ConstraintLayout
中,是一個工具類,不會被顯示,僅僅用於輔助佈局。
它能夠是horizontal
或者 vertical
的。(例如:android:orientation="vertical"
)
vertical
的Guideline
寬度爲零,高度爲ConstraintLayout
的高度horizontal
的Guideline
高度爲零,寬度爲ConstraintLayout
的高度定位Guideline
有三種方式:
layout_constraintGuide_begin
)layout_constraintGuide_end
)layout_constraintGuide_percent
)一個栗子一看便知:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="100dp"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>複製代碼
預覽:
public class Guideline extends View {
public Guideline(Context context) {
super(context);
super.setVisibility(8);
}
public Guideline(Context context, AttributeSet attrs) {
super(context, attrs);
super.setVisibility(8);
}
public Guideline(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
super.setVisibility(8);
}
public Guideline(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
super.setVisibility(8);
}
public void setVisibility(int visibility) {
}
public void draw(Canvas canvas) {
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
this.setMeasuredDimension(0, 0);
}
}複製代碼
public static final int GONE = 0x00000008;複製代碼
源碼就這麼點,這貨的源碼和ViewStub
有點像啊,能夠看出
public void setVisibility(int visibility)
方法被空實現了,因此用戶也沒辦法改變它的可見度。this.setMeasuredDimension(0, 0);
和public void draw(Canvas canvas)
的空實現,代表這是一個超輕量的View,不可見,沒有寬高,也不繪製任何東西。僅僅做爲咱們的錨點使用。好久不寫博客了,一是工做太忙了,二也是隨意的寫怕誤人子弟。
這篇文章我寫了整整一天,每一個例子我都邊寫邊跑了一遍,
也看了幾篇別人的文章,有些人簡單的翻譯了官方文檔,可是對文檔中一些沒有舉例, 不那麼好理解的地方也沒有說明,因而便有了此文。
關於可視化操做,建議直接看我寫的動態圖解&實例 ConstraintLayout Chain和郭神博文可視化操做,
我以爲ConstraintLayout ,有這幾篇就夠了。
文中代碼地址在個人Demo合集中:
github.com/mcxtzhang/D…
想看我更多文章:【張旭童的博客】blog.csdn.net/zxt0601
想來gayhub和我gaygayup:【mcxtzhang的Github主頁】github.com/mcxtzhang