原文首發自掘金蘆葦APP團隊,轉載到自己小號上再發一遍~
翻譯By Leelion6。關於 ConstraintLayout 的文章其實已經不少了,不過看到這篇文章寫的很有趣,以及在翻譯的過程中,感受到了不同文化環境下,寫作思維的不同。最關鍵的是,這篇文章的內容很細緻,對初學者比較友好,所以翻譯過來以供需要的開發者去學習。如果需要進階的用法歡迎去看郭霖等大神更深入一些的相關文章。
在本教程中,你將會使用
ConstraintLayout
從頭開始構建一個登入介面,從中學習建立Android 檢視相關的基礎知識。
作者:Fuad Kamal
原文連結:www.raywenderlich.com/9193-constr…
所需材料及原始碼下載:koenig-media.raywenderlich.com/uploads/201…
釋出日期:2019年1月30日
一款優秀的Android應用程式,需要的不僅僅是美觀的UI介面,同時還要有良好的效能表現。在繪製頁面的時候,你需要把控檢視在使用者螢幕上的何處出現,以及它出現的方式。
Android提供了多種佈局型別,這些佈局中確定子View的方式都是不同的。所有佈局都來自ViewGroup類。
構建Android UI常用的佈局有FrameLayout
, LinearLayout
和RelativeLayout
.
這些佈局易於使用,但當檢視結構變得複雜時,它們每個都有其侷限性和一些效能問題:
FrameLayout
只能通過設定相對於父View的Gravity屬性來定位子ViewLinearLayout
不允許各View相互重疊。大多數情況下,你必須使用多個LinearLayout
進行巢狀RelativeLayout
需要的效能較多,因為它總是會做兩次佈局測量
將包含 `layout_weight`屬性的 `LinearLayout` 同 `RelativeLayout`一起巢狀使用,將會指數級地增加布局效能成本。這時就需要 `ConstraintLayout` 約束佈局來進行拯救了。
在較新版本的Android Studio中,ConstraintLayout
已經作為預設佈局存在了,並且提供了許多放置物件的方法:可以相對於它們的容器本身進行約束、相對於其他物件約束或者彼此之間進行約束、以及相對於你自己建立的guileline(輔助線)進行約束。這些方法可以讓你在同一層次中建立大型、複雜、動態和響應式的檢視,甚至還能支援動畫!
Raze Galactic - 一款星際旅行應用程式
您可以使用該應用程式預定行星之間的旅行,計劃週末在空間站的度假,並在您抵達後預定月球車。
在本教程中,你將通過從頭開始構建星際旅行應用程式的登入介面UI,以瞭解ConstraintLayout
的基本功能。

在此過程中,你將學習:
- 如何新增和刪除約束
- 如何在介面上相對於其他元素動態定位UI元素
注意:本教程假設你熟悉 Android 和 Android Studio 的基礎知識。如果你不熟悉 Android 開發,請先檢視我們的 Android入門教程 系列。
入門
開啟Android Studio 3.2.1或更高版本,通過從啟動介面選擇 Start a new Android Studio project 來建立新的Android Studio專案。

在下一個介面中,輸入 Raze Galactic 作為 Application name (應用程式名稱)。
對於 Company domain (公司域名),你可以輸入任意你喜歡的內容。該示例使用 raywenderlich.com 。
對於 Project location (專案位置),請選擇計算機上對你來說有意義的地址,並確保地址路徑中沒有空格。你可以點選專案位置欄位右側的省略號 ...
以直接選擇計算機上的目錄。
最後,確保已選中 Include Kotlin support (新增Kotlin語言支援) 並點選 Next (下一步) 按鈕。
譯者注:勾選Kotlin語言支援在本文章中並非必要,後續的內容沒有直接使用到Kotilin,所以如果你是純粹的JAVA語言開發者或者不想使用Kotlin,這裡不勾選也完全OK。

在接下來的介面中,進行Android裝置選擇,你需要選擇 Phone and Tablet (手機和平板電腦),然後選擇 API 28: Android 9.0 (Pie) 並點選Next。
譯者注:API部分可以按自己習慣選擇,不一定非要到Android9.0這麼高的版本。

在“Add an Activity to Mobile” (為移動裝置新增活動) 介面,選擇 Empty Activity (空活動)。在之後你將向這個空活動中新增UI元素,以瞭解Android中的佈局操作。
接下來———你猜對了———點選Next!

