ConstraintLayout,看完一篇真的就夠了麼?

1. 前言

最近中毒很深,常常逛掘金,看到不少優秀的文章,感謝掘金。同時也看到不少標題,看看XXXX,一篇就夠了。技術一直在不停的更新迭代,看一篇永遠是不夠的,建議再看一遍官網的,能夠看到被做者過濾掉的信息或者最新的更新。這就是我爲何會在文末放官網連接的緣由,若是有的話。android

2. ConstraintLayout

ConstraintLayout做爲一款能夠靈活調整view位置和大小的Viewgroup被Google瘋狂推薦,之前建立佈局,默認根元素都是LinearLayout,如今是ConstraintLayout了。ConstraintLayout可以以支持庫的形式最小支持到API 9,同時也在不斷的豐富ConstraintLayout的API和功能。ConstraintLayout在複雜佈局中可以有效的,下降佈局的層級,提升性能,使用更加靈活。git

在app組件的Graldle默認都有以下依賴:github

//可能版本不同哦
implementation 'com.android.support.constraint:constraint-layout:1.1.3 複製代碼

火燒眉毛想了解ConstraintLayout能在佈局作點什麼了。bash

2.1 相對定位

相對定位,其實這跟RelativeLayout差很少,一個View相對另一個View的位置。app

相對佈局
經過簡單的使用ConstraintLayout的屬性也就能夠實現以上佈局。World對於Hello的右邊,GitCode對位於Hello的下邊

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    ...>
    <TextView
            ...
            android:text="Hello"
            android:id="@+id/tvHello"/>

    <TextView
            ...
            android:text="World"
            app:layout_constraintLeft_toRightOf="@+id/tvHello"/>

    <TextView
            ...
            android:text="GitCode"
            app:layout_constraintTop_toBottomOf="@id/tvHello"/>
</android.support.constraint.ConstraintLayout>
複製代碼

以TextView World相對位置屬性layout_constraintLeft_toRightOf來講,constraintLeft表示TextView World自己的左邊,一個View有四條邊,所以TextView的上、右、下邊分別對應着constraintTop、constraintRight、constraintBottom。toRightOf則表示位於另一個View的右邊,例如此處位於Hello的右邊,所以對應還有toLeftOf、toRghtOf、toBottomOf,分別位於View Hello的左、右、下邊。總結的說,constraintXXX表示View自身約束的邊,toXXXOf表示另外一個View的邊,而XXX的值能夠是Left、Top、Right、Bottom,分別對應左,上、右、下邊。layout_constraintStart_toEndOf也是相似的道理。ide

另外須要注意的是,view的位置能夠相對於同層的view和parent,在相對於parent的時候toLeftOf、toTopOf、toRghtOf、toBottomOf分別表示位於parent的內部左上右下邊緣。如圖:紅色框表示parent view。工具

再來看看一個特殊的場景:

此時想要Hello和World文本中間對齊怎麼辦?ConstraintLayout提供了 layout_constraintBaseline_toBaselineOf屬性。

<TextView
   ...
    android:text="Hello"
    android:id="@+id/tvHello"/>

<TextView
    ...
    android:text="World"
    app:layout_constraintBaseline_toBaselineOf="@id/tvHello"
    app:layout_constraintLeft_toRightOf="@+id/tvHello"/>
複製代碼

此時界面就如願了,比Relativelayout方便多了。 佈局

什麼是baseline?貼張官網的圖。

2.2 邊距

邊距與日常使用並沒有太大區別,但須要先肯定view的位置,邊距纔會生效。如:post

<TextView
        ...
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"/>
複製代碼

在其餘的ViewGroup,TextView的layout_marginToplayout_marginLeft屬性是會生效的,但在ConstraintLayout不會生效,由於此時TextView的位置還沒肯定。下面的代碼纔會生效。性能

<TextView
        ...
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
         />
複製代碼

經常使用屬性以下:

  • android:layout_marginStart
  • android:layout_marginEnd
  • android:layout_marginLeft
  • android:layout_marginTop
  • android:layout_marginRight
  • android:layout_marginBottom

GONE Margin

有時候,會有這種需求,在World可見的時候,GitCode與World的左邊距是0,當World不見時,GitCode的左邊距是某個特定的值。

World可見的效果,GitCode的左邊距爲0

World不可見的效果,GitCode的左邊距爲10
爲此,ConstraintLayout提供了特殊的goneMargin屬性,在目標View隱藏時,屬性生效。有以下屬性:

  • layout_goneMarginStart
  • layout_goneMarginEnd
  • layout_goneMarginLeft
  • layout_goneMarginTop
  • layout_goneMarginRight
  • layout_goneMarginBottom

Centering positioning and bias

