ConstraintLayout完全解析

一、概述

ConstraintLayout(約束布局),是Google在2016年推出的一種布局,其簡單、扁平化的使用方式,深得廣大開發(fā)者的喜愛。這篇文章我們不探討其性能方面的優(yōu)勢。意在于和大家一起學(xué)習(xí)ConstraintLayout的相關(guān)使用。

implementation 'com.android.support.constraint:constraint-layout:1.1.2'
二、可視化界面的使用

Android Studio 2.2開始提供了對ConstraintLayout可視化的操作。對于可視化操作方面內(nèi)容。這里大家可以通過郭霖大神的博客進(jìn)行學(xué)習(xí)。

三、屬性介紹

屬性介紹是我們ConstraintLayout重點(diǎn)要學(xué)習(xí)的部分??梢暬缑嬷皇呛喕宋覀兪褂?,對它所使用的屬性,我們要至少知道其意,并可以進(jìn)行簡單的修改和手寫布局。
1、相對位置屬性

  • layout_constraintLeft_toLeftOf :當(dāng)前View的左側(cè)和另一個(gè)View的左側(cè)位置對齊
  • layout_constraintLeft_toRightOf :當(dāng)前view的左側(cè)會(huì)在另一個(gè)View的右側(cè)位置
  • layout_constraintRight_toLeftOf :當(dāng)前view的右側(cè)會(huì)在另一個(gè)View的左側(cè)位置
  • layout_constraintRight_toRightOf :當(dāng)前View的右側(cè)和另一個(gè)View的右側(cè)位置對齊
  • layout_constraintTop_toTopOf :頭部對齊
  • layout_constraintTop_toBottomOf :當(dāng)前View在另一個(gè)View的下側(cè)
  • layout_constraintBottom_toTopOf :當(dāng)前View在另一個(gè)View的上方
  • layout_constraintBottom_toBottomOf :底部對齊
  • layout_constraintBaseline_toBaselineOf :文字底部對齊
  • layout_constraintStart_toEndOf :同left_toRightOf
  • layout_constraintStart_toStartOf :同left_toLeftOf
  • layout_constraintEnd_toStartOf :同right_toLeftOf
  • layout_constraintEnd_toEndOf :同right_toRightOf

從ConstraintLayout的相對位置屬性可以看出和我們的RelativeLayout很相似,這也是有人將ConstraintLayout成為增強(qiáng)型的相對布局的原因,不過,其實(shí)現(xiàn)思想是完全不同的。從下面的例子我們可以看出來。

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btA"
        android:layout_width="148dp"
        android:layout_height="wrap_content"
        android:text="ButtonA"
        />
    <Button
        android:id="@+id/btB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ButtonB"
        android:layout_toRightOf="@id/btA"
        android:layout_alignParentRight="true"
        />
</RelativeLayout>
1-1

在圖片1-1中,我們使用RelativeLayout布局實(shí)現(xiàn)ButtonB在ButtonA的右側(cè)并填滿右側(cè)空間,我們將ButtonB的width設(shè)置為wrap_content,并且使用 android:layout_toRightOf="@id/btA"和android:layout_alignParentRight="true"兩個(gè)相對屬性。現(xiàn)在我們將布局改為ConstraintLayout并將相對屬性改為app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_to
EndOf="@+id/buttonB"

<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="buttonB"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/buttonA" />

    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="buttonA"
        />
</android.support.constraint.ConstraintLayout>
1-2

我們通過對比可以發(fā)現(xiàn)ButtonB同樣是wrap_content,但是我們的RelativeLayout會(huì)改變我們的View大小。而ConstraintLayout并不會(huì),他會(huì)像橡皮筋一樣拉著我們的View使他在我們的右側(cè)居中。并不會(huì)改變view的大小。當(dāng)我們的View寬度足夠大的時(shí)候超過了我們又側(cè)剩余寬度。也只是如1-3一樣,左右兩側(cè)的約束一樣大,使之感覺還是居中的。寬度還是我們設(shè)置的寬度并未被改變


1-3

接下來,如果我們想要讓我們的ConstraintLayout實(shí)現(xiàn)的效果和RelativeLayout一樣。官方給我們提供了一個(gè)match_constrain也就是0dp,我們?yōu)锽uttonB的寬度設(shè)置為match_constrain(0dp)就可以將我們的ButtonB填滿我們右側(cè)的布局了。而官方對match_constrain的解釋為

  • 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”.

