跟我一起學ConstraintLayout (1)

ConstraintLayout簡介

一種應對復雜多層嵌套或復雜依賴關系的高效布局,解決相對布局中由于目標控件隱藏導致的布局混亂而被迫嵌套布局的問題,解決權重百分比布局時LinearLayout的不足,并且提供特殊布局方式,如圓形布局、特殊疊加擺放等。
學習本布局的目標是實現(xiàn)不嵌套的復雜布局。
本系列學習心得,先講解基本用法,然后逐步進行進階實戰(zhàn)UI效果的實現(xiàn),采坑之路的心得等。

低版本用Compile,關鍵字。高版本Android Studio如下

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

如下圖方法數(shù)統(tǒng)計圖,所以對整個項目的包大小不構成威脅,可以放心食用,入口有點甜。


image.png

目錄

  1. 基礎屬性介紹
  2. 相對擺放
  3. 水平居中以及權重居中
  4. Margin間距失效
  5. 目標依賴約束對象隱身術
  6. 圓形布局
  7. 寬高約束
  8. Guideline
  9. 鏈式布局
  10. 布局分組-控制可見性

基礎屬性介紹

大家應該熟悉相對布局RelativeLayout,有很多belowOf,rightOf類似的屬性,用于做控件之間相對定位布局。所以開篇我們先講這個基礎屬性,如何在ConstraintLayout布局內,進行相對定位。

屬性名 簡介
layout_constraintLeft_toLeftOf 當前控件A左側依賴于目標約束對象B的左側,簡單點 就是A左靠近B左側對齊
layout_constraintLeft_toRightOf 當前控件A左側依賴于目標約束對象B的右側,簡單點 就是A左靠近B右側對齊
layout_constraintRight_toLeftOf 當前A右側靠近B左側對齊,接下來的屬性類似...
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf 文本基準線對齊
layout_constraintStart_toEndOf 當前控件A起始邊與目標約束對象B尾部對齊
layout_constraintStart_toStartOf 當前控件A起始邊與目標約束對象B起始邊對齊,下面類似
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
image.png

如上所述,常見的基本屬性就是這些,我們先學習這些近似相對布局的屬性。理解上述屬性的宗旨是,把當前控件分為上下左右四個方向,在不同的情況下根據(jù)UI圖,進行相對約束申明。

常用特性介紹

1. 相對擺放

下面2個控件Button1在父布局右上角,button2只申明在button1的下方。跟相對布局一樣,Android坐標默認是中心點在左上角,所以不申明水平約束,會默認讓Button2在左側。

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


    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="我在右上角"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="我在他下面"
       app:layout_constraintTop_toBottomOf="@id/button1"/>


</android.support.constraint.ConstraintLayout>
2. 水平居中以及權重居中

下面開始進階使用,如何水平居中。在下圖中的ConstraintLayout下,Button控件就像是橡皮泥,多重約束控制時,會被拉伸。比如view的左側要貼住父布局左側,view的右側又要靠近父布局右側,這樣view只好在中間開始被向兩邊拉伸。這個就跟你在媳婦和公婆之間糾結一樣,兩邊都要你表態(tài),哈哈。但是人是有私心的,又要偏袒一方,或者一方一直處于劣勢,要維護下,所以又出現(xiàn)bias概念。如下圖,雖然是水平居中,但是button2明顯偏向某一方,0-1的權重值,越大就越靠近右側。當然你想玩得轉,設置0.5,兩邊不偏袒(我一條狗為啥說這個)。這里說的是水平居中實現(xiàn)方式,至于垂直居中和父布局居中類似,依樣畫葫蘆,自行實踐。紅色的TextView展示如何平鋪填滿寬度,設置0dp(match_constraint )代表matchParent。

<?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"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="水平居中"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginTop="60dp"
        android:text="水平居中后偏向某一側"
        app:layout_constraintHorizontal_bias="0.6"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/button3"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="水平填滿"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button2"/>