在最後一個嚮導頁,Configure Activity(配置活動),保留所有預設配置,需要按Finish即可完成專案配置。

檢查ConstraintLayout版本
在開始建立新佈局之前,你需要檢查ConstraintLayout
是否需要版本更新。
你可以在app模組的構建Gradle檔案中找到此資訊。在Andoird Studio中,開啟 app ▸ build.gradle 並檢視其中的 dependencies { … }
部分。
在各種構建依賴項中,你應該能找到 implementation 'com.android.support.constraint:constraint-layout:x'
,其中x
是專案中ConstraintLayout
所使用的版本。

要完成本教程,你需要ConstraintLayout
1.1.3或更高版本。為了確保你擁有最新版本的ConstraintLayout
庫,請從選單中選擇 Tools ▸ SDK Manager 。

單擊 SDK Tools (SDK工具) 選項卡,在 Support Repository (支援的庫) 下檢視 ConstraintLayout for Android ,如果顯示 installed (已安裝),則表示你使用的是最新版本

接下來,選中名為 Show Package Details (顯示包詳情) 的核取方塊,以檢視專案中包含的庫的版本,Gradle檔案中的版本號需要與 SDK manager (SDK 管理器) 中安裝的版本相匹配。
通過配置 Android Studio 的設計介面以實現高效的約束佈局開發
在繼續學習本教程之前,請設定Android Studio檢視顯示方式,以便更輕鬆地新增和檢視約束以及其相關元素。
首先開啟 activity_main.xml ,然後單擊工具欄中的 Select Design Surface (選擇設計介面) 圖示,並選擇 Design + Blueprint (設計+藍圖)。

現在你在工作時就能看到設計預覽介面旁邊的藍圖介面了。

藍圖檢視可以幫助你更清晰地檢視約束和 guidelines (輔助線),而不會被內容或背景分散注意力。

在開發 APP UI 時,你可以在程式碼檢視 (Text tab) 和設計檢視 (Design tab) 之間來回切換。
程式碼檢視允許你檢視和編輯佈局背後的XML,而設計檢視對於視覺化操作UI元素非常有用。設計檢視還提供了 Component Tree (元件樹) , 它能讓你檢視和選擇節目中存在的所有UI元素。

當你處於程式碼檢視中,能夠看到視覺預覽和藍圖是很有用的。這樣,當你在預覽介面中選擇任何元素時,XML中相應的程式碼塊將變成高亮顯示。
如果你在程式碼檢視中沒有看到預覽介面,請單擊右側的 Preview (預覽) 選項卡。
從頭開始建立新的ConstraintLayout
對於接下來的這一步,請開啟 activity_main.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"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
複製程式碼
請注意,此佈局的預設根元素是android.support.constraint.ConstraintLayout
。另外,帶有 “Hello World!” 文字的TextView
元素已經有了一些約束屬性,例如app:layout_constraintBottom_toBottomOf="parent"
,它的作用是將此檢視的底部約束到其父容器的底部。
在相對其父容器頂部、底部、左側還有右側都約束後,包含 “Hello World!”的 TextView
便處於了螢幕的中心。
切換到設計檢視並將滑鼠游標移到包含 “Hello World!” 的TextView
上, 可以看到有四條波浪線將TextView
連線到其父容器上,這些線表示TextView
已經應用了約束規則。

如果沒有出現這些線,請單擊TextView
,然後它們就會出現。如果已選中 Show Constraints (顯示約束) 檢視選項,則無需使用滑鼠游標懸停或選擇任何檢視,就可以看到所有約束。

向APP中新增影象
對於下一步,你將需要在本教程開頭的應用程式螢幕截圖中看到的火箭影象。使用教程開頭的“ 下載材質”按鈕下載本教程的材料,或者點此下載。你可以在 RazeGalactic-starter 資料夾中找到 track_icon.png 。
獲取 track_icon.png 並將其新增到專案的 drawable 資料夾中。您可以通過在Mac上使用 command-C 或在Windows上使用 control-C 進行復制,然後右鍵單擊Android專案中的 drawable 資料夾並使用Mac上的 command-V 或Windows上的 control-V 貼上影象到專案中。
您可以將UI元素從 Palette (調色盤) 拖放到設計介面中。如果沒有看到 Palette ,請單擊垂直 Palette 選項卡圖示。

