原文: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 即可,如下圖:
在 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 鍊,詳細可以看看我的另一篇文章,表現如下圖:
Chain 鍊建立後,我們隻需要在屬性視圖中為每個需要設定 weight 權重的鍊元件修改
layout_width
為
match_constraint
或者
0dp
(兩者是一樣的),然後再設定對應的權重值到
weight
的配置屬性,因為這個例子中我們使用的是水準的 Chain 鍊,是以設定權重的時候設定的屬性是
horizontal_weight
,如下圖。
最後,我們就可以再 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 。其實如果你對
RelativeLayout
和
ConstraintLayout
都熟悉的話,就會感覺
RelativeLayout
其實隻是
ConstraintLayout
的更基礎版本,
ConstraintLayout
的很多概念來源其實就是
RelativeLayout
。事實上,你還可以認為
ConstraintLayout
就是加強版的
RelativeLayout
,因為你對舊的 Android 布局元件的熟悉,這将是很好的學習了解
ConstraintLayout
的思想體系模型。
在視圖編輯器中實作 RelativeLayout
因為
RelativeLayout
就是基于描述各個子 Views 之間的關系,而對各個子 Views 添加限制來實作相同的關系以及展現其實也很相似簡易實作。舉例,建立布局“ X 位于 Y 之上”的限制就對應于
RelativeLayout
中的
android:layout_above
屬性:
相似效果的屬性對應表
上面已經提到了
RelativeLayout
和
ConstraintLayout
的基本特性概念非常相似。你可以通過查閱我的另一篇文章來熟悉
ConstraintLayout
的基礎,然後使用如下面的表格中對應的屬性來轉換
RelativeLayout
中的屬性到
ConstraintLayout
。
相對父元件
屬性 | 屬性 |
---|---|
| |
| |
| |
| |
| |
| |
| |
| 和 |
| 和 |
| , , , 和 |
這裡要注意,相對父元件的居中沒有一對一即是隻用一條屬性能設定同樣效果的,而是通過設定相同的限制條件到相對的兩個邊緣來實作。水準居中,意味着需要設定兩個相同的限制條件到水準的左和友邊緣對齊父元件,而垂直居中,則是需要設定兩個相同的限制條件到垂直的上下邊緣對齊父元件,自然而然的在兩個方向上都居中的話,則是需要設定兩對相同的限制條件在水準和垂直方向,即是四個限制條件對齊。提醒一下大家,在這裡可以通過設定限制條件的
bias
來設定 View 元件垂直或水準對齊到父元件的百分比位置,如下圖所示:
對齊到其他 View 元件邊緣或者基線
屬性 | 屬性 |
---|---|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
這裡提醒一下大家,很多
ConstraintLayout
能夠實作的限制條件在
RelativeLayout
中不能實作,比如對齊 View 的基線到另一個 View 的上或者下邊緣。之是以沒有列出來也是因為
RelativeLayout
中并沒有相對應的屬性實作。
Constraint 限制對于不顯示的 GONE
Views
GONE
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"
,如下動态圖所示:
PercentLayout
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 源碼如下:
<?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_width
為
wrap_content
和
android:layout_marginStart
為
0dp
。
實作固定橫縱比布局
最後一個特性就是實作
PercentLayout
的橫縱比特性,通過它可以讓高度固定比例為寬度的函數,或者反過來。關于
ConstraintLayout
如何實作橫縱比尺寸,我有另一篇文章 更詳細的講解了這個特性。首先我們設定一個固定的比例,然後設定這個 View 的寬高為
match_constraint
或
0dp
:
然後我們設定好水準方向的兩個限制條件,然後至少保留一個垂直方向的限制不設定,那麼我們的元件 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/