ConstraintLayout 學習總結

Gxinyu發表於2019-03-04

ConstraintLayout 的簡介

1.ConstraintLayout,中文稱約束佈局,在2016年Google I/O大會時提出,2017年2月釋出正式版,目前穩定版本為1.0.2。約束佈局作為Google今後主推的佈局樣式,可以完全替代其他佈局,降低頁面佈局層級,提升頁面渲染效能。

2.ConstraintLayout的優劣 優點:當佈局出現多層巢狀的時候,使用ConstraintLayout可以減少佈局巢狀,平時我們基本都是用LinearLayout和RelativeLayout,一層LinearLayout巢狀會導致onMeasure測量兩次,而RelativeLayout是四次,雖然我們感覺不到,為了更好就要專案效能和提升自我,有必要去學習一下。另外一點ConstraintLayout也向下相容到API 9,所以你再也沒有理由不用了。 缺點:當佈局沒巢狀的時候,ConstraintLayout要想實現一些效果需要設定太多的屬性,相對於個人來說比較繁瑣。

3.如果使用以及要求 第一步:在project的build.gradle設定谷歌的遠端倉庫

repositories {
    maven {
        url 'https://maven.google.com'
    }
}
複製程式碼

第二步:在要使用ConstraintLayout的module的build.gradle檔案中引入約束佈局庫

dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
}
複製程式碼

ConstraintLayout 屬性介紹

####相對位置屬性

layout_constraintTop_toTopOf — 期望檢視的上邊對齊另一個檢視的上邊。
layout_constraintTop_toBottomOf — 期望檢視的上邊對齊另一個檢視的底邊。
layout_constraintTop_toLeftOf — 期望檢視的上邊對齊另一個檢視的左邊。
layout_constraintTop_toRightOf — 期望檢視的上邊對齊另一個檢視的右邊。
layout_constraintBottom_toTopOf — 期望檢視的下邊對齊另一個檢視的上邊。
layout_constraintBottom_toBottomOf — 期望檢視的底邊對齊另一個檢視的底邊。
layout_constraintBottom_toLeftOf — 期望檢視的底邊對齊另一個檢視的左邊。
layout_constraintBottom_toRightOf — 期望檢視的底邊對齊另一個檢視的右邊。
layout_constraintLeft_toTopOf — 期望檢視的左邊對齊另一個檢視的上邊。
layout_constraintLeft_toBottomOf — 期望檢視的左邊對齊另一個檢視的底邊。
layout_constraintLeft_toLeftOf — 期望檢視的左邊對齊另一個檢視的左邊。
layout_constraintLeft_toRightOf — 期望檢視的左邊對齊另一個檢視的右邊。
layout_constraintRight_toTopOf — 期望檢視的右邊對齊另一個檢視的上邊。
layout_constraintRight_toBottomOf — 期望檢視的右邊對齊另一個檢視的底邊。
layout_constraintRight_toLeftOf — 期望檢視的右邊對齊另一個檢視的左邊。
layout_constraintRight_toRightOf — 期望檢視的右邊對齊另一個檢視的右邊。
複製程式碼

以上屬性都是設定當前控制元件相對控制元件的位置關係,以layout_constraintLeft_toLeftOf=@id/btn_A為例子,其中layout_部分是固定格式,分為兩部分,第一部分constraintLeft是表示當前控制元件的左邊界,toLeftOf代表就是當前控制元件在btn_A的左邊,app:layout_constraintBaseline_toBaselineOf="@id/btn_A" 這個比較特殊,這個相當於當前的控制元件的水平中心線與btn_A的水平中心線為準,下面是例子

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_relative_position"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="zr.com.constraintdemo.normal.RelativePositionActivity">

    <Button
        android:id="@+id/btn_A"
        android:text="A"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        />

    <Button
        android:text="在A下方,與A左對齊"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/btn_A"
        app:layout_constraintLeft_toLeftOf="@id/btn_A"
        android:layout_marginTop="32dp"
        />

    <Button
        android:text="在A上方,與A居中對齊"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@id/btn_A"
        app:layout_constraintLeft_toLeftOf="@id/btn_A"
        app:layout_constraintRight_toRightOf="@id/btn_A"
        android:layout_marginBottom="32dp"
        />

    <Button
        android:text="baseline對齊"
        android:layout_width="wrap_content"
        android:layout_height="80dp"
        app:layout_constraintBaseline_toBaselineOf="@id/btn_A"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginLeft="8dp"
        android:gravity="bottom"
        />

    <Button
        android:text="水平居中對齊"
        android:layout_width="wrap_content"
        android:layout_height="80dp"
        android:gravity="bottom"
        app:layout_constraintTop_toTopOf="@id/btn_A"
        app:layout_constraintBottom_toBottomOf="@id/btn_A"
        app:layout_constraintLeft_toRightOf="@id/btn_A"
        android:layout_marginLeft="16dp"
        />