</android.support.constraint.ConstraintLayout>
image.png
3. Margin間距失效

在這里要注意一點,ConstraintLayout布局中,我想讓Button2布局不遮擋演示的Button1,直接設置
android:layout_marginTop="60dp"是不行的。如下圖,沒有變更位置,控件依然遮擋Button1.

      <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginTop="60dp"
        android:text="水平居中后偏向某一側"
        app:layout_constraintHorizontal_bias="0.6"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        />
image.png

通過上面實踐發(fā)現(xiàn),沒有申明某一邊的約束目標對象(比如這里是parent),設置對應的邊的margin不起作用。比如我button1是有左側約束對象的,此時設置300dp左間距,生效!

    <Button
        android:id="@+id/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginLeft="300dp"
        android:text="水平居中"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>
image.png
4. 目標依賴約束對象隱身術

在UI實現(xiàn)中,有很多控件是在某一些場景下才會顯示,但是布局肯定又得把它寫進去。然后顯示時,它又要占據(jù)當前常規(guī)情況下的控件的位置,需要調整這個常規(guī)控件向左或者向右移動位置。這種目標隱身術,平常通過對這2個控件進行LinearLayout嵌套等方式去實現(xiàn),很不好辦。下面介紹隱身術的應對辦法,goneMargin大法。

layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom

我在布局里,要求button1在最上面,button2和button3要相對button1在它下面,當button2顯示時,button3要在button2下面。此時就需要這樣布局了。

<?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/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="你們跟著我"
        app:layout_constraintLeft_toLeftOf="parent"
        />

    <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginTop="100dp"
        android:text="我是2.我在他下面"
        android:visibility="visible"
        app:layout_constraintTop_toBottomOf="@id/button1"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="我是3.我在他下面"
        app:layout_constraintTop_toBottomOf="@id/button2"
        />
</android.support.constraint.ConstraintLayout>
image.png

接下來我再設置 android:visibility="gone",隱藏button2,神奇的一幕發(fā)生了。此時button2消失后,button2的marginTop100dp也沒了,button3仍然保持button2之前對button1的依賴約束。

image.png

那么如果我還想要維持之前的100dp怎么辦,設置button3的marginTop為100dp,不行,會變成下面圖的樣子。


這個時候就請出goneMargin了,如下圖。

<?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/button1"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="你們跟著我"
        app:layout_constraintLeft_toLeftOf="parent"
        />

    <Button
        android:id="@+id/button2"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_marginTop="100dp"
        android:text="我是2.我在他下面"
        android:visibility="visible"
        app:layout_constraintTop_toBottomOf="@id/button1"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="150dp"
        android:layout_height="50dp"
        app:layout_goneMarginTop="100dp"
        android:text="我是3.我在他下面"
        app:layout_constraintTop_toBottomOf="@id/button2"
        />
</android.support.constraint.ConstraintLayout>
image.png

設置button2消失后,如下圖


image.png

OK,一切如我們想要的效果一樣,完美。那么如果我不需要在button2消失后,保持這么多間距呢,
app:layout_goneMarginTop改下即可,實際業(yè)務場景經常有這種需要,上方的布局消失后,與頂部間距有不一樣的間距,此時goneMargin就發(fā)揮大作用了。不用自己代碼里搞事情去實現(xiàn)動態(tài)區(qū)分布局。

5. 圓形布局

實現(xiàn)星星環(huán)繞效果的布局,一看這個效果,開發(fā)頭都大了,自定義layout group?不不不,我們有新的真愛來救我們了。圓形布局大法幫你實現(xiàn)各種吊炸天的設計效果圖。另外也可實現(xiàn)頭像右上角角標效果。

layout_constraintCircle :依賴哪個控件進行布局
layout_constraintCircleRadius :到依賴對象中心的距離
layout_constraintCircleAngle :當前要擺放的控件應處于哪個角度(度數(shù),從0到360)
image.png

廢話不多說,show you fuck code。

