Android ConstraintLayout 最新使用小結

leon2017發表於2018-07-04

前言

ConstraintLayout是谷歌2016年I/O大會發布的新型的layout佈局,當然到現在已經在很多的APP中大範圍使用了,關於好處除了官方文件各大部落格也都說的很詳細了,總之我覺得ConstraintLayout帶來的最大好處就是ConstraintLayout可以最大化的減少複雜佈局的層級巢狀,實現佈局的扁平化,提升頁面的渲染速度。

使用

新增依賴


    implementation 'com.android.support.constraint:constraint-layout:1.1.2'

複製程式碼

Relative positioning 相對約束

image

<Button android:id="@+id/buttonA" ... />
         <Button android:id="@+id/buttonB" ...
                 app:layout_constraintLeft_toRightOf="@+id/buttonA" />
         
複製程式碼

主要屬性如下:

image

layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
複製程式碼

這個屬性相信大家一看就懂,他是以某個View佈局為約束來控制自己所在的位置的。

具體實現

image

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


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button1"
        app:layout_constraintStart_toStartOf="parent" //起始位置以父佈局為約束
        app:layout_constraintTop_toTopOf="parent"/> // 頂部以父佈局為約束

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button2"
        style="@style/Widget.AppCompat.Button.Colored"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button2"
        app:layout_constraintStart_toEndOf="@id/button1"  // 起始參照以button1 結束位置為約束
        app:layout_constraintTop_toTopOf="parent"/>  // 頂部以父佈局為約束

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button3"
        app:layout_constraintStart_toStartOf="parent"  //起始位置以父佈局為約束
        app:layout_constraintTop_toBottomOf="@id/button1"/> // 頂部以button1的底部為約束


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button4"
        style="@style/Widget.AppCompat.Button.Colored"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button4"
        app:layout_constraintStart_toEndOf="@id/button3" //起始位置以button3的結束位置為約束
        app:layout_constraintTop_toBottomOf="@id/button2"/> //頂部以button2的地步為約束

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

Margins 外邊距

image

外邊距不用多說,和一般的佈局使用方法一樣

具體屬性:

android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
複製程式碼

goneMargin 隱藏邊距

這個屬性很有意思的,使用方法大家一看就明白了,當前View與另一個View繫結後,另一個View的屬性設定為了Gone,則該屬性會生效

具體屬性:

layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom

複製程式碼

bias 權重,佔比

在說bias這個屬性之前我們先看下一下如何讓約束的目標居中(橫向、縱向、整體居中)

image

<android.support.constraint.ConstraintLayout ...>
             <Button android:id="@+id/button"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 app:layout_constraintLeft_toLeftOf="parent"
                 app:layout_constraintRight_toRightOf="parent/>
         </>
         
複製程式碼

這裡我們看到上面的是橫向居中的,因為他的寬度為wrap_content是,左右約束來自於父佈局ConstraintLayout,這樣他就能實現居中了。

具體實現

image

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

    //全域性居中
    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button1"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="button1"
        app:layout_constraintBottom_toBottomOf="parent" //底部以父佈局的底部為約束
        app:layout_constraintEnd_toEndOf="parent" //右側結束以父佈局的右側為約束
        app:layout_constraintStart_toStartOf="parent" //左側起始以父佈局的左側為約束
        app:layout_constraintTop_toTopOf="parent"/> //頂部以父佈局的頂部為約束


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button2"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="30dp"
        android:text="button2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


    // 以 button2 為約束的縱向居中
    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button3"
        app:layout_constraintBottom_toBottomOf="@id/button2" //底部以button2的底部為約束
        app:layout_constraintStart_toEndOf="@id/button2" 
        app:layout_constraintTop_toTopOf="@id/button2"/>  //頂部以button2的頂部為約束


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

居中實現了,也很好理解。那我們需要將約束目標定位到約束參照物的非中心位置的時候呢,這時候我們就可以用到bias了,其實居中佈局的bias預設是0.5的,他的取值範圍推薦0~1之間。bias有橫向和縱向的兩種展現方式,他們的延伸軌跡也是也是按照View佈局的預設方式來的 從左到右和從上到下。