</android.support.constraint.ConstraintLayout>
複製程式碼

相對位置的總結:

  • 一個控制元件一般只需要相對於一個控制元件設定相對位置,與Java的單繼承相似,除了連結串列結構,後面會提到
  • 設定app:layout_constraintBaseline_toBaselineOf="@id/btn_A"屬性之後 再設定上下位置的約束無效,設定左右的約束屬性還是有效的

####偏移屬性(BIAS)

在設定控制元件的居中屬性之後,通過偏移屬性可以設定讓控制元件更偏向於依賴控制元件的某一方,偏移設定為0~1之間的值。相應屬性:

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

舉個例子:

<Button
        android:text="水平偏移30%"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_bias="0.3"
        />
複製程式碼

QQ圖片20171201144606.png
這個例子是最上面哪個水平偏移30%的 關於偏移屬性的總結:

  • 設定偏移之前一定要設定一個相對位置,如果沒有相對位置,偏移時無效的
  • 設定的相對位置一定要是 ,app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"
  • 水平方向的一定要先設定app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" 垂直方向的一定要設定app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"
  • 偏移值預設從0-1的數字,當然也可以設定負數和大於1的數字,但是效果就是顯示在螢幕外
  • 水平偏移量是螢幕寬度的偏移倍數,垂直偏移時螢幕高度的偏移倍數

####可見性屬性(VISIBILITY)

#####(一) 當控制元件設定GONE的時候,雖然控制元件GONE了,但是控制元件之間的約束條件還在 例子如下:

<?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:id="@+id/activity_bias"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:visibility="gone"
        android:id="@+id/btn_A"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_marginLeft="16dp"
        android:gravity="center"
        android:text="與A水平居中對齊"
        app:layout_constraintBottom_toBottomOf="@id/btn_A"
        app:layout_constraintLeft_toRightOf="@id/btn_A"
        app:layout_constraintTop_toTopOf="@id/btn_A" />

</android.support.constraint.ConstraintLayout>
複製程式碼

效果

QQ圖片20171201152727.png
#####(二) 控制元件設定為GONE的時候,也可以設定GONE之後的app:layout_goneMarginLeft="100dp" ,有些時候想實現特殊效果可以使用

####尺寸約束

  • minWidth :最小寬度,即使沒有內容時也要佔有minWidth 的寬度,前提是寬度屬性android:layout_width="wrap_content" ,當內容的時候寬度大於minWidth 時,控制元件寬度自動擴充
  • 填充模式:類似LinearLayout 的android:layout_weight="1"屬性,區別就是,如果想水平方向最大填充,設定android:layout_width="0dp",一定要設定,否則無效,同理垂直方向。
  • constrainedWidth:constrainedWidth有兩個值,當為true的時候就開啟了控制元件的最小寬度或者高度,否則相反,配合layout_constraintWidth_min屬性設定最小寬度,需要注意的是,當屬性中存在minWidth 時,這個屬性是失效的,以minWidth 為準
  • 百分比佈局:首先必須要設定app:layout_constraintWidth_default="percent" app:layout_constraintHeight_default="percent"這兩個屬性 ,還有android:layout_width="0dp" android:layout_height="0dp"屬性,否則之後設定的都是無效的, app:layout_constraintWidth_percent="0.5" app:layout_constraintHeight_percent="0.3"設定這兩個就可以實現相對於螢幕的百分比寬高了

####控制元件寬高比(RATIO)

這個對於我們開發者簡直就是福利,如果是動態設定寬高相對麻煩。 layout_constraintDimentionRatio屬性代表設定控制元件寬高的比例,前提是控制元件的寬高必須有一個設定為0dp,否則不去作用

  • 第一種方式:直接設定一個float值,表示寬高比,記住是寬高比,寬高比!!!