<?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/button1"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:text="垂直居中"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="A"
        app:layout_constraintCircle="@+id/button1"
        app:layout_constraintCircleAngle="45"
        app:layout_constraintCircleRadius="70dp"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="B"
        app:layout_constraintCircle="@+id/button1"
        app:layout_constraintCircleAngle="90"
        app:layout_constraintCircleRadius="70dp"/>

    <Button
        android:id="@+id/button4"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text="C"
        app:layout_constraintCircle="@+id/button1"
        app:layout_constraintCircleAngle="135"
        app:layout_constraintCircleRadius="70dp"/>
</android.support.constraint.ConstraintLayout>
image.png

上圖我們實現(xiàn)了A在45°方向擺放,并且靠近依賴約束的對象的中心點70dp,B和C類似,跟A間隔45°擺放,一種星星環(huán)繞效果實現(xiàn)好了。想想以前怎么實現(xiàn),是不是淚奔。。。組織上來拯救我們了,給google遞茶,遞辣條!

6. 寬高約束

很多時候我們需要設置最大寬度和高度,或者最小寬度或高度。那么在ConstraintLayout里,我們將接觸多種限制手段。

屬性 簡介
layout_constraintWidth_min和layout_constraintHeight_min 設置寬高的最小值
layout_constraintWidth_max和layout_constraintHeight_max 設置寬高的最大值
layout_constraintWidth_percent和layout_constraintHeight_percent 設置寬高的比例值,需要設置寬高layout_width、layout_height屬性為MATCH_CONSTRAINT(0dp)

除了上述屬性外,1.1版本開始新增加WRAP_CONTENT時的強制約束特性,下圖中,由于WRAP_CONTENT時,寬高約束失效,第一個控件雖然申明了最大寬度,但是并沒有起作用。但是第二個控件 app:layout_constrainedWidth="true"設置后,寬度被限制在200dp。

//屬性
app:layout_constrainedWidth=”true|false”
app:layout_constrainedHeight=”true|false”

<?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/button1"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:text="我的寬度被限制了,但是不起作用我的寬度被限制了,但是不起作用我的寬度被限制了,但是不起作用"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintWidth_max="200dp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:maxWidth="100dp"
        android:text="寬度限制起作用了,寬度限制起作用了寬度限制起作用了寬度限制起作用了"
        app:layout_constrainedWidth="true"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button1"
        app:layout_constraintWidth_max="200dp"/>


</android.support.constraint.ConstraintLayout>

image.png

此外,可以設置寬高的比例,比如16:9.下圖文本顯示“111”控件是16:9的效果,"H,16:9"代表約束高度,且保持寬高比16:9;根據(jù)上面的學習,寬度0dp時,設置了左邊和右邊的依賴約束對象為parent,所以寬度是已知的充滿屏幕;利用已知的寬計算出 高度=寬度*16/9。
對于這一塊,我也是有很多疑問,還沒完全理解,等理解后,再對ratio進行詳細解釋。下面這一句是別人貼出來的。

In this case the system sets the largest dimensions the satisfies all constraints and maintains the aspect ratio specified.

<?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">

    <TextView
        android:id="@+id/tv1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@color/colorAccent"
        android:text="1111111"
        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"
        />

    <TextView
        android:id="@+id/tv2"
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:background="#cccccc"
        android:gravity="center"
        android:text="22222"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="W,1:4"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:background="@color/colorPrimary"
        android:text="4444444"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="W,1:4"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />


    <TextView
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:background="@color/colorPrimary"
        android:text="55555"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="H,1:4"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:background="@color/colorPrimary"
        android:text="333333"
        app:layout_constraintHeight_percent="0.3"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

</android.support.constraint.ConstraintLayout>
image.png
7. Guideline