可以理解為在ConstraintLayout中已經(jīng)不支持MATCH_PARENT,你可以通過MATCH_CONSTRAINT配合約束實(shí)現(xiàn)類似的效果。

理解了上面的內(nèi)容我們來看一下類似的內(nèi)容:強(qiáng)制約束。當(dāng)我們的ButtonB的width設(shè)置為wrap_content的時(shí)候,而我們的ButtonB的內(nèi)容過長就會(huì)像下圖一樣。導(dǎo)致約束失效。


1-4

而為了防止約束失效,在1.1.0版本中新增了一下屬性。

  • app:layout_constrainedWidth=”true|false” //默認(rèn)false
  • app:layout_constrainedHeight=”true|false” //默認(rèn)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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="buttonA"
        />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="buttonbuttonbuttonbuttonbuttonbuttonbutton"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/buttonA"
        app:layout_constrainedWidth="true"
    />

</android.support.constraint.ConstraintLayout>
1-5

2、GoneMargin

  • layout_goneMarginStart
  • layout_goneMarginEnd
  • layout_goneMarginLeft
  • layout_goneMarginTop
  • layout_goneMarginRight
  • layout_goneMarginBottom
    我們對Margin這個(gè)屬性應(yīng)該很熟悉,GoneMargin也很簡單,他的含義就是當(dāng)一個(gè)控件和另一個(gè)控件綁定后,如果另一個(gè)控件設(shè)置為Gone則,GoneMargin這組屬性會(huì)生效。
<?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/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="buttonA"
        android:visibility="gone"
        />

    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="buttonB"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/buttonA"
        app:layout_goneMarginLeft="40dp"
        />
</android.support.constraint.ConstraintLayout>
2-1

3、Chains(鏈)
鏈?zhǔn)刮覀兡軌驅(qū)σ唤M在水平或豎直方向互相關(guān)聯(lián)的控件的屬性進(jìn)行統(tǒng)一管理。成為鏈的條件:一組控件它們通過一個(gè)雙向的約束關(guān)系鏈接起來,首尾對parent約束。 并且鏈的屬性是由一條鏈的頭結(jié)點(diǎn)控制的,如下:


3-1

Chains Style:鏈的樣式有三種packed,spread_inside,spread

 app:layout_constraintHorizontal_chainStyle="packed | spread_inside | spread"
3-2
  • Spread Chain:Style為spread的時(shí)候。
  • Spread Chain:Style為spread_inside的時(shí)候。
  • Weighted Chain:當(dāng)Style為spread,width為0dp,可以使用Weighted屬性等分(Weighted屬性和線性布局的weighted用法相同)
  • Packed Chain 當(dāng)Style為packed的時(shí)候。
  • Packed Chain with Bias,當(dāng)Style為packed,并且設(shè)置了Bias屬性。Bias屬性下部分會(huì)講解
<?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/bt_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintEnd_toStartOf="@+id/bt_2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/bt_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="B"
        app:layout_constraintEnd_toStartOf="@+id/bt_3"
        app:layout_constraintStart_toEndOf="@+id/bt_1"
        app:layout_constraintTop_toTopOf="@+id/bt_1" />

    <Button
        android:id="@+id/bt_3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="c"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/bt_2"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

4、MATCH_CONSTRAINT
上邊我們說過ConstraintLayout取消了MATCH_PARENT由MATCH_CONSTRAINT來代替,默認(rèn)大小占用所有約束可用空間,并提供了以下屬性輔助我們使用。

  • layout_constraintWidth_min 設(shè)置最小寬度
  • layout_constraintHeight_min 設(shè)置最小高度
  • layout_constraintWidth_max 設(shè)置最大寬度
  • layout_constraintHeight_max 設(shè)置最大高度
  • layout_constraintWidth_percent 設(shè)置寬度相對父類寬度百富比
  • layout_constraintHeight_percent 設(shè)置高度相對父類高度的百分比

5、百分比布局
約束布局支持子控件設(shè)置寬高比,前提條件是至少需要將寬高中的一個(gè)設(shè)置為0dp。為了約束一個(gè)特定的邊,基于另一個(gè)邊的尺寸,可以預(yù)先附加W,或H以逗號隔開。