layout_constraintHorizontal_bias
layout_constraintVertical_bias
複製程式碼

具體實現

image

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


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button"
        app:layout_constraintHorizontal_bias="0.3" //距離父佈局的左側30%權重
        app:layout_constraintVertical_bias="0.2"  //距離父佈局的頂部20%權重
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


</android.support.constraint.ConstraintLayout>

複製程式碼

Circular positioning (1.1版本新加的屬性)

這個還真想不好用什麼恰當的中文詞來稱呼它,我們先來看下官方的定義吧

You can constrain a widget center relative to another widget center, at an angle and a distance. This allows you to position a widget on a circle (see Fig. 6). The following attributes can be used:

複製程式碼

我是這樣理解的,約束目標B以A為約束,而約束規則是以A形成一個圓,並根據新增的圓半徑和圓弧來定位自己的位置。

具體實現

image

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


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.3"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.3"
        />


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2"
        app:layout_constraintCircle="@id/button1" // 以button1為約束
        app:layout_constraintCircleAngle="120" //圓弧以0時針開始順時針旋轉120°
        app:layout_constraintCircleRadius="100dp"/> //圓半徑為100dp


</android.support.constraint.ConstraintLayout>

複製程式碼

Ratio 比例

Ratio這個屬性呢,其實它的應用場景對於Andrid的螢幕適配很友好,它會讓View按照一定的比列來展示給我們。

比如我們的ImageView展示要求按照寬高3:4來展示,如果我們將寬高按照UI設計稿來寫死的話,不是說不行,但是在適配上就很尷尬。Ratio就可以幫助我們解決這個問題,使用Ratio必須要保證View的寬高有一方為0dp,這樣才能按照一定比例進行展示。

具體實現

image

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

    <ImageView
        android:layout_width="200dp"
        android:layout_height="0dp" //高度為0
        android:background="@mipmap/hhh"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="3:4" //寬高比3:4
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


</android.support.constraint.ConstraintLayout>

複製程式碼

當然,如果兩個尺寸都為0dp,我們為了保持比列的話,可以預先附加W或H,分別約束寬或高。

image

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

    <ImageView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@mipmap/hhh"
        app:layout_constraintDimensionRatio="H,3:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


</android.support.constraint.ConstraintLayout>

注:
app:layout_constraintDimensionRatio="H,3:1"和
app:layout_constraintDimensionRatio="W,1:3"這兩個是一樣的
複製程式碼

通過layout_constraintWidth_percent來調整寬度

image

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

    <ImageView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@mipmap/hhh"
        app:layout_constraintDimensionRatio="W,1:3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintWidth_percent="0.5" //寬度佔螢幕50%
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


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

通過layout_constraintHorizontal_bias來擺放位置

image

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

    <ImageView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@mipmap/hhh"
        app:layout_constraintDimensionRatio="W,1:3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintWidth_percent="0.5"
        app:layout_constraintHorizontal_bias="0.2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


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

Chains

image

chains這個給我的感覺像LinearLayout裡面的weight屬性,但是比他更為靈活;有點類似css裡面的FlexBox彈性盒子佈局,Chains必須有兩個View組成,這兩個View相互約束,Chains中的第一個控制元件叫做chain head,我們稱之為鏈頭吧,Chain Style的樣式主要由鏈頭來控制

Chain Style 屬性樣式

image

具體實現

image

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


    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:background="#82d959"
        android:gravity="center"
        android:text="chain1"
        app:layout_constraintHorizontal_chainStyle="spread" //鏈頭設定樣式
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/tv2"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:background="#b536d8"
        android:gravity="center"
        android:text="chain2"
        app:layout_constraintLeft_toRightOf="@+id/tv1"
        app:layout_constraintRight_toLeftOf="@+id/tv3"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv3"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:background="#de8f6a"
        android:gravity="center"
        android:text="chain3"
        app:layout_constraintLeft_toRightOf="@+id/tv2"
        app:layout_constraintRight_toLeftOf="@+id/tv4"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv4"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:background="#07d2e4"
        android:gravity="center"
        android:text="chain4"
        app:layout_constraintLeft_toRightOf="@+id/tv3"
        app:layout_constraintRight_toRightOf="parent"/>


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

