天天看點

[譯] ConstraintLayout深入系列之代替常見布局

原文:ConstraintLayout layouts

作者:Mark Allison 、 Sebastiano Poggi

本文将列舉講述如何使用

ConstraintLayout

來代替常見的三種布局 LinearLayout 、 RelatvieLayout 、

PercentLayout

的用法,本文使用的 Android Studio 都是

2.4 alpha 7

版本的,而 ConstraintLayout 庫是使用的

1.0.2

LinearLayout

浮動對齊特性

LinearLayout

的基本用法就是将子元件 View 在水準或者垂直方向浮動對齊,基于屬性

orientation

來設定。在視圖編輯器中使用 ConstraintLayout 要實作這個特性非常簡單,假如要實作相同的垂直方向浮動對齊,步驟很簡單,就是添加 View 然後将每一個 View 的上邊緣添加限制向到它位置上的另一個 View 即可,如下圖:

[譯] ConstraintLayout深入系列之代替常見布局

在 XML 中實作浮動對齊特性

在 XML 中實作該特性也僅僅是為每一個 View 實作一個限制屬性

app:layout_constraintTop_toBottomOf

到整個浮動布局中在它之前的 View。

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

  <TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginTop="16dp"
    tools:text="TextView"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

  <TextView
    android:id="@+id/textView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginTop="8dp"
    tools:text="TextView"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView1" />

  <TextView
    android:id="@+id/textView3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginTop="8dp"
    tools:text="TextView"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView2" />

  <TextView
    android:id="@+id/textView4"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginTop="8dp"
    tools:text="TextView"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView3" />
</android.support.constraint.ConstraintLayout>
           

子元件權重特性

要想建立跟 LinearLayout 類似的 weight 權重特性的話,我們需要建立限制 Chain 鍊,詳細可以看看我的另一篇文章,表現如下圖:

[譯] ConstraintLayout深入系列之代替常見布局

Chain 鍊建立後,我們隻需要在屬性視圖中為每個需要設定 weight 權重的鍊元件修改

layout_width

match_constraint

或者

0dp

(兩者是一樣的),然後再設定對應的權重值到

weight

的配置屬性,因為這個例子中我們使用的是水準的 Chain 鍊,是以設定權重的時候設定的屬性是

horizontal_weight

,如下圖。

[譯] ConstraintLayout深入系列之代替常見布局

最後,我們就可以再 blueprint 藍圖視圖下看到如下的展現:

[譯] ConstraintLayout深入系列之代替常見布局

在 XML 中實作權重特性

首先要如之前的教程一樣,在 XML 建立 Chain 鍊,然後實作如上的效果隻需要對

textView3

修改屬性

android:layout_width="0dp"

并且設定新屬性

app:layout_constraintHorizontal_weight="1"

,如下:

<?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="com.stylingandroid.scratch.MainActivity">

  <TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginTop="16dp"
    app:layout_constraintEnd_toStartOf="@+id/textView2"
    app:layout_constraintHorizontal_chainStyle="spread"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:text="TextView" />

  <TextView
    android:id="@+id/textView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    app:layout_constraintEnd_toStartOf="@+id/textView3"
    app:layout_constraintStart_toEndOf="@+id/textView"
    app:layout_constraintTop_toTopOf="parent"
    tools:layout_editor_absoluteX="141dp"
    tools:text="TextView" />

  <TextView
    android:id="@+id/textView3"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="16dp"
    android:layout_marginTop="16dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_weight="1"
    app:layout_constraintStart_toEndOf="@+id/textView2"
    app:layout_constraintTop_toTopOf="parent"
    tools:text="TextView" />

</android.support.constraint.ConstraintLayout>
           

這裡

app:layout_constraintHorizontal_weight

屬性設定的值與

LinearLayout

中設定的

android:layout_weight

是一樣的值并且用法一樣,将會根據所有子元件的設定的權重比分割剩餘的空間。

RelativeLayout

RelativeLayout

主要被用于包裝布局根據 views 元件之間的關系或與父元件的關系來布局的子 views 。其實如果你對

RelativeLayout

ConstraintLayout

都熟悉的話,就會感覺

RelativeLayout

其實隻是

ConstraintLayout

的更基礎版本,

ConstraintLayout

的很多概念來源其實就是

RelativeLayout

。事實上,你還可以認為

ConstraintLayout

就是加強版的

RelativeLayout

,因為你對舊的 Android 布局元件的熟悉,這将是很好的學習了解

ConstraintLayout

的思想體系模型。

在視圖編輯器中實作 RelativeLayout

因為

RelativeLayout

就是基于描述各個子 Views 之間的關系,而對各個子 Views 添加限制來實作相同的關系以及展現其實也很相似簡易實作。舉例,建立布局“ X 位于 Y 之上”的限制就對應于

RelativeLayout

中的

android:layout_above

屬性:

[譯] ConstraintLayout深入系列之代替常見布局

相似效果的屬性對應表

上面已經提到了

RelativeLayout

ConstraintLayout

的基本特性概念非常相似。你可以通過查閱我的另一篇文章來熟悉

