[譯] ConstraintLayout深刻系列之代替常見佈局

原文:ConstraintLayout layouts
做者:Mark AllisonSebastiano Poggiandroid


本文將列舉講述如何使用 ConstraintLayout 來代替常見的三種佈局 LinearLayout 、 RelatvieLayout 、 PercentLayout 的用法,本文使用的 Android Studio 都是 2.4 alpha 7 版本的,而 ConstraintLayout 庫是使用的 1.0.2git

LinearLayout

浮動對齊特性

LinearLayout 的基本用法就是將子組件 View 在水平或者垂直方向浮動對齊,基於屬性 orientation 來設置。在視圖編輯器中使用 ConstraintLayout 要實現這個特性很是簡單,假如要實現相同的垂直方向浮動對齊,步驟很簡單,就是添加 View 而後將每個 View 的上邊緣添加約束向到它位置上的另外一個 View 便可,以下圖:github

在 XML 中實現浮動對齊特性

在 XML 中實現該特性也僅僅是爲每個 View 實現一個約束屬性 app:layout_constraintTop_toBottomOf 到整個浮動佈局中在它以前的 View。app

<?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 鏈,詳細能夠看看個人另外一篇文章,表現以下圖:編輯器

Chain 鏈建立後,咱們只須要在屬性視圖中爲每一個須要設置 weight 權重的鏈組件修改 layout_widthmatch_constraint 或者 0dp (二者是同樣的),而後再設置對應的權重值到 weight 的配置屬性,由於這個例子中咱們使用的是水平的 Chain 鏈,因此設置權重的時候設置的屬性是 horizontal_weight,以下圖。ide

最後,咱們就能夠再 blueprint 藍圖視圖下看到以下的展示:函數

在 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 。其實若是你對 RelativeLayoutConstraintLayout 都熟悉的話,就會感受 RelativeLayout 其實只是 ConstraintLayout 的更基礎版本,ConstraintLayout 的不少概念來源其實就是 RelativeLayout 。事實上,你還能夠認爲 ConstraintLayout 就是增強版的 RelativeLayout ,由於你對舊的 Android 佈局組件的熟悉,這將是很好的學習瞭解 ConstraintLayout 的思想體系模型。學習

在視圖編輯器中實現 RelativeLayout

由於 RelativeLayout 就是基於描述各個子 Views 之間的關係,而對各個子 Views 添加約束來實現相同的關係以及展示其實也很類似簡易實現。舉例,建立佈局「 X 位於 Y 之上」的約束就對應於 RelativeLayout 中的 android:layout_above 屬性:

類似效果的屬性對應表

上面已經提到了 RelativeLayoutConstraintLayout 的基本特性概念很是類似。你能夠經過查閱個人另外一篇文章來熟悉 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 組件垂直或水平對齊到父組件的百分比位置,以下圖所示:

對齊到其餘 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_toEndOf24dp 的外邊距啓用了另外一個屬性 app:layout_marginGoneStart="56dp" ,以下動態圖所示:

PercentLayout

PercentLayout 一般被用於響應式佈局設計,當須要根據父組件來縮放子組件到百分比的狀況。

相對父組件的百分比寬高

首先咱們要看的特性是,子組件要實現佔據父組件的寬度或者高度的固定百分比。它在 PercentLayout 中是經過屬性 app:layout_widthPercentapp:layout_heightPercent 來實現的(此處的命名空間 app 是由於 PercentLayout 的庫引入是來自於 support library)。要實現該特性的話,咱們能夠經過 ConstraintLayout 中的 Guidelines 參照線來實現。假如咱們須要實現 app:layout_widthPercent="25%" 的特性,咱們能夠首先建立一個參照線,移動到 25% 處:

而後咱們就須要建立一個 View 將它的建立約束到父組件的 start 邊緣以及 end 約束到參照線。在此處,咱們沒有使用 left 而使用 start 是爲了更友好的支持 RTL 語言(從右到左佈局,right to left)

同時,咱們還須要注意的是咱們須要設置 android:layout_width 是被設置成了 0dp 或者 match_constraint(源碼層面,他們是同樣的)。而後移除這個 View 的外邊距,那麼這個 View 的寬度就會自動設置成父組件的 25% ,進一步操做以下圖所示:

在 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 邊緣約束到參照線,以下圖所示:

而後,在這個例子中咱們還設置這個 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_widthwrap_contentandroid:layout_marginStart0dp

實現固定橫縱比佈局

最後一個特性就是實現 PercentLayout 的橫縱比特性,經過它可讓高度固定比例爲寬度的函數,或者反過來。關於 ConstraintLayout 如何實現橫縱比尺寸,我有另外一篇文章 更詳細的講解了這個特性。首先咱們設置一個固定的比例,而後設置這個 View 的寬高爲 match_constraint0dp

而後咱們設置好水平方向的兩個約束條件,而後至少保留一個垂直方向的約束不設置,那麼咱們的組件 View 高度就會是依賴於寬度的函數,而後經過移動參照線來縮放 View 的寬度的時候就會發現高度也會相應的根據函數變化。

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

相關文章
相關標籤/搜索