在RelativeLayout居中,一般是使用如下三個屬性:

  • layout_centerInParent 中間居中
  • layout_centerHorizontal 水平居中
  • layout_centerVertical 垂直居中

而在ConstraintLayout居中則採用左右上下邊來約束居中。

  • 水平居中 layout_constraintLeft_toLeftOf & layout_constraintRight_toRightOf
  • 垂直居中 layout_constraintTop_toTopOf & layout_constraintBottom_toBottomOf
  • 中間居中 水平居中 & 垂直居中 舉個栗子:
<TextView
    ...
    android:text="Hello"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"/>
複製代碼

效果圖:

那,要是想把Hello往左挪一點,怎麼辦?

那很簡單,使用margin呀。不不不,這裏要介紹的是另外兩個屬性,與LinearLayout的權重相似(固然,ConstraintLayout也可使用權重屬性),但簡單不少。

  • layout_constraintHorizontal_bias 水平偏移
  • layout_constraintVertical_bias 垂直偏移

兩個屬性的取值範圍在0-1。在水平偏移中,0表示最左,1表示最右;在垂直偏移,0表示最上,1表示最下;0.5表示中間。

<TextView
    ...
    android:text="Hello"
    app:layout_constraintHorizontal_bias="0.8"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"/>
複製代碼

效果:

2.3 圓形定位(Added in 1.1)

圓形定位指的是View的中心點相對於另外View中心點的位置。貼張官網圖。

涉及三個屬性:

  • layout_constraintCircle : 另一個view的id,上圖的A view
  • layout_constraintCircleRadius : 半徑,上圖的radius
  • layout_constraintCircleAngle : 角度,上圖angle,範圍爲0-360 根據上面上個屬性就能夠肯定B View的位置。從圖也能夠知道,角度以時間12點爲0,順時針方式。

吃個栗子:

<TextView
    ...
    android:text="Hello"
    android:id="@+id/tvHello"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"/>

<TextView
    android:text="World"
    app:layout_constraintCircle="@id/tvHello"
    app:layout_constraintCircleRadius="180dp"
    app:layout_constraintCircleAngle="135"/>
複製代碼

效果圖:Hello中間居中,World 135角度

2.4 尺寸約束

ConstraintLayout 最大最小尺寸

ConstraintLayout的寬高設爲WRAP_CONTENT時,能夠經過如下熟悉設置其最大最小尺寸。

  • android:minWidth 最小寬度
  • android:minHeight 最小高度
  • android:maxWidth 最大寬度
  • android:maxHeight 最大高度

ConstraintLayout中的控件尺寸約束

在ConstraintLayout中控件能夠三種方式來設置其尺寸約束。

  • 指定具體的值。如123dp
  • 使用值WRAP_CONTENT,內容自適配。
  • 設爲0dp,即MATCH_CONSTRAINT,擴充可用空間。

第一二種跟日常使用沒什麼區別。第三種會根據約束狀況從新計算控件的大小。 在ConstraintLayout中,不推薦使用MATCH_PARENT,而是推薦使用MATCH_CONSTRAINT(0dp),它們的行爲是相似的。

吃個栗子吧:

<TextView
        android:text="Hello"
        android:id="@+id/tvHello"
        android:gravity="center"
        android:padding="20dp"
        app:layout_constraintTop_toTopOf="parent"
        android:textColor="@color/colorWhite"
        android:background="@color/colorPrimary"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_width="0dp"
        android:layout_marginRight="20dp"
        android:layout_height="wrap_content"/>
複製代碼

設置layout_width爲0dp;layout_heightwrap_content;layout_marginRight爲20dp,與parent左右對齊。

效果圖:

在1.1以前的版本,控件尺寸設爲WRAP_CONTENT,控件默認是由組件文本大小控制,其餘約束是不生效的。能夠經過如下屬性設置是否生效。

  • app:layout_constrainedWidth=」true|false」
  • app:layout_constrainedHeight=」true|false」

控件設爲MATCH_CONSTRAINT時,控件的大小會擴展全部可用空間,在1.1版本後,能夠經過如下屬性改變控件的行爲。

  • layout_constraintWidth_min 最小寬度
  • layout_constraintHeight_min 最小高度
  • layout_constraintWidth_max 最大寬度
  • layout_constraintHeight_max 最大高度
  • layout_constraintWidth_percent 寬度佔parent的百分比
  • layout_constraintHeight_percent 高度佔parent的百分比

吃個栗子:

<TextView
        android:text="Hello"
        android:id="@+id/tvHello"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintWidth_percent="0.5"
        app:layout_constraintWidth_default="percent"
        android:layout_width="0dp"
        android:layout_height="wrap_content"/>
複製代碼