通過layout_constraintHorizontal_weight來設定權重

image

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


    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv1"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:background="#82d959"
        android:gravity="center"
        android:text="chain1"
        app:layout_constraintHorizontal_weight="1" //設定權重
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/tv2"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv2"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:background="#b536d8"
        android:gravity="center"
        android:text="chain2"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toRightOf="@+id/tv1"
        app:layout_constraintRight_toLeftOf="@+id/tv3"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv3"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:background="#de8f6a"
        android:gravity="center"
        android:text="chain3"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toRightOf="@+id/tv2"
        app:layout_constraintRight_toLeftOf="@+id/tv4"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv4"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:background="#07d2e4"
        android:gravity="center"
        android:text="chain4"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toRightOf="@+id/tv3"
        app:layout_constraintRight_toRightOf="parent"/>


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

GuideLine 基準線

GuideLine、Barrier、Group都是是ConstraintLayout的一個輔助類的控制元件,執行時是看不見的;顧名思義,GuideLine就是為ConstraintLayout裡的子佈局提供位置擺放的基準的,他有水平和垂直兩種方向android:orientation="horizontal",android:orientation="vertical"

GuideLine有三種定位方式

  1. layout_constraintGuide_begin 距離ConstraintLayout的左側或者頂部的距離
  2. layout_constraintGuide_end 距離ConstraintLayout的右側或者底部的距離
  3. layout_constraintGuide_percent 佔ConstraintLayout的寬或高的百分比

比如有一下這樣一個場景,用GuideLine就可以妥妥的解決

image

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

    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintGuide_begin="100dp"  // 距離左側100dp
        android:orientation="vertical"/>


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button1"
        app:layout_constraintRight_toLeftOf="@id/guideline"/>

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button2"
        app:layout_constraintLeft_toRightOf="@id/guideline"
        app:layout_constraintTop_toBottomOf="@id/button1"/>

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="button3"
        app:layout_constraintRight_toLeftOf="@id/guideline"
        app:layout_constraintTop_toBottomOf="@id/button2"/>

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

Barrier(1.1版本新加的控制元件)

Barrier是一個很實用的輔助類控制元件,字面意思為邊界的意思,他的作用是限制所引用一組View的邊界,讓這一組View的邊界動態的統一起來;關於Barrier的介紹非常的詳細,這裡的介紹就遵從該網站的翻譯簡單的說下吧

image

如上圖所示,我們建立佈局的時候,有時候佈局裡面的控制元件內容是變化的,我們有三個TextViews: 左邊 textView1 和 textView2 ,右邊 textView3。textView3 以textView1 右側為約束,這樣似乎看不出什麼問題。但是當textView2的文字內容很長的時候就會出現問題了,如下圖:

image

這個問題很好理解的,因為textView3是相對於textView1的。當然解決這個問題的方案最常見的方法就是在textView1、textView2外面包一層LinearLayout。但是現在Barrier可以完美解決這個問題

image

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

    <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="tv1,tv2"/>


    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Rxjava"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="大王叫我來巡山"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv1"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="RxJava到底是什麼?讓我們直接跳過官方那種晦澀的追求精確的定義,其實初學RxJava只要把握兩點:觀察者模式和非同步,就基本可以熟練使用RxJava了。
非同步在這裡並不需要做太多的解釋,因為在概念和使用上,並沒有太多高深的東西。大概就是你腦子裡想能到的那些多執行緒,執行緒切換這些東西。我會在後面會講解它的用法。"
        app:layout_constraintLeft_toLeftOf="@id/barrier"
        app:layout_constraintTop_toTopOf="parent"/>


</android.support.constraint.ConstraintLayout>
複製程式碼
  • barrierDirection屬性決定 Barrier 的方向 ,可選擇設定範圍left、start、right、end、top、 bottom。他是給約束目標指定對齊的方向。

start

image

end

image

  • constraint_referenced_ids Barrier指定引用的view的ID,以逗號隔開。

  • barrierAllowsGoneWidgets:預設為true,用來指定barrier是否生效

Group (1.1版本新加的控制元件)

