ConstraintLayout詳解

簡介

ConstraintLayout是谷歌2016年的I/O大會上推出的新控件,Constraint翻譯過來就是“約束”的意思,這個控件跟RelativeLayout類似,其子控件通過互相約束來確定位置、大小,但是比RelativeLayout的功能強大很多。使用這個控件作爲根佈局,可以實現很複雜的佈局情況,以前需要嵌套多層才能實現的佈局,現在可能一層就夠了。

谷歌的演示側重引導開發者通過鼠標拖控件的方式來寫佈局,ConstraintLayout也是比較好的支持了鼠標拖動佈局,也有不少相關博客主要以拖動講解爲主。但是還是有一些屬性無法通過鼠標拖動來完成,比如設置visible、尺寸單位(dp、px)等,所以我想,與其學習“可視化操作佈局+xml代碼佈局”,還不如直接學習“xml代碼佈局”。

要使用這個控件首先要在gralde中導入,如下:

dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
}

其次,在layout佈局文件中要引入自定義屬性的命名空間:xmlns:app=”http://schemas.android.com/apk/res-auto”,這樣下面的控件才能使用其中的自定義屬性:

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

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"/>

</android.support.constraint.ConstraintLayout>

這篇博客分爲四部分來講解ConstraintLayout:
1、第一部分講解ConstraintLayout中的子控件是如何確定位置的;
2、第二部分講解子控件是如何確定自身尺寸的;
3、第三部分講解chains鏈條特性,非常實用;
4、第四部分講解一些其它屬性;

控件位置

一、基本控制

主要有8個屬性來設置控件的位置,跟RelativeLayout差不多:

// 左邊向parent父控件的左邊靠攏(注意,這裏用的是“靠攏”,而不是“對齊”,後面就能體會到了)
app:layout_constraintLeft_toLeftOf="parent"

// 左邊向btn2的右邊靠攏。也就是再btn2的右邊,相當於RelativeLayout的toRightOf
app:layout_constraintLeft_toRightOf="@+id/btn2"

// 下面的6個屬性就不一一註釋了,參照上面兩個,一眼就能看懂
app:layout_constraintTop_toBottomOf=""
app:layout_constraintTop_toTopOf=""
app:layout_constraintRight_toLeftOf=""
app:layout_constraintRight_toRightOf=""
app:layout_constraintBottom_toBottomOf=""
app:layout_constraintBottom_toTopOf=""

在一個水平上設置一個約束是很清楚的,就跟RelativeLayout的toRightOf等屬性效果一樣,但是如果一個水平上設置兩個約束呢?比如下面:

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

這就是爲什麼前面爲什麼要用“靠攏”這個詞了,也可以理解這個控件爲什麼叫“約束控件”,這些屬性都不是絕對的,只是一種約束,控件的最終位置(其他屬性也一樣)是通過各種約束條件來決定的,而不是像RelativieLayout的toRightOf那樣一錘子買賣。

上面的意思就是左邊儘量向parent的左邊靠攏,右邊儘量向parent的右邊靠攏,最終就會取一箇中間狀態,讓控件同時滿足這兩個約束條件,所以這個button最終是居中的。你可以腦補一下,一個力向左邊拉,一個相等的力同時向右邊拉,所以你最終在中間了。

二、偏向性控制

ConstraintLayout還有個很好用的屬性app:layout_constraintHorizontal_bias=”0.1”,用來設置控件位置的偏向性,直接用代碼說明:

<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintHorizontal_bias="0.2"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"/>

可以這麼理解,btn既要向左邊靠攏,有要向右邊靠攏,然後就取了折中方案居中了。現在設置了layout_constraintHorizontal_bias約束,所以在滿足前面兩個約束的前提下,還要繼續滿足第三個約束。這個約束的意思是,在水平方向上,離左邊的距離爲0.2份,離右邊的距離0.8份,也就是離左邊更近一些。通過設置這個值,能輕鬆控制控件的相對於parent、其它控件的位置。

豎直方向的屬性是app:layout_constraintVertical_bias=”0.3”,與上面同理,就不展開了。

尺寸設置

尺寸的設置主要有三種方式:通用的屬性設置、約束條件設置、寬高比例設置

一、通用屬性設置

1、設置具體值
與其它layout一樣,設置android:layout_width=”100dp”就可以了。

2、由內容控制
與其它layout一樣,設置android:layout_width=”wrap_content”。

二、約束條件設置

設置android:layout_width=”0dp”,意思就是由約束條件控制。舉兩個例子就明白了:

// 寬度設置爲0dp,約束條件既要向parent左邊靠攏,又要向parent右邊靠攏,結果就是match_parent了
<Button
    android:id="@+id/btn1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"/>


// 寬度設置爲0dp,約束條件既要向btn1的左邊靠攏,又要向btn1的右邊靠攏,結果就是與btn1寬度一樣
<Button
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintRight_toRightOf="@+id/btn1"
    app:layout_constraintLeft_toLeftOf="@+id/btn1"/>

注意:如果只是單純地設置爲0dp,而沒有其它約束條件來約束尺寸,最終效果就是wrap_content,可以理解爲,在沒有其它約束條件時,wrap_content就成了唯一約束條件。

三、寬高比例設置

其實這個也屬於“約束條件設置”,只是這裏的約束條件變成了寬高比例。

