轉(zhuǎn)自博客:http://blog.csdn.net/lmj623565791/article/details/78011599
前言
承接上篇文章,ConstraintLayout用可視化拖拽的方式布局雖然方便,但是知其然而不知其所以然。最好的方法還是自己手寫屬性,雖然一開始麻煩一點,但是最為穩(wěn)妥,布局出了問題也最好定位。這篇文章就記錄各種屬性的使用方法。
編寫一個列表item

<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:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#11ff0000"
tools:context="com.zhy.constrantlayout_learn.MainActivity">
<TextView
android:id="@+id/tv1"
android:layout_width="140dp"
android:layout_height="86dp"
android:layout_marginLeft="12dp"
android:layout_marginTop="12dp"
android:background="#fd3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="@+id/tv2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="12dp"
android:text="馬云:一年交稅170多億馬云:一年交稅170多億馬云:一年交稅170多億"
android:textColor="#000000"
android:textSize="16dp"
app:layout_constraintLeft_toRightOf="@id/tv1"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/tv1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="12dp"
android:text="8分鐘前"
android:textColor="#333"
android:textSize="12dp"
app:layout_constraintLeft_toRightOf="@id/tv1"
app:layout_constraintBottom_toBottomOf="@id/tv1" />
</android.support.constraint.ConstraintLayout>
看上面的布局,我們好像看到了幾個模式的屬性:
首先是tv1,有兩個沒見過的屬性:
- app:layout_constraintLeft_toLeftOf="parent"
從字面上看,指的是讓該控件的左側(cè)與父布局對齊,當(dāng)我們希望控件A與控件B左側(cè)對齊時,就可以使用該屬性。
app:layout_constraintLeft_toLeftOf="@id/viewB"
類似的還有個相似的屬性為:
- app:layout_constraintLeft_toRightOf
很好理解,即當(dāng)前屬性的左側(cè)在誰的右側(cè),當(dāng)我們希望控件A在控件B的右側(cè)時,可以設(shè)置:
app:layout_constraintLeft_toRightOf="@id/viewB"
與之類似的還有幾個屬性:
- layout_constraintRight_toLeftOf
- layout_constraintRight_toRightOf
- layout_constraintTop_toTopOf
- layout_constraintTop_toBottomOf
- layout_constraintBottom_toTopOf
- layout_constraintBottom_toBottomOf
- layout_constraintBaseline_toBaselineOf
類推就可以了。
現(xiàn)在再看剛才的布局:
tv1設(shè)置了:
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tv2設(shè)置了:
app:layout_constraintLeft_toRightOf="@id/tv1"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/tv1"
tv3設(shè)置了:
app:layout_constraintLeft_toRightOf="@id/tv1"
app:layout_constraintBottom_toBottomOf="@id/tv1"
按照我們剛才的理解,再次的解讀下:
tv1應(yīng)該是在父布局的左上角;
tv2在tv1的右側(cè),tv2的右側(cè)和父布局對其,tv2和tv1頂部對齊;
tv3在tv1的右側(cè),tv3和tv1底部對其。
到這里,大家可以看到,目前我們已經(jīng)可以控制任何一個控件與其他控件間的相對位置了,以及與parent間的相對位置。
和RelativeLayout的差異
大家是不是覺得目前來看和RelativeLayout特別像?
其實還是有很明顯的區(qū)別的,我們通過一個例子來看一下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<Button
android:id="@+id/id_btn01"
android:layout_width="100dp"
android:text="Btn01"
android:layout_height="wrap_content" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/id_btn01"
android:text="Btn02"
android:layout_alignParentRight="true"
/>
</RelativeLayout>

那么經(jīng)過我們剛才的學(xué)習(xí),把:
layout_toRightOf="@id/id_btn01"
layout_alignParentRight="true"
分別替換為:
app:layout_constraintLeft_toRightOf="@id/id_btn01"
app:layout_constraintRight_toRightOf="parent"
是不是覺得so easy ,但是我們看一下效果圖:

是不是和預(yù)期有一定的區(qū)別,假設(shè)你將Btn02的寬度設(shè)置的非常大,你會發(fā)現(xiàn)更加詭異的事情:

