原文:ConstraintLayout layouts
做者:Mark Allison 、 Sebastiano Poggiandroid
本文將列舉講述如何使用 ConstraintLayout
來代替常見的三種佈局 LinearLayout 、 RelatvieLayout 、 PercentLayout
的用法,本文使用的 Android Studio 都是 2.4 alpha 7
版本的,而 ConstraintLayout 庫是使用的 1.0.2
。git
LinearLayout
的基本用法就是將子組件 View 在水平或者垂直方向浮動對齊,基於屬性 orientation
來設置。在視圖編輯器中使用 ConstraintLayout 要實現這個特性很是簡單,假如要實現相同的垂直方向浮動對齊,步驟很簡單,就是添加 View 而後將每個 View 的上邊緣添加約束向到它位置上的另外一個 View 便可,以下圖:github
在 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_width
爲 match_constraint
或者 0dp
(二者是同樣的),而後再設置對應的權重值到 weight
的配置屬性,由於這個例子中咱們使用的是水平的 Chain 鏈,因此設置權重的時候設置的屬性是 horizontal_weight
,以下圖。ide
最後,咱們就能夠再 blueprint 藍圖視圖下看到以下的展示:函數
首先要如以前的教程同樣,在 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
主要被用於包裝佈局根據 views 組件之間的關係或與父組件的關係來佈局的子 views 。其實若是你對 RelativeLayout
和 ConstraintLayout
都熟悉的話,就會感受 RelativeLayout
其實只是 ConstraintLayout
的更基礎版本,ConstraintLayout
的不少概念來源其實就是 RelativeLayout
。事實上,你還能夠認爲 ConstraintLayout
就是增強版的 RelativeLayout
,由於你對舊的 Android 佈局組件的熟悉,這將是很好的學習瞭解 ConstraintLayout
的思想體系模型。學習
由於 RelativeLayout
就是基於描述各個子 Views 之間的關係,而對各個子 Views 添加約束來實現相同的關係以及展示其實也很類似簡易實現。舉例,建立佈局「 X 位於 Y 之上」的約束就對應於 RelativeLayout
中的 android:layout_above
屬性:
上面已經提到了 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 組件垂直或水平對齊到父組件的百分比位置,以下圖所示:
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
中並無相對應的屬性實現。
GONE
ViewsRelativeLayout
實現的屬性中,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"
,以下動態圖所示:
PercentLayout
一般被用於響應式佈局設計,當須要根據父組件來縮放子組件到百分比的狀況。
首先咱們要看的特性是,子組件要實現佔據父組件的寬度或者高度的固定百分比。它在 PercentLayout
中是經過屬性 app:layout_widthPercent
和 app: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 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就能實現固定的百分比寬高。
PercentLayout
還可讓咱們實現相對於父組件的百分比外邊距 margin 。相比上面百分比寬高的例子,咱們同樣須要在指定百分比位置設置一個 Guideline參照線,但不是設置 View 的寬度約束到參照線,而是設置 View 的 start
邊緣約束到參照線。舉個例子,若是咱們須要設置的效果是 app:layout_marginStartPercent="25%"
,咱們建立一個在 25%
位置的參照線,而後設置 View 的 start
邊緣約束到參照線,以下圖所示:
而後,在這個例子中咱們還設置這個 View 的寬度 android:layout_width="wrap_content"
,而後移除各個方向的外邊距 margin ,而後 View 就會有相對於父組件的 25% 寬度外邊距 margin。
在 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
:
而後咱們設置好水平方向的兩個約束條件,而後至少保留一個垂直方向的約束不設置,那麼咱們的組件 View 高度就會是依賴於寬度的函數,而後經過移動參照線來縮放 View 的寬度的時候就會發現高度也會相應的根據函數變化。
在 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/