Group可以幫助你對一組控制元件進行統一的管理。我們最常見的情況是控制一組控制元件的visibility。你只需把控制元件的id引用到Group,就能同時對裡面的所有控制元件進行操作。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">


    <android.support.v7.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:id="@+id/tv1"
        android:text="text1"
        android:layout_height="wrap_content"/>

    <android.support.v7.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:id="@+id/tv2"
        android:text="text2"
        app:layout_constraintTop_toBottomOf="@id/tv1"
        android:layout_height="wrap_content"/>
    
    <android.support.constraint.Group
        android:layout_width="wrap_content"
        android:id="@+id/group"
        app:constraint_referenced_ids="tv1,tv2"
        android:visibility="visible" // 控制tv1,tv2的顯示和隱藏
        android:layout_height="wrap_content"/>

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

ConstraintSet

我們知道在LinearLayout、RelativeLayout等中,如果想通過程式碼來更改佈局,則需要LayoutParams,來控制控制元件的大小位置等。但是在ConstraintLayout中官方不建議使用LayoutParams,官方則推薦使用ConstraintSet來使用。ConstraintSet不僅可以調整佈局,還可以新增動畫。 我們可以通過以下 3 種方式來獲取 ConstraintSet:

  1. 手動建立
	
c = new ConstraintSet(); 
c.connect(....);

複製程式碼
  1. 讀取xml檔案
	
c.clone(context, R.layout.test);

複製程式碼
  1. 複製其他ConstraintLayout
	
c.clone(clayout);

複製程式碼

具體實現方式:

  1. 替換XML佈局

 // Activity
 
public class ConstraintDemo1Activity extends AppCompatActivity {

    ConstraintSet mConstraintSet1 = new ConstraintSet(); // create a Constraint Set
    ConstraintSet mConstraintSet2 = new ConstraintSet(); // create a Constraint Set
    private ConstraintLayout mConstraintLayout;
    private AppCompatImageView mImageView;
    boolean mOld = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_constraint_demo1);
        initEvent();
    }

    private void initEvent() {
        mConstraintSet2.clone(this, R.layout.activity_constraint_demo2);
        mConstraintLayout = findViewById(R.id.root);
        mConstraintSet1.clone(mConstraintLayout);
        mImageView = findViewById(R.id.imageView);
        mImageView.setOnClickListener(v -> {
            TransitionManager.beginDelayedTransition(mConstraintLayout);
            if (mOld = !mOld) {
                mConstraintSet1.applyTo(mConstraintLayout); // set new constraints
            } else {
                mConstraintSet2.applyTo(mConstraintLayout); // set new constraints
            }
        });
    }
}

// xml
//activity_constraint_demo1
<?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/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ConstraintDemo1Activity">


    <android.support.v7.widget.AppCompatImageView
        android:id="@+id/imageView"
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintDimensionRatio="3:4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/hhh"/>

</android.support.constraint.ConstraintLayout>

//activity_constraint_demo2
<?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/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ConstraintDemo1Activity">


    <android.support.v7.widget.AppCompatImageView
        android:id="@+id/imageView"
        android:layout_width="250dp"
        android:layout_height="0dp"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="3:4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:srcCompat="@mipmap/hhh"/>

</android.support.constraint.ConstraintLayout>
複製程式碼
  1. 完全程式碼控制

//Activity

public class ConstraintDemo2Activity extends AppCompatActivity {


    private AppCompatButton mBtnOne;
    private AppCompatButton mBtnTwo;
    private AppCompatButton mBtnThree;
    private AppCompatButton mBtnApply;
    private ConstraintLayout mRoot;

