[Android]帶你了解 Android 約束布局 ConstraintLayout

注:本文是玉剛說的副本

ConstraintLayout是Android新推出的一個布局,其性能更好,連官方的hello world都用ConstraintLayout來寫了。所以極力推薦使用ConstraintLayout來編寫布局。

本文主要介紹一下如何使用代碼來編寫ConstraintLayout布局。

好了,開始我們的征程。

ConstraintLayout簡介

ConstraintLayout,可以翻譯為約束布局,在2016年Google I/O 大會上發布。我們知道,當布局嵌套過多時會出現一些性能問題。之前我們可以去通過RelativeLayout或者GridLayout來減少這種布局嵌套的問題。現在,我們可以改用ConstraintLayout來減少布局的層級結構。ConstraintLayout相比RelativeLayout,其性能更好,也更容易使用,結合Android Studio的布局編輯器可以實現拖拽控件來編寫布局等等。

如果我們是新建工程,則Android Studio會默認幫我們加入ConstraintLayout的依賴了。如果我們是改造舊項目,可以在build.gradle中添加以下依賴:

    implementation 'com.android.support.constraint:constraint-layout:1.1.2'

然后就可以使用ConstraintLayout了。

相對位置

要在ConstraintLayout中確定view的位置,必須至少添加一個水平和垂直的約束。每一個約束表示到另一個view,父布局,或者不可見的參考線的連接或者對齊。如果水平或者垂直方向上沒有約束,那么其位置就是0。

我們先來看個例子:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    
android:layout_width="match_parent"    
android:layout_height="match_parent">  
  <Button        
android:layout_width="wrap_content"        
android:layout_height="wrap_content"        
android:text="左對齊"        
app:layout_constraintLeft_toLeftOf="parent"        
app:layout_constraintTop_toTopOf="parent"/>    

<Button        
android:layout_width="wrap_content"        
android:layout_height="wrap_content"        
android:text="右對齊"        
app:layout_constraintRight_toRightOf="parent"        
app:layout_constraintTop_toTopOf="parent"/>    

<Button        
android:layout_width="wrap_content"        
android:layout_height="wrap_content"        
android:text="水平居中"        
app:layout_constraintLeft_toLeftOf="parent"        
app:layout_constraintRight_toRightOf="parent"/>   

 <Button       
 android:layout_width="wrap_content"        
android:layout_height="wrap_content"        
android:text="垂直居中"        
app:layout_constraintBottom_toBottomOf="parent"        
app:layout_constraintTop_toTopOf="parent"/>    

<Button        
android:layout_width="wrap_content"        
android:layout_height="wrap_content"        
android:text="底部對齊"       
 app:layout_constraintBottom_toBottomOf="parent"        
app:layout_constraintLeft_toLeftOf="parent"/>   

 <Button       
 android:layout_width="wrap_content"       
 android:layout_height="wrap_content"       
 android:text="水平居中+垂直居中"        
app:layout_constraintBottom_toBottomOf="parent"        
app:layout_constraintLeft_toLeftOf="parent"        
app:layout_constraintRight_toRightOf="parent"        
app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

其顯示效果如下圖所示:

image

上面例子中app:layout_constraintLeft_toLeftOf="parent" 表示view的左邊對齊父布局的左邊。

app:layout_constraintRight_toRightOf="parent"則表示view的右邊對齊父布局的右邊。

其他同理,就不一一說明。

相對位置的屬性
下面是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
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf

這些屬性的值即可以是parent,也可以是某個view的id。

居中顯示
如果一個view滿足以下

  app:layout_constraintLeft_toLeftOf="parent"      
  app:layout_constraintRight_toRightOf="parent"

即view的左邊對齊父布局的左邊,view的右邊對齊父布局的右邊,除非這個view的大小剛好充滿整個父布局;否則的話,就是水平居中顯示了。我們可以理解為有兩個力,它們左右互搏,view只能給扯到中間了。

再來兩個例子,把上面的屬性基本都涉及到了,看下估計就懂了,就不逐一說明了。

  • 例子一:水平方向的相對位置
    這里主要關注水平方式的屬性即可。
    布局代碼:
<?xml version="1.0" encoding="utf-8"?>
<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">    
<Button
        android:id="@+id/btn_center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#00f"
        android:text="水平參照物"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#f00"
        android:text="Left_toLeftOf"
        app:layout_constraintBottom_toTopOf="@id/btn_center"
        app:layout_constraintLeft_toLeftOf="@id/btn_center"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0f0"
        android:text="Right_toLeftOf"
        app:layout_constraintBottom_toTopOf="@id/btn_center"
        app:layout_constraintRight_toLeftOf="@id/btn_center"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0f0"
        android:text="Right_toRightOf"
        app:layout_constraintRight_toRightOf="@id/btn_center"
        app:layout_constraintTop_toBottomOf="@id/btn_center"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#f00"
        android:text="Left_toRightOf"
        app:layout_constraintLeft_toRightOf="@id/btn_center"
        app:layout_constraintTop_toBottomOf="@id/btn_center"/>
</android.support.constraint.ConstraintLayout>

顯示效果如下:

image
  • 例子二:豎直方向的相對位置
    這里主要關注豎直方向的屬性即可。
    布局代碼:
<?xml version="1.0" encoding="utf-8"?>
<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"> 
   <Button
        android:id="@+id/btn_center"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:background="#00f"
        android:text="豎直參照物"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#f00"
        android:text="Top_toTopOf"
        app:layout_constraintTop_toTopOf="@id/btn_center"
        app:layout_constraintRight_toLeftOf="@id/btn_center"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0f0"
        android:text="Bottom_toTopOf"
        app:layout_constraintBottom_toTopOf="@id/btn_center"
        app:layout_constraintRight_toLeftOf="@id/btn_center"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0f0"
        android:text="Top_toBottomOf"
        app:layout_constraintLeft_toRightOf="@id/btn_center"
        app:layout_constraintTop_toBottomOf="@id/btn_center"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#f00"
        android:text="Bottom_toBottomOf"
        app:layout_constraintLeft_toRightOf="@id/btn_center"
        app:layout_constraintBottom_toBottomOf="@id/btn_center"/>
</android.support.constraint.ConstraintLayout>

顯示效果如下:

image

尺寸約束

view中使用warp_content或者固定值等等是沒有問題的。但是ConstraintLayout中不支持MATCH_PARENT這個值,如果需要實現跟MATCH_PARENT同樣的效果,可以使用0dp來代替,其表示MATCH_CONSTRAINT,即適應約束。其跟MATCH_PARENT還是有區別的,后面的例子(寬高比那一小節)會提到。
我們來看下例子:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btn_center" 
       android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/> 

   <Button
        android:id="@+id/btn_1"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="具體數值:200dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btn_center"/>

    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="0dp(MATCH_CONSTRAINT)"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/btn_1"/>

</android.support.constraint.ConstraintLayout>

其效果如下:

image

寬高比

ConstraintLayout中,還可以將寬定義成高的一個比例或者高定義成寬的比率。首先,需要將寬或者高設置為0dp(即MATCH_CONSTRAINT),即要適應約束條件。然后通過layout_constraintDimensionRatio屬性設置一個比率即可。這個比率可以是一個浮點數,表示寬度和高度之間的比率;也可以是“寬度:高度”形式的比率。比如:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:text="-------------------寬高比2:1-------------------"
        app:layout_constraintDimensionRatio="2:1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>

這里將按鈕的高度設置為寬度的一半了。如下圖所示:

image

如果寬和高都設置為0dpMATCH_CONSTRAINT),那么layout_constraintDimensionRatio的值需要先加一個"W,"或者"H,"來表示約束寬度或高度。如下:

    <Button
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="H,16:9"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

這里例子是說,首先寬度將滿足父布局的約束,然后將按照16:9的比例設置高度。

百分比寬高

ConstraintLayout還能使用百分比來設置view的寬高。
要使用百分比,寬或高同樣要設置為0dpMATCH_CONSTRAINT)。
然后設置以下屬性即可:

app:layout_constraintWidth_default="percent" //設置寬為百分比app:layout_constraintWidth_percent="0.3" //0到1之間的值或app:layout_constraintHeight_default="percent" //設置高為百分比app:layout_constraintHeight_percent="0.3" //0到1之間的值

例子如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="寬50%"
        app:layout_constraintHeight_default="percent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintWidth_percent="0.5"/>
</android.support.constraint.ConstraintLayout>

效果如下:

image

位置偏向

如果想讓view的位置偏向某一側,可以使用以下的兩個屬性來設置:

layout_constraintHorizontal_bias  //水平偏向layout_constraintVertical_bias  //豎直偏向

其值同樣也是0到1之間。
比如,以下例子為橫向偏向左側30%,默認的居中效果就是50%:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="左邊偏向30%"
        app:layout_constraintHorizontal_bias="0.3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>

顯示效果如下:

image

權重

LinearLayout中可以設置權重,ConstraintLayout同樣也有這玩意。
通過設置以下兩個屬性:

app:layout_constraintHorizontal_weight //水平權重
app:layout_constraintVertical_weight //豎直權重

然后將相連的view兩兩約束好即可。如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/btn_1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="權重為1"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/btn_2"/>
    <Button
        android:id="@+id/btn_2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="權重為2"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintLeft_toRightOf="@id/btn_1"
        app:layout_constraintRight_toLeftOf="@id/btn_3"/>
    <Button
        android:id="@+id/btn_3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintHorizontal_weight="2"
        android:text="權重為2"
        app:layout_constraintLeft_toRightOf="@id/btn_2"
        app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>

顯示效果如下:

image

上面這種將相連的view兩兩約束好的實際上就形成了鏈。在ConstraintLayout中可以實現各種不同的鏈,權重鏈是其中一種。整個鏈由鏈中的第一個view(鏈頭)上設置的屬性控制。

官網上一共有5種樣式的鏈:

image

可以通過以下屬性來設置鏈的樣式:

app:layout_constraintHorizontal_chainStyle="spread|spread_inside|packed"

下面簡單說下上面5種樣式的實現:

  1. Spread Chain
    默認樣式就是spread,所以可以不用設置。然后寬或高非0即可。
    效果如下圖:
image
所以:Spread Chain = spread + 寬或高非0
  1. Spread Inside Chain
    這里設置以下屬性
app:layout_constraintHorizontal_chainStyle="spread_inside"

然后寬度同樣為非0,效果如下:

image

所以:Spread Inside Chain = spread_inside + 寬或高非0

  1. Weighter Chain
    即權重鏈,具體可以查看上一小節。
    Weighter Chain = spread + 寬或高為0 + 權重值

  2. Packed Chain
    Packed是一種view聚攏起來的效果,這里設置以下屬性

app:layout_constraintHorizontal_chainStyle="packed"

寬度同樣為非0,效果如下:

image

所以:Packed Chain = packed + 寬或高非0

  1. Packed Chain with bias
    就是Packed Chain再加一個偏向屬性,偏向屬性可以看下前面的內容。
    效果如下圖所示:

    image

    所以:Packed Chain with bias = Packed Chain + bias

Guideline輔助線

Guideline可以用來輔助布局,通過Guideline能創建出一條條的水平線或者垂直線,該線不會顯示到界面上,但是能夠利用這些線條來添加約束去完成界面的布局。
Guideline主要的屬性有:

    android:orientation="horizontal|vertical"
     app:layout_constraintGuide_begin="30dp"
     app:layout_constraintGuide_end="30dp"
    app:layout_constraintGuide_percent="0.5"

android:orientation=”horizontal|vertical”表示是水平或垂直引導線。

app:layout_constraintGuide_begin=”30dp”,如果是水平引導線,則距離布局頂部30dp,如果是垂直引導線,則距離布局左邊30dp。

app:layout_constraintGuide_end=”30dp”,如果是水平引導線,則距離布局底部30dp,如果是垂直引導線,則距離布局右邊30dp。

app:layout_constraintGuide_percent=”0.5”,如果是水平引導線,則距離布局頂部為整個布局高度的50%,如果是垂直引導線,則距離布局左邊文這個布局寬度的50%。

來看個例子:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <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.5"/>
    <android.support.constraint.Guideline
        android:id="@+id/guideline_v" 
       android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5"/>
    <Button
        app:layout_constraintLeft_toLeftOf="@id/guideline_v"
        app:layout_constraintTop_toTopOf="@id/guideline_h"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="輔助線定位"/>
</android.support.constraint.ConstraintLayout>

如下圖所示:

image

本節基本把ConstraintLayout的常用屬性都介紹了一遍了。通過使用ConstraintLayout能夠減少布局嵌套等,可以有效的提升性能。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,996評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,128評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,524評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,759評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,310評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,065評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,249評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,479評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,984評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,228評論 2 375

推薦閱讀更多精彩內容