ConstraintLayout有個app:layout_constraintDimensionRatio=”2:1”屬性,用於控制寬高比例,前面的數字是寬,後面數字是高。

要使這個屬性生效,需要要滿足兩個條件:
1、寬高屬性中其中一個屬性由約束條件設置,也就是設置爲0dp,並且設置了Right_toRight和Left_toLeft屬性;
2、另一個屬性不能爲0dp(從實際使用效果來看,最好設置爲固定值,設置爲wrap_content的效果很奇怪);

看下代碼示例就理解了:

// 最終的效果就是寬是高的2倍。如果height設置爲wrap_content,然後通過設置text屬性設置內容,最終效果也確實是寬是高的2倍,但是text的內容會換行。。。效果不盡人意。
<Button
    android:id="@+id/btn2"
    android:layout_width="0dp"
    android:layout_height="100dp"
    app:layout_constraintDimensionRatio="2:1"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"/>


Chains鏈條佈局

鏈條就是一系列控件在同一水平上首位相連,形成一個整體。實現鏈條需要滿足兩個條件:
1、btn1設置靠攏btn2左邊,同時btn2要設置靠攏btn1右邊,兩個控件都要設置屬性;
2、最左邊的btn要設置靠攏parent左邊,最右邊的btn要設置靠攏parent右邊;

形成chains鏈條後,這個chains就是一個整體了,通過對head頭(該鏈條上的第一個控件)設置app:layout_constraintHorizontal_chainStyle=”“屬性,能夠設置這個鏈條樣式。

下面是官網上顯示的各種樣式鏈條的效果圖:

這裏寫圖片描述

這5個樣式分爲三類,一類是直接通過style屬性設置(樣式1、2、4),一類是通過權重設置(樣式3),最後一類是通過bias屬性設置(樣式5)。

一、style屬性設置

通過對head頭控件設置app:layout_constraintHorizontal_chainStyle屬性,可以顯示出上面樣式1、2、4的效果:

<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/btn1"
        android:layout_width="wrap_content"
        app:layout_constraintHorizontal_chainStyle="spread" // 對應樣式1(默認樣式)
        app:layout_constraintHorizontal_chainStyle="spread_inside" // 對應樣式2
        app:layout_constraintHorizontal_chainStyle="packed" // 對應樣式4
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent" // 最左邊的鏈條靠攏parent左邊
        app:layout_constraintRight_toLeftOf="@+id/btn2" />

// 可以看到,btn1靠攏btn2的左邊,btn2靠攏btn1的右邊,同樣btn2和btn3也是如此
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintRight_toLeftOf="@+id/btn3" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintRight_toRightOf="parent" //最右邊的鏈條靠攏parent右邊
        app:layout_constraintLeft_toRightOf="@+id/btn2" />

</android.support.constraint.ConstraintLayout>


二、權重鏈

第3種樣式是加權鏈,在鏈條樣式爲spread的情況下,通過設置btn的app:layout_constraintHorizontal_weight=”“屬性來控制btn寬度,跟LinearLayout是一樣的,也需要把寬度設置爲0dp,代碼如下:

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content" // 不需要由weight決定寬
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/btn2" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="0dp" // width設置爲0,讓weight決定寬度
        android:layout_height="wrap_content"
        app:layout_constraintHorizontal_weight="1" // 佔剩下空間的1份
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintRight_toLeftOf="@+id/btn3" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="0dp"
        app:layout_constraintHorizontal_weight="1" //也佔1份,所以剩下空間要與btn2平分
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn2"
        app:layout_constraintRight_toRightOf="parent"/>


三、bias鏈條

在鏈條樣式是packed的情況下,通過設置head頭控件的bias屬性,來設置鏈條的位置(更偏向左或更偏向右)

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintHorizontal_bias="0.3" // 權重爲靠左0.3
        app:layout_constraintHorizontal_chainStyle="packed" // 樣式爲packed
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/btn2" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintRight_toLeftOf="@+id/btn3" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn2"
        app:layout_constraintRight_toRightOf="parent" />


其它屬性

一、app:layout_goneMarginLeft=”50dp”

這個屬性的意思是,如果左邊的控件是可見的,這個屬性不生效;如果左邊控件設置爲gone,這個屬性就會生效,離左邊間隔50dp。

這個屬性與android:layout_marginLeft=”“屬性不衝突,可以同時生效。

二、android.support.constraint.Guideline控件

這是一條基準線,沒有內容、不會顯示、draw方法都是空實現,它的作用就是輔助我們進行控件定位。

android:orientation=”vertical”屬性用來設置基準線的方向,垂直線或水平線;

app:layout_constraintGuide_percent=”0.7”屬性或android:layout_margin=”“屬性用來設置它的位置,percent=0.7就是離父控件左邊/上邊70%距離,margin=100dp就是離父控件左邊/上邊100dp

確定了它的方向和位置,我們就可以通過它來定位,設置其它控件的位置了

<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/line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" // 設置方向和位置
        app:layout_constraintGuide_percent="0.7"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/line"/>

</android.support.constraint.ConstraintLayout>

大致的使用就是這些了,只用這個控件,很多佈局都變得方便了,嵌套層級減少不是一點兩點,視圖加載效率也會高很多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章