1. 程式人生 > >constrainlayout用法總結(一)

constrainlayout用法總結(一)

       constrainlayout字面之意約束佈局,是google推出的用於最大化的解決佈局巢狀問題,同時減少佈局渲染時間,提升效能的佈局。與相對佈局Relativelayout有些類似,約束佈局的原理與相對佈局是一樣的,都是根據檢視與檢視之間的相互依賴,相對父級佈局的位置來進行佈局的。但是比Relativelayout更加的靈活,功能更加強大。

        該筆記按照幾個步驟來進行展開描述,闡釋。

  • 開始

     在專案當中的gradle當中新增constraint-layout依賴:

implementation 'com.android.support.constraint:constraint-layout:1.1.2'
  • 新建佈局
<?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">


</android.support.constraint.ConstraintLayout>
  • 新增約束

       新增約束的方式常見兩種方式:

       手動進行拖拽:將控制元件拖進佈局空間,然後通過constraint-layout為控制元件提供的課件的操作部件進行約束的新增。這種方式簡便快捷,效率較高,一句話android終於有了一種可以抗衡ios的操作,但是給我的感覺說真的還是差那麼一點,體驗過ios操作的朋友們能夠明白我的意思。當然筆者並不建議以拖拽的方式新增約束。筆者的習慣絕對是會去手敲去新增約束的。

      手敲xml:可能有朋友會說,放著簡單便捷的操作不用而去手敲,不是自己找麻煩嗎?是的你說的很對。但是我就是願意去找這個麻煩的。首先我手寫的方式能夠讓我更深刻的去理解constraint-layout為我們提供的屬性的特性,並且根據組合屬性方式去觀察展示效果,這個過程是一個最簡單有效的去理解記憶屬性特點的好方法。有一部分朋友在使用拖拽方式完成佈局後基本上不會再去看xml中自動生成的屬性了,等到需求介面一旦發生改變的時候,如果不知道具體的屬性產生什麼效果,那麼你絕對會後悔的,其中苦痛自己體會。

首先,先說一下拖拽方式:

這裡寫圖片描述

可以看到,上下左右都有一個小圓圈,這個圓圈就是用來新增約束的。 
四個角的矩形,是用來擴大或縮小控制元件的。

這裡寫圖片描述:刪除選中控制元件的所有約束。 
這裡寫圖片描述:編輯基線,用於基線對齊(下面會說)。

現在我們來為這個 Button 新增約束:

這裡寫圖片描述

  • 屬性介紹      
  • layout_constraintRight_toLeftOf
  • layout_constraintRight_toRightOf
  • layout_constraintTop_toTopOf
  • layout_constraintTop_toBottomOf
  • layout_constraintBottom_toTopOf
  • layout_constraintBottom_toBottomOf
  • layout_constraintBaseline_toBaselineOf
  • layout_constraintDimensionRatio
  • layout_constraintHorizontal_weight
  • layout_constraintVertical_weight
  • layout_constraintHorizontal_bias
  • layout_constraintVertical_bias
  • layout_constraintHorizontal_chainStyle
  • layout_constraintVertical_chainStyle

      如上面列出來的屬性都是我們開發過程中經常需要用到的屬性,大家還可以注意到上面的屬性我分別用不同的幾種顏色標註了。那麼我們就來一一的解讀吧。

  首先看紅色的部分,這部分的屬性跟相對佈局(Relativelayout)比較類似,例如app:layout_constraintRight_toLeftOf="@+id/idview"意思為當前控制元件的右側在其他控制元件的左側,其他的此類屬性都是類似的意思。那麼最簡單的我們想佈局中拖入一個控制元件例如button:

 <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:text="button"/>

   大家肯定注意到了其中的四個屬性後面的值是“parent”,這樣的四個屬性同時作用下,控制元件button居於父級佈局的中央。大家可能有疑問,為什麼控制元件button不在父佈局的左上角。constraint-layout約束佈局,如果我們只新增左側或者頂部的屬性那麼效果會是怎麼樣呢? 