刪除“Hello World!”,然後將以下UI元素拖到 Palette 中的檢視中:
-
1個
ImageView
, 提示選擇影象時選擇火箭影象。 -
3個
Button
-
3個
TextView
更改UI元素的文字並將其拖動到螢幕周圍,以使它們看起來像本教程開頭的最終佈局預覽。不要過分在意檢視的尺寸、間距或對齊的精度。
接下來,你需要新增android:textAppearance="@style/TextAppearance.AppCompat.Headline"
到 Raze Galactic TextView
以獲得合適的樣式。

請注意,當你在螢幕上拖動檢視時,這些小對齊線是能幫助到你的,這些線條可以很容易地將檢視們相互排列。

測試檢視放置的位置
現在,構建並執行你的應用程式。

天了嚕!檢視沒有按你在設計檢視中分配好的位置排列!一切都在螢幕的左上角蜷縮著,這是腫麼回事?
嗯..好吧,Android得到如何放置UI元素的這部分資訊,因為你新增的檢視沒有任何已定義的約束。所以你需要解決這個問題。
回到Android Studio,可以看到 Component Tree (元件樹) 中的每個新檢視現在都有一系列錯誤。要檢視錯誤的完整文字,請單擊Component Tree (元件樹)中的紅色感嘆號。

錯誤如下:
此檢視不受約束。它僅包含在剛新增時的位置,所以它會在執行時放置於(0,0)的位置,除非你新增約束。
切換回程式碼檢視以檢查佈局XML的原始碼。請注意,任何檢視的原始碼都有一些帶tools
字首的屬性,例如:
<ImageView
android:id="@+id/imageView"
android:layout_width="46dp"
android:layout_height="46dp"
app:srcCompat="@drawable/track_icon"
tools:layout_editor_absoluteX="16dp"
tools:layout_editor_absoluteY="16dp" />
複製程式碼
佈局編輯器允許你將小部件放在畫布上的任何位置,並使用tools
字首屬性記錄當前位置。這些屬性在實際執行時並不會被應用,請記住這一點。
譯者補充:
tools
字首的屬性都是服務於佈局編輯器中的,只在XML的預覽檢視中起作用,實際執行時是沒有用的。
開始瞭解佈局編輯工具欄
切換回佈局的設計檢視,你可以在佈局預覽得上方看到一堆小控制元件。如果將滑鼠懸停在每個控制元件上,可以閱讀該控制元件的簡要說明。
自動連線

單擊工具欄中的磁鐵圖示以啟用 Autoconnect (自動連線) ,將任何檢視拖動到父檢視角落附近的區域,此時Android Studio將自動為你建立新約束。

請注意,這僅適用於在UI元素及其父檢視之間建立約束,而不是在UI元素之間建立約束,因此 Autoconnect 的用處非常有限。大多數情況下,你還是關閉 Autoconnect 吧。
自動連線(推測約束)

接下來,單擊 Infer Constraints (推測約束) 按鈕,Android Studio會自動新增布局中缺少的約束。
再次構建並執行您的應用,會發現現在這些元件不在角落,而是出現在你放置它們的位置了。

這是新增約束的最簡單方法,但也可能是最不好的。因為Android Studio佈局的意圖往往和你實際的意圖不同。
使用自動連線在特定大小的情況下佈局可能是正常的,但在不同的螢幕大小或螢幕方向上,可能就不會像你所希望的樣子佈局了。
清除所有約束

如果約束混亂,你可以清除所有約束並從頭開始重新新增約束。具體方法是,點選 Clear All Constraints (清楚所有約束) 按鈕清除當前頁面所有約束。
新增和刪除個別約束
Android提供了多種選項,可以將UI元素限制在螢幕的不同部分,讓你靈活地設計佈局。
在本教程的這一部分中,你將學習如何將物件約束到其容器,刪除單個約束以及將物件約束到另一個物件以達到動態佈局的效果。
將物件約束到其容器
請注意,當你單擊ImageView
時,它會突出顯示,並且檢視的頂部,底部,左側和右側會出現小圓圈。這些圓圈是你的約束錨點。

當你將滑鼠懸停在其中一個約束錨點上時,它將閃爍綠色。Android Studio會提示你建立約束連線。

單擊元素左側的圓圈並將其拖動到左側。在拖動時,將出現帶箭頭的線條,在UI元素的左側和要將其連線到的元素之間建立約束。
將圓拖動到檢視的左側,並將圓圈ImageView
的 左側約束到父檢視的左部。對圓圈ImageView
的頂部重複此操作,將其約束到父檢視頂部。您現在已將ImageView
的約束限制在檢視的左上角。
將影象向下拖動一點,Android Studio將在頂部建立一個邊距。通過在程式碼檢視中編輯XML程式碼或在設計檢視的 Attributes (屬性) 檢查器中對其進行編輯,將頂部邊距設定為30dp。