<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:layout_width="0dp"
        android:layout_height="0dp"http://高度基于寬度的尺寸所以高度為0dp,否則百分比屬性無效
        android:text="A"
        app:layout_constraintDimensionRatio="H,16:9"http://約束高度基于寬度的尺寸。
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
5-1

6、Guideline 輔助線
Guideline輔助線是一條寬或高為0dp的一個(gè)看不見的線,和LinearLayout一樣都有orientation屬性設(shè)置橫向或垂直。有三種定位方式

  • layout_constraintGuide_begin 距離父容器起始位置的距離(左側(cè)或頂部)垂直的輔助線使用
  • layout_constraintGuide_end 距離父容器結(jié)束位置的距離(右側(cè)或底部)水平的輔助線使用
  • layout_constraintGuide_percent 距離父容器寬度或高度的百分比(垂直或水平的輔助線都可以使用)

例:設(shè)置一條垂直方向距離父控件左側(cè)為100dp的Guideline:

<android.support.constraint.Guideline
        android:layout_width="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="100dp"
        android:layout_height="wrap_content"/>

7、Group為ConstraintLayout布局中的子控件進(jìn)行分組,可控制多個(gè)布局控件統(tǒng)一的可見性

    <android.support.constraint.Group
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"http://設(shè)置可見性
        app:constraint_referenced_ids="bt1,bt2"http://要管理的控件id
       />
        <Button
            android:id="@+id/bt1"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:text="A"
            />
        <Button
            android:id="@+id/bt2"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:text="A"
            />

8、Barrier,屏障,在約束布局中,可以將幾個(gè)View作為一個(gè)整體來使用,讓其他布局關(guān)聯(lián)約束這個(gè)整體。注意設(shè)置他的visibility不能控制這個(gè)整體的可見性。我們看一個(gè)例子。

<TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="姓名:"
        app:layout_constraintBottom_toBottomOf="@+id/et_name"
        app:layout_constraintTop_toTopOf="@+id/et_name"/>

    <TextView
        android:id="@+id/tv_contract"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="聯(lián)系方式:"
        app:layout_constraintBottom_toBottomOf="@+id/et_contract"
        app:layout_constraintTop_toTopOf="@+id/et_contract"/>

    <EditText
        android:id="@+id/et_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="請輸入姓名"
        app:layout_constraintLeft_toLeftOf="@+id/barrier"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <EditText
        android:id="@+id/et_contract"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="請輸入聯(lián)系方式"
        app:layout_constraintLeft_toLeftOf="@+id/barrier"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/et_name"/>

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="right"
        app:constraint_referenced_ids="tv_name,tv_contract"/>
8-1

這里我們把A區(qū)域中的姓名和聯(lián)系方式用Barrier關(guān)聯(lián)成一個(gè)整體。而右側(cè)的B區(qū)域中的EditText分別關(guān)聯(lián)約束這個(gè)Barrier整體。當(dāng)Barrier整體中的TextView的寬度發(fā)生改變時(shí),Barrier整體也是變化的,右側(cè)的B區(qū)域的寬度也會(huì)隨之改變。
9、Circular positioning (圓形定位)

  • 您可以將一個(gè)控件的中心以一定的角度和距離約束到另一個(gè)控件的中心,相當(dāng)于在一個(gè)圓上放置一個(gè)控件。
<Button android:id="@+id/buttonA" ... />
  <Button android:id="@+id/buttonB" ...
      //引用的控件ID
      app:layout_constraintCircle="@+id/buttonA"
      //圓半徑
      app:layout_constraintCircleRadius="100dp"
      //偏移圓角度  水平右方向?yàn)?逆時(shí)針方向旋轉(zhuǎn)
      app:layout_constraintCircleAngle="45" />
9-1

10、bias 權(quán)重

  • 當(dāng)一個(gè)控件擁有水平方向(左右兩個(gè)約束)或者垂直方向(上下兩個(gè)約束)的時(shí)候,我們可以通過app:layout_constraintHorizontal_bias
    ="0.16"或 app:layout_constraintVertical_bias="0.303"兩個(gè)屬性來對兩個(gè)方向的約束進(jìn)行百分比的調(diào)整。讓控件按百分比偏向一方。當(dāng)不設(shè)置是默認(rèn)是0.5居中的效果。
    <Button
        android:id="@+id/bt1"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:text="A"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.2" />
10-1

參考文章:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容