只新增左上約束:此時的button就出現了停留在左上角的位置,當然如果新增其他的屬性,大家可自行實驗。

此時我們在頂部在次新增一個button。

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

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

我們想實現一個button1在左側,然後button佔滿右側空間,如上程式碼所示卻並不能得到我們想要的結果,上述程式碼的效果是

而想要達到這樣的效果我們button2的寬度需要修改為0dp效果即為:

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

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

你會發現button2,好像瘋了一樣,我們設定的在button1右側,和與parent右側對齊完全失效了!!!

別怕,接下來就讓你認識到為什麼這個控制元件叫做constraint-layout

在當控制元件有自己設定的寬度,例如warp_content、固定值時,我們為控制元件新增的都是約束“Constraint”,這個約束有點像橡皮筋一樣會拉這個控制元件,但是並不會改變控制元件的尺寸(RL很明顯不是這樣的)。

例如上例,當button2的寬度較小時,我們為其左側設定了一個約束(button1右側),右側設定了一個約束(parent右側對其),當兩個約束同時生效的時候(你可以認為兩邊都是相同的一個拉力),button2會居中。

當btn02特別大的時候,依然是這兩個力,那麼會發生什麼?會造成左側和右側超出的距離一樣大。

那麼現在大家肯定有些疑問:

  • 怎麼樣才能和上面的RL一樣,寬度剛好佔據剩下的距離呢(btn01右側到螢幕右側的距離)?

這個問題,問得很好,我們剛才所有的嘗試都是在控制元件自身擁有特定的寬度情況下執行的;那麼如果希望控制元件的寬度根據由約束來控制元件,不妨去掉這個特定的寬度,即設定為0試試?

對!當我們將btn02的寬度設定為0時,一切又變得很完美

大家可能疑惑為什麼match_parent不是將控制元件填滿剩下的水平空間呢,之前我也想不明白,但是我查閱了官方資料,如下

那麼這裡,你可能會問0值是什麼含義,其實在ConstraintLayout中0代表:MATCH_CONSTRAINT,看到這個常量,是不是瞬間覺得好理解了一點。

  • 最後一個問題,MATCH_PARENT哪去了?

看官網的解釋:

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中已經不支援MATCH_PARENT這個值了,你可以通過MATCH_CONSTRAINT配合約束實現類似的效果。當然在我們在將button2的寬度設為wrap_content時候,button2卻位於右側的中央部分,產生這種情況的原因是因為在constraint-layout中每個控制元件的屬性設定當中預設都包含兩個屬性:

  • layout_constraintHorizontal_bias
  • layout_constraintVertical_bias

他們表示的意思就是控制元件在設定了水平或者垂直方向上的約束後,表示在水平或者垂直方向的拉力的大小,預設不新增這兩個屬性的話,他們的預設值是兩個方向都是50%,即0.5。如果兩側拉力不是0.5,那麼就不是位於中間的位置,請大家自行實踐。

好了,到這裡,目前我們已經看到其已經和RelativeLayout勢均力敵了,接下來我們看一下RL做不到的特性

 現在的要求是我們要做一個banner,寬高比為16:9,如果在之前的話我們只能在程式碼中動態設定寬高比,可是呢constraint-layout在我們xml裡面設定一個屬性就可以實現了。

layout_constraintDimensionRatio

沒錯就是這個屬性,使用的時候

 <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/button1"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintDimensionRatio="16:6"
        android:text="button"/>

那麼這樣啟動的作用就是寬高比為16:6當然控制元件的寬高都是 MATCH_CONSTRAINT即0dp。寫法可以是app:layout_constraintDimensionRatio="H,16:6"或者是app:layout_constraintDimensionRatio="W,16:6",這就是寬高方向上的比例。效果就是

下面我們再來看如果我要做一個tab,怎麼做呢?