ConstraintLayout

的基礎,然後使用如下面的表格中對應的屬性來轉換

RelativeLayout

中的屬性到

ConstraintLayout

相對父元件

RelativeLayout

屬性

ConstraintLayout

屬性

android:layout_alignParentLeft="true"

app:layout_constraintLeft_toLeftOf="parent"

android:layout_alignParentLeft="true"

app:layout_constraintLeft_toLeftOf="parent"

android:layout_alignParentStart="true"

app:layout_constraintStart_toStartOf="parent"

android:layout_alignParentTop="true"

app:layout_constraintTop_toTopOf="parent"

android:layout_alignParentRight="true"

app:layout_constraintRight_toRightOf="parent"

android:layout_alignParentEnd="true"

app:layout_constraintEnd_toEndOf="parent"

android:layout_alignParentBottom="true"

app:layout_constraintBottom_toBottomOf="parent"

android:layout_centerHorizontal="true"

app:layout_constraintStart_toStartOf="parent"

app:layout_constraintEnd_toEndOf="parent"

android:layout_centerVertical="true"

app:layout_constraintTop_toTopOf="parent"

app:layout_constraintBottom_toBottomOf="parent"

android:layout_centerInParent="true"

app:layout_constraintStart_toStartOf="parent"

,

app:layout_constraintTop_toTopOf="parent"

,

app:layout_constraintEnd_toEndOf="parent"

, 和

app:layout_constraintBottom_toBottomOf="parent"

這裡要注意,相對父元件的居中沒有一對一即是隻用一條屬性能設定同樣效果的,而是通過設定相同的限制條件到相對的兩個邊緣來實作。水準居中,意味着需要設定兩個相同的限制條件到水準的左和友邊緣對齊父元件,而垂直居中,則是需要設定兩個相同的限制條件到垂直的上下邊緣對齊父元件,自然而然的在兩個方向上都居中的話,則是需要設定兩對相同的限制條件在水準和垂直方向,即是四個限制條件對齊。提醒一下大家,在這裡可以通過設定限制條件的

bias

來設定 View 元件垂直或水準對齊到父元件的百分比位置,如下圖所示:

[譯] ConstraintLayout深入系列之代替常見布局

對齊到其他 View 元件邊緣或者基線

RelativeLayout

屬性

ConstraintLayout

屬性

android:layout_toLeftOf

app:layout_constraintRight_toLeftOf

android:layout_toStartOf

app:layout_constraintEnd_toStartOf

android:layout_above

app:layout_constraintBottom_toTopOf

android:layout_toRightOf

app:layout_constraintLeft_toRightOf

android:layout_toEndOf

app:layout_constraintStart_toEndOf

android:layout_below

app:layout_constraintTop_toBottomOf

android:layout_alignLeft

app:layout_constraintLeft_toLeftOf

android:layout_alignStart

app:layout_constraintStart_toStartOf

android:layout_alignTop

app:layout_constraintTop_toTopOf

android:layout_alignRight

app:layout_constraintRight_toRightOf

android:layout_alignEnd

app:layout_constraintEnd_toEndOf

android:layout_alignBottom

app:layout_constraintBottom_toBottomOf

android:layout_alignBaseline

app:layout_constraintBaseline_toBaselineOf

這裡提醒一下大家,很多

ConstraintLayout

能夠實作的限制條件在

RelativeLayout

中不能實作,比如對齊 View 的基線到另一個 View 的上或者下邊緣。之是以沒有列出來也是因為

RelativeLayout

中并沒有相對應的屬性實作。

Constraint 限制對于不顯示的

GONE

Views

RelativeLayout

實作的屬性中,

ConstraintLayout

沒有實作的屬性隻有一個

android:layout_alignWithParentIfMissing

,這個屬性将會讓 View 元件能夠在對齊對象不顯示

GONE

的時候,對齊到父元件。舉個例子,如果 A View 需要設定左對齊到

toRightOf

另一個 View (這個就命名為 B ) ,當B不顯示的時候,就會左邊對齊到父元件。

ConstraintLayout

在這點上跟

RelativeLayout

或者說大多數布局都不同,它會考慮顯示為

GONE

的元件的位置并且針對不顯示任何東西的 View 的限制 Constraint 仍然有效。唯一的缺陷是這個

GONE

的 View 的寬高是 0,而且外邊距 margin 也被忽略不考慮。

為了适應這種場景的情況下,

ConstraintLayout

擁有一個屬性

app:layout_goneMargin[Left|Start|Top|Right|End|Bottom]

可以用于當限制對象是一個

GONE

View 的時候,設定外邊距 margin 。在下面的例子中,當按鈕消失 gone 的時候,原本存在于輸入框對按鈕的屬性

start_toEndOf

24dp

的外邊距啟用了另一個屬性

app:layout_marginGoneStart="56dp"

,如下動态圖所示:

[譯] ConstraintLayout深入系列之代替常見布局

PercentLayout

PercentLayout

通常被用于響應式布局設計,當需要根據父元件來縮放子元件到百分比的情況。