你會發(fā)現(xiàn)Btn02,好像瘋了一樣,我們設(shè)置的在btn01右側(cè),和與parent右側(cè)對齊完全失效了?。。?/p>
別怕,接下來就讓你認(rèn)識到為什么這個控件叫做“Constraint”Layout。
在當(dāng)控件有自己設(shè)置的寬度,例如warp_content、固定值時,我們?yōu)榭丶砑拥亩际羌s束“Constraint”,這個約束有點像橡皮筋一樣會拉這個控件,但是并不會改變控件的尺寸(RL很明顯不是這樣的)。
例如上例,當(dāng)btn02的寬度較小時,我們?yōu)槠渥髠?cè)設(shè)置了一個約束(btn01右側(cè)),右側(cè)設(shè)置了一個約束(parent右側(cè)對其),當(dāng)兩個約束同時生效的時候(你可以認(rèn)為兩邊都是相同的一個拉力),btn02會居中。
當(dāng)btn02特別大的時候,依然是這兩個力,那么會發(fā)生什么?會造成左側(cè)和右側(cè)超出的距離一樣大。
那么現(xiàn)在大家肯定有些疑問:
- 怎么樣才能和上面的RL一樣,寬度剛好占據(jù)剩下的距離呢(btn01右側(cè)到屏幕右側(cè)的距離)?
這個問題,問得很好,我們剛才所有的嘗試都是在控件自身擁有特定的寬度情況下執(zhí)行的;那么如果希望控件的寬度根據(jù)由約束來控件,不妨去掉這個特定的寬度,即設(shè)置為0試試?
對!當(dāng)我們將btn02的寬度設(shè)置為0時,一切又變得很完美。