有些時候UI效果把界面化為幾個區(qū)域,A區(qū)域會根據(jù)B區(qū)域的寬度而動態(tài)調整位置?;蛘哂袝r候,某些控件要作為一個整體在同一垂直方向都居中(這些控件在同一個X坐標),但是以前的相對布局單純設置垂直居中就會被疊加。比如下圖效果,1和2控件都要在垂直居中,使用Guideline可以愉快的解決這些問題。其中的 app:layout_constraintGuide_percent="0.5"表示在垂直方向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"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5"

        />

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorAccent"
        android:text="1111"
        app:layout_constraintBottom_toTopOf="@id/guideline"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorPrimary"
        android:text="2222"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/guideline"/>

</android.support.constraint.ConstraintLayout>
8. 鏈式布局

當AB控件相互之間建立依賴約束時,比如A申明右邊與B的左邊約束,B同樣申明,形成一個回路,就產生了鏈AB。比如官網的下圖


image.png

image.png

其中第一個申明的控件作為鏈頭,所有鏈的屬性在鏈頭申明,比如chainstyle。根據(jù)官網的圖,chainstyle有下面幾種,默認是spread。


image.png

重點講下權重鏈,類似于linearlayout的weight使用,但是比它要更強大。設置0dp(MATCH_CONSTRAINT)的鏈內控件將瓜分剩余可用控件,根據(jù)權重值,分割不同的大小。如下圖,Button1和button2設置了0dp,因為button1設置了做間距80dp,瓜分剩余空間是在這個區(qū)域右側。由于未設置具體權重,所以2者均分。而button3、button4、button5不一樣,button5設置了50dp寬度,但是未設置權重,而butto3和button4分別設置了1、2的權重值,所以button4是button3的2倍大小。


image.png
<?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/button1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:text="Button1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/button2"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintLeft_toRightOf="@+id/button1"
        app:layout_constraintRight_toRightOf="parent"/>


    <Button
        android:id="@+id/button3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/button4"/>

    <Button
        android:id="@+id/button4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintLeft_toRightOf="@+id/button3"
        app:layout_constraintRight_toLeftOf="@+id/button5"
        />

    <Button
        android:id="@+id/button5"
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        android:text="Button5"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/button4"
        app:layout_constraintRight_toRightOf="parent"/>

</android.support.constraint.ConstraintLayout>


9. 布局分組-控制可見性

利用constraint.Group對布局里的控件進行分組,app:constraint_referenced_ids里申明同一組的控件。
看源碼其實group類繼承ConstraintHelper,最終繼承的view。我們通過referenced_ids屬性設置的id數(shù)組最終被它存放,最終updatePreLayout方法遍歷設置可見或不可見等。比如下圖,我button1所在的group設置了invisible,button2保持了與button1的約束。而左下角放置的2個button都因為group設置了gone,導致隱藏了。值得注意的是,一旦被設置到某個分組group控件里,子控件本身的visibility屬性就不起作用了,group設置的隱藏和可見才是最終的效果,默認group是顯示。


image.png
<?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.Group
        android:id="@+id/group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        app:constraint_referenced_ids="button1"/>

    <android.support.constraint.Group
        android:id="@+id/group2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:constraint_referenced_ids="button3,button4"/>

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:text="Button1"
        android:visibility="gone"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/button2"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintLeft_toRightOf="@+id/button1"
        app:layout_constraintRight_toRightOf="parent"/>


    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        />


    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@id/button3"
        />
</android.support.constraint.ConstraintLayout>

而看源碼group,像space控件一樣draw()為空,沒有繪制動作,性能上沒有什么負擔。在上面介紹的Guideline也是同樣如此,

    public void onDraw(Canvas canvas) {
    }

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (this.mUseViewMeasure) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        } else {
            this.setMeasuredDimension(0, 0);
        }

    }
10. Barrier

如果A控件要同時在B和C的右側,而B和C又長度不一定,此時相對布局無法確定A到底寫誰右側好,而只能嵌套一個層,然后A在B和C的父布局右側,這樣很不好,所以Barrier界線就是拿來做這種特殊布局的。

<?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/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:text="Button1-111111111111"
        app:layout_constraintLeft_toLeftOf="parent"
        />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:text="Button2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button1"/>

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="button1,button2"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Button3"
        app:layout_constraintLeft_toRightOf="@id/barrier"
        app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