<TextView
        android:id="@+id/tab1"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:background="#f67"
        android:gravity="center"
        android:text="Tab1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/tab2" />


    <TextView
        android:id="@+id/tab2"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:background="#A67"
        android:gravity="center"
        android:text="Tab2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@id/tab1"
        app:layout_constraintRight_toLeftOf="@+id/tab3" />


    <TextView
        android:id="@+id/tab3"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:background="#767"
        android:gravity="center"
        android:text="Tab3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@id/tab2"
        app:layout_constraintRight_toRightOf="parent" />

看下效果:

 

我們設定了tab的左右兩兩依賴,並且都是設定了寬度0dp,我們實現了等分的效果,使用text冒充tab成功了。

當然大家都知道我們通過linearlayout配合weight也可以實現這樣的效果。是的constraint-layout同樣支援這樣的效果。這個屬性就是app:layout_constraintHorizontal_weight,app:layout_constraintVertical_weight見名知意,假設我把他們設定為2,1,1那麼效果就是:

大家都看到了到這裡constraint-layout是可以達到了relativelayout和linearlayout同樣的效果,可以使用constraint-layout替代他們。當然還有一些特性是相對佈局與線性佈局不可達到的特性。

copy一下官方文件:

Chain heads

Chains are controlled by attributes set on the first element of the chain (the "head" of the chain):

 
Fig. 10 - Chain Head

The head is the left-most widget for horizontal chains, and the top-most widget for vertical chains.

Chain Style

When setting the attribute layout_constraintHorizontal_chainStyle or layout_constraintVertical_chainStyle on the first element of a chain, the behavior of the chain will change according to the specified style (default is CHAIN_SPREAD).

  • CHAIN_SPREAD -- the elements will be spread out (default style)
  • Weighted chain -- in CHAIN_SPREAD mode, if some widgets are set to MATCH_CONSTRAINT, they will split the available space
  • CHAIN_SPREAD_INSIDE -- similar, but the endpoints of the chain will not be spread out
  • CHAIN_PACKED -- the elements of the chain will be packed together. The horizontal or vertical bias attribute of the child will then affect the positioning of the packed elements

 
Fig. 11 - Chains Styles

橫向的相當於組成了一個鏈chains,同時配合上weight可以實現上圖所示的效果。

chains styles:

  • layout_constraintHorizontal_chainStyle
  • layout_constraintVertical_chainStyle

前提是需要配合app:layout_constraintHorizontal_weight

1.spread + 寬度0dp   效果既是等分的效果並且佔滿各自的空間

2.spread + 寬度非0  

3.spread_inside + 寬度非0 

4.packed + 寬度非0

以上的條件都是寬度非0的情況下實現的效果。

下面一個屬性為:layout_constraintBaseline_toBaselineOf

這個屬性是對其兩個控制元件文字部分,這個屬性就不在詳細的去說了。

下面還有一個叫做android.support.constraint.Guideline,這個類使用起來較為簡單。

所以其有個屬性為:

android:orientation取值為”vertical”和”horizontal”.

除此以外,還差個屬性,決定該輔助線的位置:

  • layout_constraintGuide_begin
  • layout_constraintGuide_end
  • layout_constraintGuide_percent

可以通過上面3個屬性其中之一來確定屬性值位置。

begin=30dp,即可認為距離頂部30dp的地方有個輔助線,根據orientation來決定是橫向還是縱向。

end=30dp,即為距離底部。 
percent=0.8即為距離頂部80%。

好了,下面看一個例子,剛才我們的浮點按鈕,我決定通過兩根輔助線來定位,一根橫向距離底部80%,一個縱向距離頂部80%,浮點按鈕就定位在他們交叉的地方。

<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.8" />


    <android.support.constraint.Guideline
        android:id="@+id/guideline_w"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.8" />

    <TextView
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:background="#612"
        app:layout_constraintLeft_toRightOf="@id/guideline_w"
        app:layout_constraintTop_toBottomOf="@id/guideline_h" />

看下效果圖吧:

到這裡constraint-layout的用法即屬性總結了差不多了,後續還有一些屬性的用法會陸續新增上來。