    private ConstraintSet mConstraintSet1 = new ConstraintSet();
    private ConstraintSet mConstraintSet2 = new ConstraintSet();
    boolean mOld = true;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_constraint2_layout);
        initView();
        initEevent();
    }

    private void initView() {
        mBtnOne = findViewById(R.id.btn_one);
        mBtnTwo = findViewById(R.id.btn_two);
        mBtnThree = findViewById(R.id.btn_three);
        mBtnApply = findViewById(R.id.btn_apply);
        mRoot = findViewById(R.id.root);
    }

    private void initEevent() {
        mConstraintSet1.clone(mRoot);
        mConstraintSet2.clone(mRoot);
        mBtnApply.setOnClickListener(v->apply());
    }

    private void apply() {
        TransitionManager.beginDelayedTransition(mRoot);
        if (mOld = !mOld) {
            //預設樣式
            mConstraintSet2.applyTo(mRoot);
        }else {
            //清除子View的佈局
            mConstraintSet1.clear(R.id.btn_one);
            mConstraintSet1.clear(R.id.btn_two);
            mConstraintSet1.clear(R.id.btn_three);

            //重新排列布局
            mConstraintSet1.connect(R.id.btn_one, ConstraintSet.LEFT, R.id.root, ConstraintSet.LEFT, 0);
            mConstraintSet1.connect(R.id.btn_three, ConstraintSet.RIGHT, R.id.root, ConstraintSet.RIGHT, 0);

            mConstraintSet1.connect(R.id.btn_two, ConstraintSet.LEFT, R.id.btn_one, ConstraintSet.RIGHT, 0);
            mConstraintSet1.connect(R.id.btn_one, ConstraintSet.RIGHT, R.id.btn_two, ConstraintSet.LEFT, 0);

            mConstraintSet1.connect(R.id.btn_two, ConstraintSet.RIGHT, R.id.btn_three, ConstraintSet.LEFT, 0);
            mConstraintSet1.connect(R.id.btn_three, ConstraintSet.LEFT, R.id.btn_two, ConstraintSet.RIGHT, 0);

            //設定chains的樣式
            mConstraintSet1.createHorizontalChain(R.id.root, ConstraintSet.LEFT,
                    R.id.root, ConstraintSet.RIGHT,
                    new int[]{R.id.btn_one,R.id.btn_two,R.id.btn_three}, null, ConstraintWidget.CHAIN_PACKED);

            //設定子View的寬高
            mConstraintSet1.constrainWidth(R.id.btn_one,ConstraintSet.WRAP_CONTENT);
            mConstraintSet1.constrainWidth(R.id.btn_two,ConstraintSet.WRAP_CONTENT);
            mConstraintSet1.constrainWidth(R.id.btn_three,ConstraintSet.WRAP_CONTENT);
            mConstraintSet1.constrainHeight(R.id.btn_one,ConstraintSet.WRAP_CONTENT);
            mConstraintSet1.constrainHeight(R.id.btn_two,ConstraintSet.WRAP_CONTENT);
            mConstraintSet1.constrainHeight(R.id.btn_three,ConstraintSet.WRAP_CONTENT);

            //重新載入佈局
            mConstraintSet1.applyTo(mRoot);
        }
    }
}

// xml
<?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/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ConstraintDemo2Activity">


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/btn_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="50dp"
        android:background="@color/colorAccent"
        android:text="button1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/btn_two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="40dp"
        android:layout_marginTop="20dp"
        android:background="@android:color/holo_green_dark"
        android:text="button2"
        app:layout_constraintStart_toEndOf="@id/btn_one"
        app:layout_constraintTop_toBottomOf="@id/btn_one"/>

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/btn_three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:background="@android:color/holo_orange_dark"
        android:text="button3"
        app:layout_constraintStart_toEndOf="@id/btn_one"
        app:layout_constraintTop_toBottomOf="@id/btn_two"/>


    <android.support.v7.widget.AppCompatButton
        android:id="@+id/btn_apply"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="30dp"
        android:text="apply"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>

</android.support.constraint.ConstraintLayout>

複製程式碼

總結

本文主要參照官方文件並加上自己的理解來的,在這我們看到ConstraintLayout的強大之處,他可以說是RelativeLayout和LinearLayout的集大成者,同時又簡化了佈局的層層巢狀,大大提升了頁面的渲染速度。但是官方提供的可拖拽的功能還不是很完美,大多情況下需要手動調整。總之,ConstraintLayout是現階段的大勢所趨。

參考

Android Developers

Constraintlayout網站

ConstraintLayout 完全解析 快來優化你的佈局吧

Android新特性介紹,ConstraintLayout完全解析

相關文章