接下來,選擇 Raze Galactic TextView
, 將左約束錨點拖動到父檢視的左側,將右約束錨點拖動到父檢視的右側。通過將此UI元素約束到左側和右側,Android明白你是想將其達到居中的效果。

刪除個別約束
接下來,將滑鼠游標放在已設定約束的約束錨點之一上,表示箭頭的圓圈現在應該閃爍紅色,約束也會以紅色突出顯示。

單擊錨點就會刪除約束。現在先不要單擊,並將約束錨點保留在原位,但如果以後需要刪除約束,請記住該操作。
現在你知道如何將UI元素約束到其父容器的邊框,接下來到了學習如何將UI元素相互約束的時候了。
將物件彼此之間進行約束
在本教程的這一步中,你將實現 Raze Galactic TextView
始終與火箭影象對齊。
要做到這一點,你需要把 Raze Galactic TextView
的頂部錨點約束到ImageView
的頂部錨點,同時將TextView
底部錨點固定到ImageView
的底部錨點。兩個檢視便會垂直對齊。

現在,如果你按住火箭上下拖動,你將看到 Raze Galactic 隨著影象上下移動。

稍後,你將看到如何使用對齊選單建立這樣的對齊。但是,該方法並不總是完美無缺的,所以知道如何手動完成它也是一件好事。
切換到Android Studio中的程式碼檢視,並檢查剛才新增約束的檢視的程式碼:
<ImageView
android:id="@+id/imageView2"
android:layout_width="46dp"
android:layout_height="46dp"
android:layout_marginStart="16dp"
android:layout_marginTop="30dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/track_icon"/>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="Raze Galactic"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
app:layout_constraintBottom_toBottomOf="@+id/imageView2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/imageView2"/>
複製程式碼
現在你已經新增了一些約束,帶有tools字首的屬性已經消失,因為Android Studio現在不再需要僅設計時生效的這些屬性了。
你新增的每個約束都會在程式碼中提現,比如app:layout_constraintTop_toTopOf="parent"
,代表該元素的頂部約束到了父佈局的頂部。
你可以看到Android Studio為你設定了一些邊距,也許你並沒有想過去新增。這些邊距可能看起來像android:layout_marginStart="16dp"
。
繼續往下看,在 Raze Galactic TextView
中 刪除邊距屬性後切換回設計檢視,現在你會發現, Raze Galactic TextView
應該與火箭影象對齊了。
這兩個檢視不會再有錯誤出現了,因為已經滿足了Android Studio放置這兩個UI元素所需要的資訊量下限。
注意:你仍可能會看到有關使用硬編碼字串或丟失的警告
contentDescription
。不過你現在現在可以忽略這些。
現在,你應該將 ** Login** (登入)按鈕與 Sign Up (註冊) 按鈕對齊,就像將 Raze Galactic TextView
與火箭影象對齊一樣。為此,你將需要設定三個約束:
“登入”按鈕的頂部錨點位於“註冊”按鈕的頂部。 “登入”按鈕的底部錨點位於“註冊”按鈕的底部。 “登入”按鈕的左側錨點位於“註冊”按鈕的右部,並設定從左起30dp的起始邊距以在它們之間留出一些空間。

應用對齊約束
選擇 Raze Galactic TextView
並單擊右側錨點以刪除這些約束。然後,在TextView
仍然選中時,單擊工具欄中的 Align (對齊)工具,然後在選擇 Horizontally in Parent (水平佈局)。

這會自動將Raze Galactic TextView
相對父容器進行居中佈局。這與你在手動新增約束時之前實現的效果相同。
事實上,如果你切換到程式碼檢視並仔細檢查Raze Galactic TextView
,你會注意到Android Studio新增了以下約束屬性:
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
複製程式碼
這時你可能問了,Android Studio中自動新增的layout_constraintHorizontal_bias
是什麼?
Constraint Bias(約束偏差)
當檢視在水平或垂直方向上受到約束時,無論是父檢視還是其他檢視,預設情況下它具有0.5或50%的約束偏差。換句話說,檢視保持在它受約束的兩條邊之間的中心。
約束偏差範圍從0.0(0%)到1.0(100%),水平約束偏差從左到右增長,而垂直約束偏差從上到下增長。約束偏差對於為不同的螢幕尺寸動態定位檢視很有用。
要更輕鬆地瞭解約束偏差的工作原理,請切換回設計檢視。選中 Raze Galactic TextView
,在右側檢視 view inspector (檢視檢視器) 以及各種 Attributes(屬性)。