android:layout_width設爲MATCH_CONSTRAINT,即0dp;將app:layout_constraintWidth_default設爲percent;將app:layout_constraintWidth_percent設爲0.5,表示佔parent的50%,取值範圍是0-1。

效果圖:

比例約束

控件的寬高比,要求是寬或高至少一個設爲0dp,而後設置屬性layout_constraintDimensionRatio便可。

<TextView
    android:text="Hello"
    app:layout_constraintDimensionRatio="3:1"
    android:layout_width="0dp"
    android:layout_height="100dp"
    />
複製代碼

這裏設置寬高比爲3:1,高度爲100dp,那麼寬度將爲300dp。

也能夠在比例前加 W, H表示是寬高比仍是高寬比。以下面表示高寬比。

<Button android:layout_width="0dp" android:layout_height="0dp"
    app:layout_constraintDimensionRatio="H,16:9"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent"/>
複製代碼

2.5 鏈

鏈在水平或者垂直方向提供一組相似行爲。如圖所示能夠理解爲橫向鏈。這裏須要瞭解一點,A與parent的左邊緣約束,B與parent的右邊邊緣約束,A右邊和B左邊之間相互約束,才能使用一條鏈。多個元素之間也是如此,最左最右與parent約束,元素之間邊相互約束。否則下面的鏈式永遠沒法生效。

橫向鏈最左邊第一個控件,垂直鏈最頂邊第一個控件稱爲鏈頭,能夠經過下面兩個屬性鏈頭統必定製鏈的樣式。

  • layout_constraintHorizontal_chainStyle 水平方向鏈式
  • layout_constraintVertical_chainStyle 垂直方向鏈式

它兩的值默承認以是

  • CHAIN_SPREAD 展開樣式(默認)
  • Weighted chain 在CHAIN_SPREAD樣式,部分控件設置了MATCH_CONSTRAINT,那他們將擴展可用空間。
  • CHAIN_SPREAD_INSIDE 展開樣式,但兩端不展開
  • CHAIN_PACKED 抱團(打包)樣式,控件抱團一塊兒。經過偏移bias,能夠改變packed元素的位置。
    從實際開發,這麼應用仍是挺普遍的。 提供份代碼參考,避免走冤枉路:
<?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:text="Hello"
            android:id="@+id/tvHello"
            android:gravity="center"
            android:padding="20dp"
            app:layout_constraintHorizontal_chainStyle="spread"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@id/tvWorld"
            android:textColor="@color/colorWhite"
            android:background="@color/colorPrimaryDark"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    <TextView
            android:text="World"
            android:gravity="center"
            android:padding="20dp"
            android:id="@+id/tvWorld"
            app:layout_constraintLeft_toRightOf="@id/tvHello"
            app:layout_constraintRight_toRightOf="parent"
            android:textColor="@color/colorWhite"
            android:background="@color/colorPrimary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    
</android.support.constraint.ConstraintLayout>
複製代碼

效果:

在鏈中,剩餘空餘空間默認平均給各元素,但有時能夠經過權重屬性layout_constraintVertical_weight來指定分配空間的大小。

1.1以後,在鏈中使用邊距時,邊距是相加的,也就說,假設Hello的右邊距爲5,World的左邊距爲20,那麼它們之間的邊距就是25。在鏈式,邊距先從剩餘空間減去的,而後再用剩餘的空間在元素之間進行定位。

2.6 優化器

在1.1以後,公開了優化器,經過在app:layout_optimizationLevel來決定控件在哪方面進行優化。

  • none : 不進行優化
  • standard : 默認方式, 僅僅優化direct和barrier約束
  • direct : 優化direct約束
  • barrier : 優化barrier約束
  • chain : 優化鏈約束 (實驗性質)
  • dimensions : 優化尺寸 (實驗性質), 減小測量次數

3.工具類

3.1 Guideline(參考線)

參考線實際上不會在界面進行顯示,只是方便在ConstraintLayout佈局view時候作一個參考。

經過設置Guideline的屬性orientation來表示是水平方向仍是垂直方向的參考線,對應值爲verticalhorizontal。能夠經過三種方式來定位Guideline位置。

  • layout_constraintGuide_begin 從左邊或頂部指定具體的距離
  • layout_constraintGuide_end 從右邊或底部指定具體的距離
  • layout_constraintGuide_percent 從寬度或高度的百分比來指定具體距離

丟個栗子:

<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">
    
    <android.support.constraint.Guideline
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/guideline"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="10dp"/>

    <Button android:text="Button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/button"
            app:layout_constraintLeft_toLeftOf="@+id/guideline"
            android:layout_marginTop="16dp"
            app:layout_constraintTop_toTopOf="parent"/>

    <Button android:text="Button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/button2"
            app:layout_constraintLeft_toLeftOf="@+id/guideline"
            android:layout_marginTop="16dp"
            app:layout_constraintTop_toBottomOf="@id/button"/>