那么這里,你可能會問0值是什么含義,其實在ConstraintLayout中0代表:MATCH_CONSTRAINT,看到這個常量,是不是瞬間覺得好理解了一點。
- 最后一個問題,MATCH_PARENT哪去了?
看官網(wǎng)的解釋:
Important: 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”.`
所以你可以認(rèn)為:在ConstraintLayout中已經(jīng)不支持MATCH_PARENT這個值了,你可以通過MATCH_CONSTRAINT配合約束實現(xiàn)類似的效果。
好了,到這里,目前我們已經(jīng)看到其已經(jīng)和RelativeLayout勢均力敵了,接下來我們看一下RL做不到的特性。
增加一個banner
我們現(xiàn)在以往在這個feed item頂部添加一個banner,寬度為占據(jù)整個屏幕,寬高比為16:6。
這里尷尬了,在之前的做法,很難在布局中設(shè)置寬高比,一般我們都需要在代碼中顯示的去操作,那么如果你用了ConstraintLayout,它就支持。
看一眼如何支持:
<android.support.constraint.ConstraintLayout
...
tools:context="com.zhy.constrantlayout_learn.MainActivity">
<TextView
android:id="@+id/banner"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#765"
android:gravity="center"
android:text="Banner"
app:layout_constraintDimensionRatio="H,16:6"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/tv1"
app:layout_constraintTop_toBottomOf="@id/banner"
></TextView>
...
</...>
我們添加了一個banner,還記得我們剛才所說的么,不要使用match_parent了,而是設(shè)置match_contraint,即0,讓約束來控制布局寬高。
所以我們設(shè)置了寬、高都是match_contraint,然后這兩個屬性:
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
讓我們的寬度充滿整個父布局,在添加一個:
app:layout_constraintDimensionRatio="16:6"
該屬性指的是寬高比,所以16:6就可以完成我們的需求。
好了看下效果圖:

這個寬高比屬性,還支持這樣的寫法:
app:layout_constraintDimensionRatio="W,16:6"
app:layout_constraintDimensionRatio="H,16:6"
可以自己試驗下。
好了,到這里,我們又新增了一個屬性,還是個非常實用的屬性。
那么,我們繼續(xù),再看一個似曾相識的功能。
增加幾個Tab
現(xiàn)在我們希望在底部增加3個tab,均分。是不是想到了LinearLayout和weight。
沒錯!ConstraintLayout也支持類似的屬性。
雖然我知道,但是寫到這我還是有點小驚喜~~
看下如何實現(xiàn):
<TextView
android:id="@+id/tab1"
android:layout_width="0dp"
android:layout_height="30dp"
android:background="#f67"
android:gravity="center"
android:text="Tab1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tab2" />
<TextView
android:id="@+id/tab2"
android:layout_width="0dp"
android:layout_height="30dp"
android:background="#A67"
android:gravity="center"
android:text="Tab2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/tab1"
app:layout_constraintRight_toLeftOf="@+id/tab3" />
<TextView
android:id="@+id/tab3"
android:layout_width="0dp"
android:layout_height="30dp"
android:background="#767"
android:gravity="center"
android:text="Tab3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/tab2"
app:layout_constraintRight_toRightOf="parent" />
我們增加3個textview來冒充tab。我們看橫向的依賴,3個tab兩兩設(shè)置了約束(即你在我們的左邊,我在你的右邊),最外層的設(shè)置了parent約束;再加上我們把寬度都設(shè)置為了match_constraint,so,這樣我們就完成了3個tab等分。
看一眼效果圖:

你可能會說,LL配合weight更加靈活,可以單個設(shè)置占據(jù)的比例。
對,沒錯,我們也支持,我不是還沒說完么。
現(xiàn)在我們可以給每個tab設(shè)置一個屬性:
app:layout_constraintHorizontal_weight
看到這個名字,應(yīng)該就明白了吧,假設(shè)我們分別設(shè)置值為2,1,1。
效果圖為:

是不是很驚喜,別急,剛才你說我不如LL,現(xiàn)在我要讓你再看一些LL配合weight做不到的。
這里需要借助幾張官網(wǎng)上的圖了:
剛才我們說了,3個tab兩兩設(shè)置了依賴,即類似下圖:

橫向的相當(dāng)于組成了一個鏈(Chains)。在這個鏈的最左側(cè)的元素成為鏈頭,我們可以在其身上設(shè)置一些屬性,來決定這個鏈的展示效果:
該屬性為:
layout_constraintHorizontal_chainStyle
我們已經(jīng)見過一種效果了,即按照weight等分,可以成為weighted chain。設(shè)置條件為:
chainStyle=”spread”,所有控件寬度設(shè)置為match_constraint,因為默認(rèn)就是spread,所以我們沒有顯示設(shè)置。
其取值還可以為:
- packed
- spread_inside
我還是分別顯示一下吧:
1.spread + 寬度非0

1.spread + 寬度為0,且可以通過weight控制分配比例(上例)
2.spread_inside + 寬度非0

1.packed + 寬度非0

好了,差不多了,我們可以在橫向或者縱向組成一個Chain,然后在Chain head設(shè)置chainStyle來搞一些事情。
官網(wǎng)有個圖:

前四個我們都演示了,最后一個設(shè)計到一個新的bias屬性,別急,咱們慢慢說~~
好了,到這里,我們再次見證了ConstraintLayout的強(qiáng)大。
我們最后再看一個例子。
增加浮動按鈕
一個很常見的功能,我們現(xiàn)在希望在右下角增加一個浮動按鈕。
看下如何實現(xiàn):
<android.support.constraint.ConstraintLayout
...
tools:context="com.zhy.constrantlayout_learn.MainActivity">
<TextView
android:layout_width="60dp"
android:layout_height="60dp"
android:background="#612"
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" />
</....>
我們在最后追加一個TextView冒充我們的浮動按鈕??梢钥吹轿覀冊O(shè)置了固定值,被設(shè)置約束為右下角。
正常情況我們可以通過margin來設(shè)置與右側(cè)與底部的距離。
但是這里我們嘗試使用量個新的屬性:
layout_constraintHorizontal_bias
layout_constraintVertical_bias
即設(shè)置上下兩側(cè)間隙比例分別為90%與10%。這個很好理解,我們之前說了,再沒有bias這個屬性的時候,這兩側(cè)的拉力大小是一樣的,但是你可以通過bias來控制哪一側(cè)的力要大一些~明白了么
所以,該屬性可以用于約束之前,控制兩側(cè)的“拉力”。
我們看一下效果圖:

那么到這里,ConstraintLayout的屬性我們基本上介紹完了:
我們看一下:
layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
# 即文章的baseline對齊
layout_constraintBaseline_toBaselineOf
# 與left,right類似
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
# margin不需要解釋
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
layout_constraintHorizontal_bias
layout_constraintVertical_bias
layout_constraintHorizontal_chainStyle
layout_constraintVertical_chainStyle
layout_constraintVertical_weight
Guideline
好像,還有個比較特殊的,叫Guideline。
好吧,繼續(xù)~
嘗試使用Guideline
android.support.constraint.Guideline該類比較簡單,主要用于輔助布局,即類似為輔助線,橫向的、縱向的。該布局是不會顯示到界面上的。
所以其有個屬性為:
android:orientation取值為”vertical”和”horizontal”.
除此以外,還差個屬性,決定該輔助線的位置:
- layout_constraintGuide_begin
- layout_constraintGuide_end
- layout_constraintGuide_percent
可以通過上面3個屬性其中之一來確定屬性值位置。
begin=30dp,即可認(rèn)為距離頂部30dp的地方有個輔助線,根據(jù)orientation來決定是橫向還是縱向。
end=30dp,即為距離底部。
percent=0.8即為距離頂部80%。
好了,下面看一個例子,剛才我們的浮點按鈕,我決定通過兩根輔助線來定位,一根橫向距離底部80%,一個縱向距離頂部80%,浮點按鈕就定位在他們交叉的地方。
<android.support.constraint.ConstraintLayout
...
tools:context="com.zhy.constrantlayout_learn.MainActivity">
<android.support.constraint.Guideline
android:id="@+id/guideline_h"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.8" />
<android.support.constraint.Guideline
android:id="@+id/guideline_w"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.8" />
<TextView
android:layout_width="60dp"
android:layout_height="60dp"
android:background="#612"
app:layout_constraintLeft_toRightOf="@id/guideline_w"
app:layout_constraintTop_toBottomOf="@id/guideline_h" />
</....>
我感覺都不用解釋了~~看眼效果圖吧:

到此,屬性基本上講完啦~
可以看到,上述相當(dāng)復(fù)雜的一個布局,在ConstraintLayout中完全沒有嵌套!