檢視檢視器左側的垂直滑塊控制垂直約束偏差,底部的垂直滑塊控制水平約束偏差。拖動每個滑塊以檢視約束偏差的工作原理:

在繼續下一步之前,將水平和垂直約束的偏差重置為50%。
對齊左邊緣並垂直分佈
接下來,同時選擇所有TextView
, Google Sign-In(從谷歌登入) 和 Sign-Up (註冊) 按鈕。你可以按住Shift鍵單擊每個UI元素依次全部選中它們。然後,從工具欄中單擊 Align (對齊) 並選擇 Left Edges (左邊緣) 。

這是應用左對齊後的佈局:

你注意到這裡有什麼問題了嗎? Raze Galactic TextView
的水平約束變不見了。
左邊緣將檢視與所選擇的最左側檢視對齊,它實際上做的是按降序從一個檢視建立左約束依賴關係,以最底部的檢視充當錨點。
因此,要使 Left Edges (左邊緣)命令起作用,它必須刪除其餘所選檢視的現有水平約束。
要反轉約束依賴關係順序,請再次應用 Left Edges (左邊緣)命令。您可以看到約束箭頭現在指向上方。

現在,使用與上一步驟中選擇的相同UI元素,單擊 Pack (包) 選單並選擇 Distribute Vertically (垂直分佈) 。

垂直分佈後,你的螢幕將如下所示:

同樣,你可能已經注意到與上述類似的問題:連線火箭圖示底部和 Raze Galactic TextView
的約束屬性已經消失。
約束依賴性按降序排列,就像在第一個 Left Edges (左邊緣) 命令之後一樣。不幸的是,沒有簡單的方法來解決這個問題,因此您必須手動建立垂直分散式約束。
本教程將介紹如何在下一部分中執行此操作,因此請繼續並撤消 Distribute Vertically (垂直分佈) 命令。
注意:Align 和 Pack 命令可能無法正常工作。它們可能會刪除現有的約束,並且可能不會移動某些受約束的檢視。在應用這些命令之前和之後,請務必檢查受影響檢視的約束。
使用預設邊距
要建立垂直分散式約束,只需連線具有相同邊距的約束即可。快速完成此操作的一個技巧是使用 Default Margin (預設邊距) 工具。

現在,單擊 Default Margin (預設邊距) 按鈕並設定值為60dp,然後將Google登入按鈕的頂部約束連線到 Raze Galactic TextView
的底部。你會發現Google登入按鈕會自動變換位置,在它與 Raze Galactic TextView
之間留出60dp的間距。
魔法 :]

建立其餘的垂直約束,如上面的GIF所示。最後,將到 Raze Galactic TextView
的左側約束到火箭圖示的右側,邊距為30dp。
檢查元件樹以檢視是否存在其他錯誤。如果沒有錯誤,恭喜!

構建並執行你的應用程式,現在所有內容應該在模擬器中以適當的佈局顯示了。

接下來做什麼?
你可以使用本教程頂部的下載材質按鈕下載此專案的最終版本。
ConstraintLayout
在佈局編輯器中構建UI 可能會令人沮喪,因為某些工具不夠智慧。但是,如果如果你知道如何正確的去使用,則可以節省大量時間。
本教程中未提及其他佈局編輯器工具,你可以去使用它們來了解它們的工作原理。看看谷歌ConstraintLayout
的文件上,以瞭解更多資訊。
有關更復雜的ConstraintLayout
示例,請參閱我們的後續文章ConstraintLayout Android教程:複雜佈局。
要檢視更多ConstraintLayout
的示例,請檢視我們的 Android Apprentice 一書,該書使用ConstraintLayout
作為所有頁面的佈局。
你現在已經掌握了ConstraintLayout
基本的概念,要了解更多高階功能並獲得處理複雜佈局的提示,請繼續關注我們即將推出的構建複雜佈局的教程ConstraintLayout Android教程:複雜佈局,你將在其中為 Raze Galactic 旅行應用程式構建更復雜的約束檢視,然後為其製作動畫!
如果你有任何問題或意見,歡迎在下方留言討論~