</android.support.constraint.ConstraintLayout>
複製代碼

Guideline設置爲垂直參考線,距離開始的位置爲10dp。以下圖所示,實際中須要把鼠標移到button纔會顯示出來哦。

3.2 Barrier(柵欄)

Barrier有點相似Guideline,但Barrier會根據全部引用的控件尺寸的變化從新定位。例如經典的登陸界面,右邊的EditText老是但願與左右全部TextView的最長邊緣靠齊。 若是兩個TextView其中一個變得更長,EditText的位置都會跟這變化,這比使用RelativeLayout靈活不少。

代碼:

<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.Barrier
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="right"
            android:id="@+id/barrier"
            app:constraint_referenced_ids="tvPhone,tvPassword"
            />

    <TextView android:layout_width="wrap_content"
              android:text="手機號碼"
              android:id="@+id/tvPhone"
              android:gravity="center_vertical|left"
              android:padding="10dp"
              android:layout_height="50dp"/>

    <TextView android:layout_width="wrap_content"
              android:text="密碼"
              android:padding="10dp"
              android:gravity="center_vertical|left"
              android:id="@+id/tvPassword"
              app:layout_constraintTop_toBottomOf="@id/tvPhone"
              android:layout_height="wrap_content"/>

    <EditText android:layout_width="wrap_content"
              android:hint="輸入手機號碼"
              android:id="@+id/etPassword"
              app:layout_constraintLeft_toLeftOf="@id/barrier"
              android:layout_height="wrap_content"/>

    <EditText android:layout_width="wrap_content"
              android:hint="輸入密碼"
              app:layout_constraintTop_toBottomOf="@id/etPassword"
              app:layout_constraintLeft_toLeftOf="@id/barrier"
              android:layout_height="wrap_content"/>


</android.support.constraint.ConstraintLayout>
複製代碼

app:barrierDirection所引用控件對齊的位置,可設置的值有:bottom、end、left、right、start、top.constraint_referenced_ids爲所引用的控件,例如這裏的tvPhone,tvPasswrod。

3.3 Group(組)

用來控制一組view的可見性,若是view被多個Group控制,則以最後的Group定義的可見性爲主。

吃個香噴噴栗子吧: Group默承認見時,是這樣的。

設置Group的 visible屬性爲gone.

<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:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/group"
            android:visibility="gone"
            app:constraint_referenced_ids="tvPhone,tvPassword"
            />

    <TextView android:layout_width="wrap_content"
              android:text="手機號碼"
              android:id="@+id/tvPhone"
              android:gravity="center_vertical|left"
              android:padding="10dp"
              android:layout_height="50dp"/>

    <TextView android:layout_width="wrap_content"
              android:text="密碼"
              android:padding="10dp"
              android:gravity="center_vertical|left"
              android:id="@+id/tvPassword"
              app:layout_constraintLeft_toRightOf="@id/tvPhone"
              app:layout_constraintTop_toBottomOf="@id/tvPhone"
              android:layout_height="wrap_content"/>

    <TextView android:layout_width="wrap_content"
              android:text="GitCode"
              android:padding="10dp"
              android:gravity="center_vertical|left"
              app:layout_constraintLeft_toRightOf="@id/tvPassword"
              android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>
複製代碼

效果就變成了這樣了,tvPhone,tvPassword都被隱藏了。

3.4 Placeholder(佔位符)

一個view佔位的佔位符,當指定Placeholder的content屬性爲另外一個view的id時,該view會移動到Placeholder的位置。

代碼中,將TextView的定位在屏幕中間,隨着將id設置給Placeholder的屬性後,TextView的位置就跑到Placeholder所在的地方,效果圖跟上圖一直。

<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.Placeholder
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:content="@id/tvGitCode"
              />
    
    <TextView android:layout_width="wrap_content"
              android:text="GitCode"
              android:id="@+id/tvGitCode"
              android:padding="10dp"
              app:layout_constraintLeft_toLeftOf="parent"
              app:layout_constraintRight_toRightOf="parent"
              android:gravity="center_vertical|left"
              android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>
複製代碼

3.5 其餘

在2.0,爲ConstraintLayout增長了ConstraintProperties、ConstraintsChangedListener等,感興趣能夠本身看看官網。

更多信息請參考官網

4.總結

在寫本文以前,其實還不會用ConstraintLayout,寫完本文以後,已經上手和喜歡上了,知足本身在實際開發中想要的效果,可以有效的減小布局的層級,從而提升性能。不知道看完本文,你會使用ConstraintLayout了沒有?

Welcom to visit my github

好文推薦: Jetpack:在數據變化時如何優雅更新Views數據

相關文章
相關標籤/搜索