【转】 ConstraintLayout 完全解析 快来优化你的布局吧
轉(zhuǎn)自:?
http://blog.csdn.net/lmj623565791/article/details/78011599?
本文出自張鴻洋的博客
一、概述
ConstraintLayout出現(xiàn)有一段時(shí)間了,不過(guò)一直沒(méi)有特別去關(guān)注,也多多少少看了一些文字介紹,多數(shù)都是對(duì)使用可視化布局拖拽,個(gè)人對(duì)拖拽一直不看好,直到前段時(shí)間看到該文:
- 解析ConstraintLayout的性能優(yōu)勢(shì)
非常詳盡的介紹了ConstraintLayout的性能優(yōu)勢(shì),于是乎開(kāi)始學(xué)習(xí)了一下ConstraintLayout。
本文的重點(diǎn)不在與可視化界面的學(xué)習(xí),而在于如何手寫各類約束布局屬性。對(duì)于可視化界面學(xué)習(xí)推薦:
- Android新特性介紹,ConstraintLayout完全解析
下面開(kāi)始進(jìn)入正題,大家都知道,當(dāng)布局嵌套深入比較深的時(shí)候,往往會(huì)伴隨著一些性能問(wèn)題。所以很多時(shí)候我們建議使用RelativeLayout或者GridLayout來(lái)簡(jiǎn)化掉布局的深度。
而對(duì)于簡(jiǎn)化布局深度,ConstraintLayout幾乎可以做到極致,接下來(lái)我們通過(guò)實(shí)例來(lái)盡可能將所有常見(jiàn)的屬性一步步的介紹清楚。
首先需要引入我們的ConstraintLayout,在build.gradle中加入:
compile 'com.android.support.constraint:constraint-layout:1.0.2'二、來(lái)編寫一個(gè)Feed Item
我們先看一個(gè)簡(jiǎn)單的新聞列表中常見(jiàn)的feed item。
看到這樣的布局,大家條件反射應(yīng)該就是使用RelativeLayout來(lái)做,當(dāng)然了,本案例我們使用ConstraintLayout來(lái)寫:
<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>看上面的布局,我們好像看到了幾個(gè)模式的屬性:
首先是tv1,有兩個(gè)沒(méi)見(jiàn)過(guò)的屬性:
- app:layout_constraintLeft_toLeftOf="parent"
從字面上看,指的是讓該控件的左側(cè)與父布局對(duì)齊,當(dāng)我們希望控件A與控件B左側(cè)對(duì)齊時(shí),就可以使用該屬性。
app:layout_constraintLeft_toLeftOf="@id/viewB"類似的還有個(gè)相似的屬性為:
- app:layout_constraintLeft_toRightOf
很好理解,即當(dāng)前屬性的左側(cè)在誰(shuí)的右側(cè),當(dāng)我們希望控件A在控件B的右側(cè)時(shí),可以設(shè)置:
app:layout_constraintLeft_toRightOf="@id/viewB"與之類似的還有幾個(gè)屬性:
- 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è)和父布局對(duì)其,tv2和tv1頂部對(duì)齊;
tv3在tv1的右側(cè),tv3和tv1底部對(duì)其。
到這里,大家可以看到,目前我們已經(jīng)可以控制任何一個(gè)控件與其他控件間的相對(duì)位置了,以及與parent間的相對(duì)位置。
和RL的差異
大家是不是覺(jué)得目前來(lái)看和RelativeLayout特別像?
其實(shí)還是有很明顯的區(qū)別的,我們通過(guò)一個(gè)例子來(lái)看一下:
<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)過(guò)我們剛才的學(xué)習(xí),把:
layout_toRightOf="@id/id_btn01",layout_alignParentRight="true"
分別替換為:
app:layout_constraintLeft_toRightOf="@id/id_btn01",app:layout_constraintRight_toRightOf="parent"
是不是覺(jué)得so easy ,但是我們看一下效果圖:
是不是和預(yù)期有一定的區(qū)別,假設(shè)你將Btn02的寬度設(shè)置的非常大,你會(huì)發(fā)現(xiàn)更加詭異的事情:
你會(huì)發(fā)現(xiàn)Btn02,好像瘋了一樣,我們?cè)O(shè)置的在btn01右側(cè),和與parent右側(cè)對(duì)齊完全失效了!!!
別怕,接下來(lái)就讓你認(rèn)識(shí)到為什么這個(gè)控件叫做“Constraint”Layout。
在當(dāng)控件有自己設(shè)置的寬度,例如warp_content、固定值時(shí),我們?yōu)榭丶砑拥亩际羌s束“Constraint”,這個(gè)約束有點(diǎn)像橡皮筋一樣會(huì)拉這個(gè)控件,但是并不會(huì)改變控件的尺寸(RL很明顯不是這樣的)。
例如上例,當(dāng)btn02的寬度較小時(shí),我們?yōu)槠渥髠?cè)設(shè)置了一個(gè)約束(btn01右側(cè)),右側(cè)設(shè)置了一個(gè)約束(parent右側(cè)對(duì)其),當(dāng)兩個(gè)約束同時(shí)生效的時(shí)候(你可以認(rèn)為兩邊都是相同的一個(gè)拉力),btn02會(huì)居中。
當(dāng)btn02特別大的時(shí)候,依然是這兩個(gè)力,那么會(huì)發(fā)生什么?會(huì)造成左側(cè)和右側(cè)超出的距離一樣大。
那么現(xiàn)在大家肯定有些疑問(wèn):
- 怎么樣才能和上面的RL一樣,寬度剛好占據(jù)剩下的距離呢(btn01右側(cè)到屏幕右側(cè)的距離)?
這個(gè)問(wèn)題,問(wèn)得很好,我們剛才所有的嘗試都是在控件自身?yè)碛刑囟ǖ膶挾惹闆r下執(zhí)行的;那么如果希望控件的寬度根據(jù)由約束來(lái)控件,不妨去掉這個(gè)特定的寬度,即設(shè)置為0試試?
對(duì)!當(dāng)我們將btn02的寬度設(shè)置為0時(shí),一切又變得很完美。
那么這里,你可能會(huì)問(wèn)0值是什么含義,其實(shí)在ConstraintLayout中0代表:MATCH_CONSTRAINT,看到這個(gè)常量,是不是瞬間覺(jué)得好理解了一點(diǎn)。
- 最后一個(gè)問(wèn)題,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這個(gè)值了,你可以通過(guò)MATCH_CONSTRAINT配合約束實(shí)現(xiàn)類似的效果。
好了,到這里,目前我們已經(jīng)看到其已經(jīng)和RelativeLayout勢(shì)均力敵了,接下來(lái)我們看一下RL做不到的特性。
三、增加一個(gè)banner
我們現(xiàn)在以往在這個(gè)feed item頂部添加一個(gè)banner,寬度為占據(jù)整個(gè)屏幕,寬高比為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> ... </...>我們添加了一個(gè)banner,還記得我們剛才所說(shuō)的么,不要使用match_parent了,而是設(shè)置match_contraint,即0,讓約束來(lái)控制布局寬高。
所以我們?cè)O(shè)置了寬、高都是match_contraint,然后這兩個(gè)屬性:
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"讓我們的寬度充滿整個(gè)父布局,在添加一個(gè):
app:layout_constraintDimensionRatio="16:6"該屬性指的是寬高比,所以16:6就可以完成我們的需求。
好了看下效果圖:
這個(gè)寬高比屬性,還支持這樣的寫法:
app:layout_constraintDimensionRatio="W,16:6" app:layout_constraintDimensionRatio="H,16:6"可以自己試驗(yàn)下。
好了,到這里,我們又新增了一個(gè)屬性,還是個(gè)非常實(shí)用的屬性。
那么,我們繼續(xù),再看一個(gè)似曾相識(shí)的功能。
四、增加幾個(gè)Tab
現(xiàn)在我們希望在底部增加3個(gè)tab,均分。是不是想到了LinearLayout和weight。
沒(méi)錯(cuò)!ConstraintLayout也支持類似的屬性。
雖然我知道,但是寫到這我還是有點(diǎn)小驚喜~~
看下如何實(shí)現(xiàn):
<TextViewandroid: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" />?
我們?cè)黾?個(gè)textview來(lái)冒充tab。我們看橫向的依賴,3個(gè)tab兩兩設(shè)置了約束(即你在我們的左邊,我在你的右邊),最外層的設(shè)置了parent約束;再加上我們把寬度都設(shè)置為了match_constraint,so,這樣我們就完成了3個(gè)tab等分。
看一眼效果圖:
你可能會(huì)說(shuō),LL配合weight更加靈活,可以單個(gè)設(shè)置占據(jù)的比例。
對(duì),沒(méi)錯(cuò),我們也支持,我不是還沒(méi)說(shuō)完么。
現(xiàn)在我們可以給每個(gè)tab設(shè)置一個(gè)屬性:
app:layout_constraintHorizontal_weight- 1
看到這個(gè)名字,應(yīng)該就明白了吧,假設(shè)我們分別設(shè)置值為2,1,1。
效果圖為:
是不是很驚喜,別急,剛才你說(shuō)我不如LL,現(xiàn)在我要讓你再看一些LL配合weight做不到的。
這里需要借助幾張官網(wǎng)上的圖了:
剛才我們說(shuō)了,3個(gè)tab兩兩設(shè)置了依賴,即類似下圖:
橫向的相當(dāng)于組成了一個(gè)鏈(Chains)。在這個(gè)鏈的最左側(cè)的元素成為鏈頭,我們可以在其身上設(shè)置一些屬性,來(lái)決定這個(gè)鏈的展示效果:
該屬性為:
layout_constraintHorizontal_chainStyle我們已經(jīng)見(jiàn)過(guò)一種效果了,即按照weight等分,可以成為weighted chain。設(shè)置條件為:
chainStyle=”spread”,所有控件寬度設(shè)置為match_constraint,因?yàn)槟J(rèn)就是spread,所以我們沒(méi)有顯示設(shè)置。
其取值還可以為:
- packed
- spread_inside
我還是分別顯示一下吧:
spread + 寬度為0,且可以通過(guò)weight控制分配比例(上例)
spread_inside + 寬度非0
好了,差不多了,我們可以在橫向或者縱向組成一個(gè)Chain,然后在Chain head設(shè)置chainStyle來(lái)搞一些事情。
官網(wǎng)有個(gè)圖:
前四個(gè)我們都演示了,最后一個(gè)設(shè)計(jì)到一個(gè)新的bias屬性,別急,咱們慢慢說(shuō)~~
好了,到這里,我們?cè)俅我?jiàn)證了ConstraintLayout的強(qiáng)大。
我們最后再看一個(gè)例子。
五、增加浮動(dòng)按鈕
一個(gè)很常見(jiàn)的功能,我們現(xiàn)在希望在右下角增加一個(gè)浮動(dòng)按鈕。
看下如何實(shí)現(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" /> </....>我們?cè)谧詈笞芳右粋€(gè)TextView冒充我們的浮動(dòng)按鈕。可以看到我們?cè)O(shè)置了固定值,被設(shè)置約束為右下角。
正常情況我們可以通過(guò)margin來(lái)設(shè)置與右側(cè)與底部的距離。
但是這里我們嘗試使用量個(gè)新的屬性:
layout_constraintHorizontal_bias layout_constraintVertical_bias即設(shè)置上下兩側(cè)間隙比例分別為90%與10%。這個(gè)很好理解,我們之前說(shuō)了,再?zèng)]有bias這個(gè)屬性的時(shí)候,這兩側(cè)的拉力大小是一樣的,但是你可以通過(guò)bias來(lái)控制哪一側(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對(duì)齊 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_marginBottomlayout_constraintHorizontal_bias layout_constraintVertical_bias layout_constraintHorizontal_chainStyle layout_constraintVertical_chainStylelayout_constraintVertical_weightGuideline好像,還有個(gè)比較特殊的,叫Guideline。
好吧,繼續(xù)~
六、嘗試使用Guideline
android.support.constraint.Guideline該類比較簡(jiǎn)單,主要用于輔助布局,即類似為輔助線,橫向的、縱向的。該布局是不會(huì)顯示到界面上的。
所以其有個(gè)屬性為:
android:orientation取值為”vertical”和”horizontal”.
除此以外,還差個(gè)屬性,決定該輔助線的位置:
- 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%。
好了,下面看一個(gè)例子,剛才我們的浮點(diǎn)按鈕,我決定通過(guò)兩根輔助線來(lái)定位,一根橫向距離底部80%,一個(gè)縱向距離頂部80%,浮點(diǎn)按鈕就定位在他們交叉的地方。
<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" /> </....>我感覺(jué)都不用解釋了~~看眼效果圖吧:
到此,屬性基本上講完啦~
可以看到,上述相當(dāng)復(fù)雜的一個(gè)布局,在ConstraintLayout中完全沒(méi)有嵌套!
六、總結(jié)
本文通過(guò)實(shí)際的按鈕,基本上介紹了ConstraintLayout所支持的所有的屬性,全文沒(méi)有提及拖拽,因?yàn)楫?dāng)界面復(fù)雜之后,想要完美的拖拽實(shí)在是太難了,而且誰(shuí)也不期望,看不懂拖拽完成后的布局屬性吧~
所以,我建議還是盡可能手寫,通過(guò)本文這樣一個(gè)流程,雖然支持的屬性有20多個(gè),但是分類后并不難記,難記也可以拿出本文翻一翻~
好了,思考了半天,如何通過(guò)一個(gè)案例介紹完所有的屬性,總體來(lái)說(shuō)還是完成了,給自己點(diǎn)個(gè)贊。
轉(zhuǎn)載于:https://www.cnblogs.com/ryq2014/p/8328833.html
總結(jié)
以上是生活随笔為你收集整理的【转】 ConstraintLayout 完全解析 快来优化你的布局吧的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: BGP-MED-2
- 下一篇: leetcode 66 Plus One