天天看點

Android基本控件之ConstraintLayout

一、ConstraintLayout

1、 ConstraintLayout作為一款可以靈活調整view位置和大小的ViewGroup,ConstrantLayOut在複雜布局中能夠有效的,降低布局的層級,提高性能,使用更加的靈活。

在app中的依賴

Android基本控件之ConstraintLayout

2、相對定位:

 相對定位,其實這跟RelativeLayout差不多,一個View相對于另一個View的位置

Android基本控件之ConstraintLayout

通過簡單的使用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

也是類似的道理。

另外需要注意的是,view的位置可以相對于同層的view和parent,在相對于parent的時候toLeftOf、toTopOf、toRghtOf、toBottomOf分别表示位于parent的内部左上右下邊緣。如圖:紅色框表示parent view。

Android基本控件之ConstraintLayout

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

Android基本控件之ConstraintLayout

此時想要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友善多了。

Android基本控件之ConstraintLayout

什麼是baseline?貼張官網的圖

Android基本控件之ConstraintLayout

3、邊距

邊距與平常使用并無太大差別,但需要先确定view的位置,邊距才會生效。如:

<TextView

        ...

        android:layout_marginTop="10dp"

        android:layout_marginLeft="10dp"/>

在其他的ViewGroup,TextView的

layout_marginTop

layout_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

Android基本控件之ConstraintLayout

World不可見的效果,GitCode的左邊距為10

Android基本控件之ConstraintLayout

為此,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"/>
           

效果圖:

Android基本控件之ConstraintLayout

那,要是想把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"/>
           

4、圓形定位(Added in 1.1)

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

Android基本控件之ConstraintLayout

涉及三個屬性:

  • 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角度

Android基本控件之ConstraintLayout

 5、尺寸限制

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_height

wrap_content

;

layout_marginRight

為20dp,與parent左右對齊。

效果圖:

Android基本控件之ConstraintLayout

在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。

效果圖:

Android基本控件之ConstraintLayout

比例限制

控件的寬高比,要求是寬或高至少一個設為0dp,然後設定屬性

layout_constraintDimensionRatio

即可。

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

這裡設定寬高比為3:1,高度為100dp,那麼寬度将為300dp。

Android基本控件之ConstraintLayout

也可以在比例前加

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

6、 鍊

鍊在水準或者垂直方向提供一組類似行為。如圖所示可以了解為橫向鍊。這裡需要了解一點,A與parent的左邊緣限制,B與parent的右邊邊緣限制,A右邊和B左邊之間互相限制,才能使用一條鍊。多個元素之間也是如此,最左最右與parent限制,元素之間邊互相限制。不然下面的鍊式永遠無法生效。

Android基本控件之ConstraintLayout

橫向鍊最左邊第一個控件,垂直鍊最頂邊第一個控件稱為鍊頭,可以通過下面兩個屬性鍊頭統一定制鍊的樣式。

  • layout_constraintHorizontal_chainStyle  水準方向鍊式
  • layout_constraintVertical_chainStyle  垂直方向鍊式

它兩的值預設可以是

  • CHAIN_SPREAD 展開樣式(預設)
  • Weighted chain 在CHAIN_SPREAD樣式,部分控件設定了MATCH_CONSTRAINT,那他們将擴充可用空間。
  • CHAIN_SPREAD_INSIDE 展開樣式,但兩端不展開
  • CHAIN_PACKED 抱團(打包)樣式,控件抱團一起。通過偏移bias,可以改變packed元素的位置。
    Android基本控件之ConstraintLayout

    從實際開發,這麼應用還是挺廣泛的。

    提供份代碼參考,避免走冤枉路:

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

效果:

Android基本控件之ConstraintLayout

在鍊中,剩餘空餘空間預設平均給各元素,但有時可以通過權重屬性

layout_constraintVertical_weight

來指定配置設定空間的大小。

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

7、 優化器

在1.1之後,公開了優化器,通過在

app:layout_optimizationLevel

來決定控件在哪方面進行優化。

  • none : 不進行優化
  • standard : 預設方式, 僅僅優化direct和barrier限制
  • direct : 優化direct限制
  • barrier : 優化barrier限制
  • chain : 優化鍊限制 (實驗性質)
  • dimensions : 優化尺寸 (實驗性質), 減少測量次數

8、工具類

1) Guideline(參考線)

參考線實際上不會在界面進行顯示,隻是友善在ConstraintLayout布局view時候做一個參考。

通過設定Guideline的屬性

orientation

來表示是水準方向還是垂直方向的參考線,對應值為

vertical

horizontal

。可以通過三種方式來定位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才會顯示出來哦。

Android基本控件之ConstraintLayout

2) Barrier(栅欄)

Barrier有點類似Guideline,但Barrier會根據所有引用的控件尺寸的變化重新定位。例如經典的登入界面,右邊的EditText總是希望與左右所有TextView的最長邊緣靠齊。

如果兩個TextView其中一個變得更長,EditText的位置都會跟這變化,這比使用RelativeLayout靈活很多。

Android基本控件之ConstraintLayout

代碼:

<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 )Group(組)

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

吃個香噴噴栗子吧:

Group預設可見時,是這樣的。

Android基本控件之ConstraintLayout

設定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都被隐藏了。

Android基本控件之ConstraintLayout

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>
           

5 )其他

在2.0版本,為ConstraintLayout增加了ConstraintProperties、ConstraintsChangedListener等,感興趣可以自己看看官網。

總結

在寫本文之前,其實還不會用ConstraintLayout,寫完本文之後,已經上手和喜歡上了,滿足自己在實際開發中想要的效果,能夠有效的減少布局的層級,進而提高性能。不知道看完本文,你會使用ConstraintLayout了沒有?

轉載連結:https://juejin.im/post/5d12c4146fb9a07ea33c24b7

繼續閱讀