app:layout_constraintDimensionRatio="2"
複製程式碼
  • 第二種方式:使用比值,例如 :
app:layout_constraintDimensionRatio="16:9"
或者
app:layout_constraintDimensionRatio="H,16:9"
複製程式碼

這種方式,如果沒有字首就代表是寬高比,如果加了字首H代表比值的第一個數字是高度,W是寬度

說明:這裡面坑很多,這裡就一一簡單的介紹一下,如果寬高都設定的屬性為0dp,首先螢幕高度肯定大於寬度,所以寬度上面一定處於填充狀態,這0-1之間有個值正好是螢幕寬高比,這時候控制元件正好填充螢幕,如果想不想寬度被填充至少要保證寬高其中一個屬性的值不為0dp,只要有一個屬性不為0dp的時候,這時候比例的根據就是內容的填充寬高來定而不是螢幕。當寬高兩個值都不為0dp的時候,這時候比例屬性無效。並且這個比例會自動計算內容是否需要換行展示更好的比例,所以非常智慧。

####鏈式約束(CHAIN)

鏈這個概念是約束佈局新提出的,它提供了在一個維度(水平或者垂直),管理一組控制元件的方式。

1728184-af9bd09ddec63651.png
處於水平或者垂直方向第一個控制元件為chain head(鏈頭),當我們給chain head設定layout_constraintHorizontal_chainStyle或layout_constraintVertical_chainStyle,整條鏈的狀態將會發生改變。

  • CHAIN_SPREAD – 元素之間的空間將會均勻分佈,這是系統預設的排列方式

  • CHAIN_SPREAD – 首尾的兩條鏈將不會分配空間,其餘內部的鏈將均勻分配空間。

  • CHAIN_PACKED – 首尾兩條鏈將會分配空間,鏈內部將不會分配空間

  • Weight 通過設定的weight值來分配元素的寬或者高

1728184-3f72d2f2cc29e797.png
注意:

  • layout_constraintHorizontal_chainStyle屬性值是小寫的,文件裡給出的是大寫的。

  • weight樣式的實現有一個前提,chainStyle必須為預設的spread樣式

  • 設定Weight樣式時記得把元素中的寬或者設定成0dp

Guideline

首先說明一下,Guideline只能用於ConstraintLayout中,是一個工具類,不會被顯示,僅僅用於輔助佈局。 它可以是horizontal或者 vertical的。(例如:android:orientation="vertical")

  • vertical的Guideline寬度為零,高度為ConstraintLayout的高度
  • horizontal的Guideline高度為零,寬度為ConstraintLayout的高度

定位Guideline有三種方式:

  • 指定距離左側或頂部的固定距離(layout_constraintGuide_begin)
  • 指定距離右側或底部的固定距離(layout_constraintGuide_end)
  • 指定在父控制元件中的寬度或高度的百分比(layout_constraintGuide_percent)

Guideline 的原始碼如下:

public class Guideline extends View {
    public Guideline(Context context) {
        super(context);
        super.setVisibility(8);
    }
    public Guideline(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setVisibility(8);
    }
    public Guideline(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        super.setVisibility(8);
    }
    public Guideline(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr);
        super.setVisibility(8);
    }
    public void setVisibility(int visibility) {
    }
    public void draw(Canvas canvas) {
    }
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        this.setMeasuredDimension(0, 0);
    }
}
複製程式碼

原始碼說明:

  • 它預設是GONE的。8 就是View.GONE的值。
  • 它的public void setVisibility(int visibility)方法被空實現了,所以使用者也沒辦法改變它的可見度。
  • 推匯出它一定是GONE的。在螢幕上不可見
  • this.setMeasuredDimension(0, 0); 和public void draw(Canvas canvas)的空實現,表明這是一個超輕量的View,不可見,沒有寬高,也不繪製任何東西。僅僅作為我們的錨點使用。

##總結 ConstraintLayout佈局剛開始學習的時候確實非常麻煩,但是所有的新鮮事物都是入手難,光理論肯定是不行的,總之自己動手豐衣足食。我自己也做個了demo傳送門,可以方便參考,謝謝大家提供意見!