教程取自于Google官方課程之Jetpack Compose 基礎知識
1. 準備工作
Jetpack Compose 是一款新型工具包,旨在幫助簡化界面開發。該工具包將響應式編程模型與簡潔易用的 Kotlin 編程語言相結合,并采用完全聲明式的代碼編寫方式,讓您可以通過調用一系列函數來描述界面,這些函數會將數據轉換為界面層次結構。當底層數據發生變化時,框架會自動重新執行這些函數,為您更新界面層次結構。
Compose 應用由可組合函數構成。可組合函數即帶有 @Composable
標記的常規函數,這些函數可以調用其他可組合函數。使用一個函數就可以創建一個新的界面組件。該注解會告知 Compose 為函數添加特殊支持,以便后續更新和維護界面。借助 Compose,您可以將代碼設計成多個小代碼塊。可組合函數通常簡稱為“可組合項”。
通過創建可重用的小型可組合項,您可以輕松構建應用中所用界面元素的庫。每個可組合項對應于屏幕的一個部分,可以單獨修改。
注意:在本 Codelab 中,“界面組件”、“可組合函數”和“可組合項”幾個術語可以互換使用來指代同一個概念。
2. 啟動新的 Compose 項目
如需啟動新的 Compose 項目,請打開 Android Studio Bumblebee,然后選擇 Start a new Android Studio project,如下所示:
如果系統未顯示上述界面,請依次進入 File > New > New Project。
創建新項目時,請從可用模板中選擇 Empty Compose Activity。
點擊 Next,然后照常配置項目,并將其命名為 Basics Codelab。請確保您選擇的 minimumSdkVersion 至少為 API 級別 21,這是 Compose 支持的最低 API 級別。
注意:如需詳細了解如何使用空 activity 設置 Compose,或如何將其添加到現有項目,請查看此文檔。
選擇 Empty Compose Activity 模板后,會在項目中為您生成以下代碼:
- 該項目已配置為使用 Compose。
- 已創建 AndroidManifest.xml 文件。
- build.gradle 和 app/build.gradle 文件包含 Compose 所需的選項和依賴項。
同步項目后,請打開 MainActivity.kt 并查看代碼。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BasicsCodelabTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
}
}
}
@Composable
private fun Greeting(name: String) {
Text(text = "Hello $name!")
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
BasicsCodelabTheme {
Greeting("Android")
}
}
警告:setContent 中使用的應用主題取決于項目名稱。本 Codelab 假定項目名稱為 LayoutsCodelab。如果您從 Codelab 中復制并粘貼代碼,請記得使用 ui/Theme.kt 文件中提供的主題名稱來更新 BasicsCodelabTheme。本 Codelab 后面會講到如何設置主題。
本 Codelab 的解決方案
您可以從 GitHub 獲取本 Codelab 的解決方案代碼:https://github.com/googlecodelabs/android-compose-codelabs/archive/main.zip
您可以在 BasicsCodelab 項目中找到解決方案代碼。建議您按照自己的節奏逐步完成 Codelab,必要時再查看解決方案。在此 Codelab 的學習過程中,我們會為您提供需要添加到項目的代碼段。
3. Compose 使用入門
了解 Android Studio 為您生成的與 Compose 相關的各種類和方法。
可組合函數
可組合函數是帶有 @Composable
注解的常規函數。這類函數自身可以調用其他 @Composable
函數。我們會展示如何為 Greeting
函數添加 @Composable
標記。此函數會生成一段顯示給定輸入 String
的界面層次結構。Text
是由庫提供的可組合函數。
@Composable
private fun Greeting(name: String) {
Text(text = "Hello $name!")
}
注意:可組合函數是帶有 @Composable 注解的 Kotlin 函數,如上述代碼段所示。
Android 應用中的 Compose
使用 Compose 時,Activities 仍然是 Android 應用的入口點。在我們的項目中,用戶打開應用時會啟動 MainActivity(如 AndroidManifest.xml 文件中所指定)。您可以使用 setContent 來定義布局,但不同于在傳統 View 系統中使用 XML 文件,您將在該函數中調用可組合函數。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BasicsCodelabTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
}
}
}
BasicsCodelabTheme 是為可組合函數設置樣式的一種方式。有關詳細內容,請參閱設置應用主題部分。如需查看文本在屏幕上的顯示效果,您可以在模擬器或設備上運行該應用,或使用 Android Studio 預覽進行查看。
若要使用 Android Studio 預覽,您只需使用 @Preview 注解標記所有無參數可組合函數或采用默認參數的函數,然后構建您的項目即可。現在 MainActivity.kt 文件中已經包含了一個 Preview Composable 函數。您可以在同一個文件中包含多個預覽,并為它們指定名稱。
@Preview(showBackground = true, name = "Text preview")
@Composable
fun DefaultPreview() {
BasicsCodelabTheme {
Greeting(name = "Android")
}
}
注意:在此項目中導入與 Jetpack Compose 相關的類時,請從以下位置導入:
androidx.compose.*(針對編譯器和運行時類)
androidx.compose.ui.*(針對界面工具包和庫)
4. 調整界面
首先,為 Greeting 設置不同的背景顏色。為此,您可以用 Surface 包圍 Text 可組合項。Surface 會采用一種顏色,因此請使用 MaterialTheme.colors.primary。
注意:
Surface
和MaterialTheme
是與 Material Design 相關的概念。Material Design 是 Google 提供的一個設計系統,旨在幫助您構建界面和體驗。
@Composable
private fun Greeting(name: String) {
Surface(color = MaterialTheme.colors.primary) {
Text (text = "Hello $name!")
}
}
嵌套在 Surface 內的組件將在該背景顏色之上繪制。
將上述代碼添加到項目后,您會在 Android Studio 的右上角看到 Build & Refresh 按鈕。點按該按鈕或構建項目即可在預覽中查看新更改。
您可以在預覽中查看新更改:
您可能忽略了一個重要的細節:文字現在是白色的。我們是何時對此進行定義的?
我們并沒有對此進行過定義!Material 組件(例如 androidx.compose.material.Surface)旨在提供應用中可能需要的常見功能(例如為文本選擇適當的顏色),讓您獲得更好的體驗。我們之所以說 Material 很實用,是因為它提供在大多數應用中都會用到的實用默認值和模式。Compose 中的 Material 組件是在其他基礎組件(位于 androidx.compose.foundation 中)的基礎上構建的。如果您需要更高的靈活性,也可以從您的應用組件中訪問這些組件。
在這種情況下,Surface 會了解,當該背景設置為 primary 顏色后,其上的任何文本都應使用 onPrimary 顏色,此顏色也在主題中進行了定義。如需了解詳情,請參閱設置應用主題部分。
注意:如需查看 Compose 中 Material 組件的交互式列表,請查看 Compose Material Catalog 應用。
修飾符
大多數 Compose 界面元素(例如 Surface 和 Text)都接受可選的 modifier 參數。修飾符會指示界面元素如何在其父級布局中放置、顯示或表現。
例如,padding 修飾符會在其修飾的元素周圍應用一定的空間。您可以使用 Modifier.padding() 創建內邊距修飾符。
現在,為屏幕上的 Text 添加內邊距:
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
...
點擊 Build & Refresh 即可查看新更改。
@Composable
private fun Greeting(name: String) {
Surface(color = MaterialTheme.colors.primary) {
Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
}
}
點擊 Build & Refresh 即可查看新更改。
5. 重復使用可組合項
您添加到界面的組件越多,創建的嵌套層級就越多。如果函數變得非常大,可能會影響可讀性。通過創建可重用的小型組件,可以輕松構建應用中所用界面元素的庫。每個組件對應于屏幕的一個部分,可以單獨修改。
創建一個名為 MyApp
的可組合項,該組合項中包含問候語。
@Composable
private fun MyApp() {
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
這樣一來,由于現在可以重復使用 MyApp 可組合項,您就可以省去 onCreate 回調和預覽,從而避免重復編寫代碼。您的 MainActivity.kt 文件應如下所示:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.codelab.basicstep1.ui.theme.BasicsCodelabTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BasicsCodelabTheme {
MyApp()
}
}
}
}
@Composable
private fun MyApp() {
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
@Composable
private fun Greeting(name: String) {
Surface(color = MaterialTheme.colors.primary) {
Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
}
}
@Preview(showBackground = true)
@Composable
private fun DefaultPreview() {
BasicsCodelabTheme {
MyApp()
}
}
6. 創建列和行
Compose 中的三個基本標準布局元素是 Column
、Row
和 Box
可組合項。
它們是接受可組合內容的可組合函數,因此您可以在其中放置內容。例如,Column 中的每個子級都將垂直放置。
// Don't copy over
Column {
Text("First row")
Text("Second row")
}
現在嘗試更改 Greeting,使其顯示包含兩個文本元素的列,如以下示例中所示:
請注意,您可能需要移動周圍的內邊距。
將您的結果與此解決方案進行比較:
import androidx.compose.foundation.layout.Column
...
@Composable
private fun Greeting(name: String) {
Surface(color = MaterialTheme.colors.primary) {
Column(modifier = Modifier.padding(24.dp)) {
Text(text = "Hello,")
Text(text = name)
}
}
}
Compose 和 Kotlin
可組合函數可以像 Kotlin 中的其他函數一樣使用。這會使界面構建變得非常有效,因為您可以添加語句來影響界面的顯示方式。
例如,您可以使用 for 循環向 Column 中添加元素:
@Composable
fun MyApp(names: List<String> = listOf("World", "Compose")) {
Column {
for (name in names) {
Greeting(name = name)
}
}
}
您尚未設置可組合項的尺寸,也未對可組合項的大小添加任何限制,因此每一行僅占用可能的最小空間,預覽時的效果也是如此。讓我們更改預覽效果,以模擬小屏幕手機的常見寬度 320dp。按如下所示向 @Preview 注解添加 widthDp 參數:
@Preview(showBackground = true, widthDp = 320)
@Composable
fun DefaultPreview() {
BasicsCodelabTheme {
MyApp()
}
}
修飾符在 Compose 中使用得非常廣泛,現在我們來練習更高級的用法:嘗試使用
fillMaxWidth
和 padding
修飾符復制以下布局。現在,將您的代碼與解決方案進行比較:
@Composable
fun MyApp(names: List<String> = listOf("World", "Compose")) {
Column(modifier = Modifier.padding(vertical = 4.dp)) {
for (name in names) {
Greeting(name = name)
}
}
}
@Composable
private fun Greeting(name: String) {
Surface(
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Column(modifier = Modifier.fillMaxWidth().padding(24.dp)) {
Text(text = "Hello, ")
Text(text = name)
}
}
}
請注意:
修飾符可以包含重載,因而具有相應的優勢,例如您可以指定不同的方式來創建內邊距。
若要向一個元素添加多個修飾符,您只需要將它們鏈接起來即可。
有多種方式可以實現此結果,因此,如果您的代碼與此代碼段不同,并不表示您的代碼就是錯的。不過,為了繼續完成此 Codelab,仍請復制并粘貼此代碼。
添加按鈕
接下來,您將添加一個用于展開 Greeting 的可點擊元素,因此需要先添加對應的按鈕。您的目標是要創建以下布局:
Button
是 Material 軟件包提供的一種可組合項,它采用可組合項作為最后一個參數。由于尾隨 lambda 可以移到括號之外,因此您可以向按鈕添加任何內容作為子級,例如 Text
:
// Don't copy yet
Button(
onClick = { } // You'll learn about this callback later
) {
Text("Show less")
}
注意:Compose 根據 Material Design 按鈕規范提供了不同類型的
Button
:Button
、OutlinedButton
和TextButton
。在本示例中,您將使用包圍Text
作為Button
內容的OutlinedButton
。
為了實現這一點,您需要學習如何在行尾放置可組合項。由于沒有 alignEnd 修飾符,因此您需要在開始時為該可組合項賦予一定的 weight。weight 修飾符會讓元素填滿所有可用空間,使其“具有彈性”,也就是會推開其他沒有權重的元素(即“無彈性”元素)。該修飾符還會使 fillMaxWidth 修飾符變得多余。
現在嘗試添加該按鈕,并按照上述圖片中所示放置該按鈕。
下面列出了對應的解決方案代碼:
import androidx.compose.material.Button
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
...
@Composable
private fun Greeting(name: String) {
Surface(
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(modifier = Modifier.weight(1f)) {
Text(text = "Hello, ")
Text(text = name)
}
OutlinedButton(
onClick = { /* TODO */ }
) {
Text("Show more")
}
}
}
}