代替常見布局

2018-06-06 18:25 更新

原文:ConstraintLayout layouts 作者:Mark AllisonSebastiano Poggi


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

LinearLayout

浮動對齊特性

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

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

子組件權重特性

要想創(chuàng)建跟 LinearLayout 類似的 weight 權重特性的話,我們需要創(chuàng)建約束 Chain 鏈,詳細可以看看我的另一篇文章,表現如下圖:

Chain 鏈創(chuàng)建后,我們只需要在屬性視圖中為每個需要設置 weight 權重的鏈組件修改 layout_widthmatch_constraint 或者 0dp (兩者是一樣的),然后再設置對應的權重值到 weight 的配置屬性,因為這個例子中我們使用的是水平的 Chain 鏈,所以設置權重的時候設置的屬性是 horizontal_weight,如下圖。

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

在 XML 中實現權重特性

首先要如之前的教程一樣,在 XML 創(chuàng)建 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 添加約束來實現相同的關系以及展現其實也很相似簡易實現。舉例,創(chuàng)建布局“ 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" ,如下動態(tài)圖所示:

PercentLayout

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

相對父組件的百分比寬高

首先我們要看的特性是,子組件要實現占據父組件的寬度或者高度的固定百分比。它在 PercentLayout 中是通過屬性 app:layout_widthPercentapp:layout_heightPercent 來實現的(此處的命名空間 app 是因為 PercentLayout 的庫引入是來自于 support library)。要實現該特性的話,我們可以通過 ConstraintLayout 中的 Guidelines 參照線來實現。假如我們需要實現 app:layout_widthPercent="25%" 的特性,我們可以首先創(chuàng)建一個參照線,移動到 25% 處:

然后我們就需要創(chuàng)建一個 View 將它的創(chuàng)建約束到父組件的 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 只是創(chuàng)建了一個約束到參照線 Guideline就能實現固定的百分比寬高。

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

PercentLayout 還可以讓我們實現相對于父組件的百分比外邊距 margin 。相比上面百分比寬高的例子,我們一樣需要在指定百分比位置設置一個 Guideline參照線,但不是設置 View 的寬度約束到參照線,而是設置 View 的 start 邊緣約束到參照線。舉個例子,如果我們需要設置的效果是 app:layout_marginStartPercent="25%" ,我們創(chuàng)建一個在 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>

區(qū)別在于,我們的 View 如何設置約束到這個參照線,在這個例子,我們需要設置 app:layout_constraintStart_toStartOf="@+id/guideline" 然后如上面編輯器中說的一樣設置 android:layout_widthwrap_contentandroid:layout_marginStart0dp 。

實現固定橫縱比布局

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

然后我們設置好水平方向的兩個約束條件,然后至少保留一個垂直方向的約束不設置,那么我們的組件 View 高度就會是依賴于寬度的函數,然后通過移動參照線來縮放 View 的寬度的時候就會發(fā)現高度也會相應的根據函數變化。

在 XML 中設置橫縱比布局

在 XML 中,真正設置了寬高比的屬性是 app:layout_constraintDimensionRatio 為想要的值,其他規(guī)則跟在視圖編輯器中是一樣的。

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

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號