代替常見布局

2018-06-06 18:25 更新

原文:ConstraintLayout layouts 作者:Mark Allison 、 Sebastiano Poggi


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

LinearLayout

浮動對齊特性

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

在 XML 中實現(xiàn)浮動對齊特性

在 XML 中實現(xiàn)該特性也僅僅是為每一個 View 實現(xiàn)一個約束屬性 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>

子組件權(quán)重特性

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

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

最后,我們就可以再 blueprint 藍(lán)圖視圖下看到如下的展現(xiàn):

在 XML 中實現(xiàn)權(quán)重特性

首先要如之前的教程一樣,在 XML 創(chuàng)建 Chain 鏈,然后實現(xiàn)如上的效果只需要對 textView3 修改屬性 android:layout_width="0dp" 并且設(shè)置新屬性 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 屬性設(shè)置的值與 LinearLayout 中設(shè)置的 android:layout_weight 是一樣的值并且用法一樣,將會根據(jù)所有子組件的設(shè)置的權(quán)重比分割剩余的空間。

RelativeLayout

RelativeLayout 主要被用于包裝布局根據(jù) views 組件之間的關(guān)系或與父組件的關(guān)系來布局的子 views 。其實如果你對 RelativeLayoutConstraintLayout 都熟悉的話,就會感覺 RelativeLayout 其實只是 ConstraintLayout 的更基礎(chǔ)版本,ConstraintLayout 的很多概念來源其實就是 RelativeLayout 。事實上,你還可以認(rèn)為 ConstraintLayout 就是加強版的 RelativeLayout ,因為你對舊的 Android 布局組件的熟悉,這將是很好的學(xué)習(xí)了解 ConstraintLayout 的思想體系模型。

在視圖編輯器中實現(xiàn) RelativeLayout

因為 RelativeLayout 就是基于描述各個子 Views 之間的關(guān)系,而對各個子 Views 添加約束來實現(xiàn)相同的關(guān)系以及展現(xiàn)其實也很相似簡易實現(xiàn)。舉例,創(chuàng)建布局“ X 位于 Y 之上”的約束就對應(yīng)于 RelativeLayout 中的 android:layout_above 屬性:

相似效果的屬性對應(yīng)表

上面已經(jīng)提到了 RelativeLayoutConstraintLayout 的基本特性概念非常相似。你可以通過查閱我的另一篇文章來熟悉 ConstraintLayout 的基礎(chǔ),然后使用如下面的表格中對應(yīng)的屬性來轉(zhuǎn)換 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"

這里要注意,相對父組件的居中沒有一對一即是只用一條屬性能設(shè)置同樣效果的,而是通過設(shè)置相同的約束條件到相對的兩個邊緣來實現(xiàn)。水平居中,意味著需要設(shè)置兩個相同的約束條件到水平的左和友邊緣對齊父組件,而垂直居中,則是需要設(shè)置兩個相同的約束條件到垂直的上下邊緣對齊父組件,自然而然的在兩個方向上都居中的話,則是需要設(shè)置兩對相同的約束條件在水平和垂直方向,即是四個約束條件對齊。提醒一下大家,在這里可以通過設(shè)置約束條件的 bias 來設(shè)置 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 能夠?qū)崿F(xiàn)的約束條件在 RelativeLayout 中不能實現(xiàn),比如對齊 View 的基線到另一個 View 的上或者下邊緣。之所以沒有列出來也是因為 RelativeLayout 中并沒有相對應(yīng)的屬性實現(xiàn)。

Constraint 約束對于不顯示的 GONE Views

RelativeLayout 實現(xiàn)的屬性中,ConstraintLayout 沒有實現(xiàn)的屬性只有一個 android:layout_alignWithParentIfMissing ,這個屬性將會讓 View 組件能夠在對齊對象不顯示 GONE 的時候,對齊到父組件。舉個例子,如果 A View 需要設(shè)置左對齊到toRightOf另一個 View (這個就命名為 B ) ,當(dāng)B不顯示的時候,就會左邊對齊到父組件。

ConstraintLayout 在這點上跟 RelativeLayout 或者說大多數(shù)布局都不同,它會考慮顯示為 GONE 的組件的位置并且針對不顯示任何東西的 View 的約束 Constraint 仍然有效。唯一的缺陷是這個 GONE 的 View 的寬高是 0,而且外邊距 margin 也被忽略不考慮。

為了適應(yīng)這種場景的情況下,ConstraintLayout 擁有一個屬性 app:layout_goneMargin[Left|Start|Top|Right|End|Bottom] 可以用于當(dāng)約束對象是一個 GONE View 的時候,設(shè)置外邊距 margin 。在下面的例子中,當(dāng)按鈕消失 gone 的時候,原本存在于輸入框?qū)Π粹o的屬性 start_toEndOf24dp 的外邊距啟用了另一個屬性 app:layout_marginGoneStart="56dp" ,如下動態(tài)圖所示:

PercentLayout

PercentLayout 通常被用于響應(yīng)式布局設(shè)計,當(dāng)需要根據(jù)父組件來縮放子組件到百分比的情況。

相對父組件的百分比寬高

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

然后我們就需要創(chuàng)建一個 View 將它的創(chuàng)建約束到父組件的 start 邊緣以及 end 約束到參照線。在此處,我們沒有使用 left 而使用 start 是為了更友好的支持 RTL 語言(從右到左布局,right to left)

同時,我們還需要注意的是我們需要設(shè)置 android:layout_width 是被設(shè)置成了 0dp 或者 match_constraint(源碼層面,他們是一樣的)。然后移除這個 View 的外邊距,那么這個 View 的寬度就會自動設(shè)置成父組件的 25% ,進(jìn)一步操作如下圖所示:

在 XML 中實現(xiàn)百分比寬高

以上例子的 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就能實現(xiàn)固定的百分比寬高。

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

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

然后,在這個例子中我們還設(shè)置這個 View 的寬度 android:layout_width="wrap_content" ,然后移除各個方向的外邊距 margin ,然后 View 就會有相對于父組件的 25% 寬度外邊距 margin。

在 XML 中實現(xiàn)百分比外邊距

在 XML 中,參照線 Guidline 是跟上面的例子一樣的設(shè)置,如下:

<?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 如何設(shè)置約束到這個參照線,在這個例子,我們需要設(shè)置 app:layout_constraintStart_toStartOf="@+id/guideline" 然后如上面編輯器中說的一樣設(shè)置 android:layout_widthwrap_contentandroid:layout_marginStart0dp 。

實現(xiàn)固定橫縱比布局

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

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

在 XML 中設(shè)置橫縱比布局

在 XML 中,真正設(shè)置了寬高比的屬性是 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基礎(chǔ)系列之尺寸橫縱比 dimensions。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號