image.png

下面是在具體實踐中的一種UI效果事例,僅供參考練習。整個布局只有一層,避免了嵌套過審的問題?!皬埲焙拖旅娴霓D圈圖片以及“我放學了”文本提示都是要求作為一個整體垂直居中,如果常規(guī)的布局,需要把這三個放到一個容器里一起進行居中,必然存在嵌套,但是約束布局就不需要這樣。下面轉圈圖片消失時,我放學文本提示要自動依賴顯示在左側方形圖片右側,與張三左對齊,傳統(tǒng)的布局也需要對我放學了文本和轉圈圖片進行嵌套,這樣加上上面的嵌套,導致多了2層嵌套。


image.png
<?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="67dp"
    >

    <ImageView
        android:id="@+id/sdv_chat_dialog_header"
        android:layout_width="44dp"
        android:layout_height="44dp"
        android:layout_marginLeft="16dp"
        android:background="@null"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:src="@drawable/ic_launcher_background"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:id="@+id/rrv_remind_unread"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="34dp"
        android:gravity="center"
        android:includeFontPadding="false"
        android:textColor="#ff4444"
        app:layout_constraintLeft_toLeftOf="@id/sdv_chat_dialog_header"
        app:layout_constraintTop_toTopOf="@id/sdv_chat_dialog_header"
        tools:text="9"/>

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="sdv_chat_dialog_header"/>

    <android.support.constraint.Guideline
        android:id="@+id/guideline_horizontal_center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5"
        />

    <TextView
        android:id="@+id/tv_chat_dialog_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="11dp"
        android:includeFontPadding="false"
        android:singleLine="true"
        android:textColor="#000000"
        android:textSize="17sp"
        app:layout_constraintBottom_toTopOf="@id/guideline_horizontal_center"
        app:layout_constraintLeft_toRightOf="@id/barrier"
        tools:text="張三"/>

    <TextView
        android:id="@+id/tv_chat_dialog_update_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="16dp"
        android:includeFontPadding="false"
        android:textSize="12sp"
        app:layout_constraintBaseline_toBaselineOf="@id/tv_chat_dialog_name"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="18:05"/>


    <ImageView
        android:id="@+id/iv_chat_watch_sync"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="11dp"
        android:layout_marginRight="5dp"
        android:layout_marginTop="2dp"
        android:background="@mipmap/ic_synchronization"
        android:visibility="visible"
        app:layout_constraintLeft_toRightOf="@id/barrier"
        app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_center"
        />

    <TextView
        android:id="@+id/tv_chat_dialog_last_msg_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="2dp"
        android:includeFontPadding="false"
        android:singleLine="true"
        android:textColor="#000000"
        android:textSize="13sp"
        app:layout_constraintLeft_toRightOf="@id/iv_chat_watch_sync"
        app:layout_constraintRight_toLeftOf="@id/tv_chat_dialog_update_time"
        app:layout_constraintTop_toBottomOf="@id/guideline_horizontal_center"
        app:layout_goneMarginLeft="11dp"
        tools:text="我放學了,我放學了,我放學了,我放學了,我放學了,我放學了,我放學了,我放學了,我放學了,我放學了,我放學了8888888"/>


    <View
        android:id="@+id/view_divider"
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:layout_marginLeft="72dp"
        android:background="#cccccc"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>

</android.support.constraint.ConstraintLayout>

結尾

約束布局也不是一定要完全替代傳統(tǒng)布局,合適的才是最好的。充分利用各種布局的特性滿足UI效果,才是最好的編碼方式。
到這里,一些常見的使用已經具備了,剩下的就是實戰(zhàn)磨合API,還有一些進階配合使用下一次再講。
比如ConstraintSet,在代碼里動態(tài)改變約束,做一些布局動畫。代碼就不用貼出來,都在上面。大家可以在拷貝后,不斷修改以便熟悉API。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容