相對父元件的百分比寬高

首先我們要看的特性是,子元件要實作占據父元件的寬度或者高度的固定百分比。它在

PercentLayout

中是通過屬性

app:layout_widthPercent

app:layout_heightPercent

來實作的(此處的命名空間 app 是因為 PercentLayout 的庫引入是來自于 support library)。要實作該特性的話,我們可以通過

ConstraintLayout

中的 Guidelines 參照線來實作。假如我們需要實作

app:layout_widthPercent="25%"

的特性,我們可以首先建立一個參照線,移動到

25%

處:

[譯] ConstraintLayout深入系列之代替常見布局

然後我們就需要建立一個 View 将它的建立限制到父元件的

start

邊緣以及

end

限制到參照線。在此處,我們沒有使用

left

而使用

start

是為了更友好的支援 RTL 語言(從右到左布局,right to left)

同時,我們還需要注意的是我們需要設定

android:layout_width

是被設定成了

0dp

或者

match_constraint

(源碼層面,他們是一樣的)。然後移除這個 View 的外邊距,那麼這個 View 的寬度就會自動設定成父元件的

25%

,進一步操作如下圖所示:

[譯] ConstraintLayout深入系列之代替常見布局

在 XML 中實作百分比寬高

以上例子的 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: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"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.25" />

  <TextView
    android:id="@+id/textView3"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="0dp"
    android:layout_marginStart="0dp"
    tools:text="TextView"
    app:layout_constraintEnd_toStartOf="@+id/guideline"
    app:layout_constraintStart_toStartOf="parent" />

</android.support.constraint.ConstraintLayout>
           

實際上真正對齊百分比寬高是由 Guidline 完成的,百分比寬的 TextView 隻是建立了一個限制到參照線 Guideline就能實作固定的百分比寬高。

相對父元件的百分比外邊距 margin

PercentLayout

還可以讓我們實作相對于父元件的百分比外邊距 margin 。相比上面百分比寬高的例子,我們一樣需要在指定百分比位置設定一個 Guideline參照線,但不是設定 View 的寬度限制到參照線,而是設定 View 的

start

邊緣限制到參照線。舉個例子,如果我們需要設定的效果是

app:layout_marginStartPercent="25%"

,我們建立一個在

25%

位置的參照線,然後設定 View 的

start

邊緣限制到參照線,如下圖所示:

[譯] ConstraintLayout深入系列之代替常見布局

然後,在這個例子中我們還設定這個 View 的寬度

android:layout_width="wrap_content"

,然後移除各個方向的外邊距 margin ,然後 View 就會有相對于父元件的 25% 寬度外邊距 margin。

在 XML 中實作百分比外邊距

在 XML 中,參照線 Guidline 是跟上面的例子一樣的設定,如下:

<?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.constraint.Guideline
    android:id="@+id/guideline"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.25" />

  <TextView
    android:id="@+id/textView3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="0dp"
    app:layout_constraintStart_toStartOf="@+id/guideline"
    tools:text="TextView" />

</android.support.constraint.ConstraintLayout>
           

差別在于,我們的 View 如何設定限制到這個參照線,在這個例子,我們需要設定

app:layout_constraintStart_toStartOf="@+id/guideline"

然後如上面編輯器中說的一樣設定

android:layout_width

wrap_content

android:layout_marginStart

0dp

實作固定橫縱比布局

最後一個特性就是實作

PercentLayout

的橫縱比特性,通過它可以讓高度固定比例為寬度的函數,或者反過來。關于

ConstraintLayout

如何實作橫縱比尺寸,我有另一篇文章 更詳細的講解了這個特性。首先我們設定一個固定的比例,然後設定這個 View 的寬高為

match_constraint

0dp

[譯] ConstraintLayout深入系列之代替常見布局

然後我們設定好水準方向的兩個限制條件,然後至少保留一個垂直方向的限制不設定,那麼我們的元件 View 高度就會是依賴于寬度的函數,然後通過移動參照線來縮放 View 的寬度的時候就會發現高度也會相應的根據函數變化。

[譯] ConstraintLayout深入系列之代替常見布局

在 XML 中設定橫縱比布局

在 XML 中,真正設定了寬高比的屬性是

app:layout_constraintDimensionRatio

為想要的值,其他規則跟在視圖編輯器中是一樣的。

<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="com.stylingandroid.scratch.MainActivity">
  
    <View
      android:id="@+id/imageView"
      android:layout_width="0dp"
      android:layout_height="0dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="16dp"
      app:layout_constraintDimensionRatio="h,15:9"
      app:layout_constraintEnd_toStartOf="@+id/guideline"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent" />
  
    <android.support.constraint.Guideline
      android:id="@+id/guideline"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:orientation="vertical"
      app:layout_constraintGuide_percent="0.39" />
  
  </android.support.constraint.ConstraintLayout>
           

最後提醒一下,沒懂的小夥伴可以看看另一篇文章 ConstraintLayout基礎系列之尺寸橫縱比 dimensions。

最新系列教程,可以關注我的部落格 https://biaomingzhong.github.io/