將 Compose 新增至以 View 為基礎的應用程式

1. 事前準備

從一開始,Jetpack Compose 的設計就包含與 View 的互通性,也就是讓 Compose 和 View 系統可以共用資源並搭配運作來顯示 UI。這項功能可讓您將 Compose 新增至以 View 為基礎的現有應用程式。這表示 Compose 和 View 可在程式碼集中並存,直到整個應用程式完全使用 Compose 為止。

在本程式碼研究室中,您會將 Juice Tracker 應用程式中以 View 為基礎的清單項目變更為 Compose。您可以視需要自行轉換 Juice Tracker 的其餘檢視區塊。

如果應用程式使用的是以 View 為基礎的 UI,您可能不想一次重寫整個 UI。本程式碼研究室會協助您在以 View 為基礎的 UI 中,將單ㄧ檢視區塊轉換為 Compose 元素。

必要條件

  • 熟悉以 View 為基礎的 UI。
  • 瞭解如何使用以 View 為基礎的 UI 建構應用程式。
  • 具備 Kotlin 語法經驗 (包括 lambda)。
  • 瞭解如何在 Jetpack Compose 中建構應用程式。

課程內容

  • 如何將 Compose 新增至使用 Android View 建構的現有畫面。
  • 將可組合函式新增至以 View 為基礎的應用程式後,如何進行預覽。

建構項目

  • Juice Tracker 應用程式中,將以 View 為基礎的清單項目轉換為 Compose。

2. 範例應用程式總覽

本程式碼研究室使用的範例程式碼為「使用 View 建構 Android 應用程式」中,Juice Tracker 應用程式的解決方案程式碼。此範例應用程式已使用 Room 持續性資料庫儲存資料。使用者可以在應用程式資料庫中新增果汁資訊,例如果汁名稱、說明、顏色和評等。

36bd5542e97fee2e.png

在本程式碼研究室中,您會將處於 View 狀態的清單項目轉換為 Compose。

列出果汁詳細資料的清單項目

下載本程式碼研究室的範例程式碼

如要開始使用,請先下載範例程式碼:

或者,您也可以複製 GitHub 存放區的程式碼:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git
$ cd basic-android-kotlin-compose-training-juice-tracker
$ git checkout views

您可以瀏覽 JuiceTracker GitHub 存放區中的程式碼。

3. 新增 Jetpack Compose 程式庫

提醒您,Compose 和 View 可以並存在特定畫面中。您可以採用部分 Compose UI 元素,其他則採用 View 系統 UI 元素。舉例來說,您可以只採用 Compose 的清單,畫面的其餘部分則採用 View 系統。

如要將 Compose 程式庫新增至 Juice Tracker 應用程式,請完成下列步驟。

  1. 在 Android Studio 中開啟 Juice Tracker。
  2. 開啟應用程式層級的 build.gradle.kts 檔案。
  3. buildFeatures 區塊內新增 compose = true 標記。
buildFeatures {
    //...
    // Enable Jetpack Compose for this module
    compose = true
}

這個標記可讓 Android Studio 與 Compose 共同運作。您未在先前的程式碼研究室中執行過這個步驟,是因為當您建立新的 Android Studio Compose 範本專案時,Android Studio 會自動產生這段程式碼。

  1. buildFeatures 下方新增 composeOptions 區塊。
  2. 在此區塊中,將 kotlinCompilerExtensionVersion 設為 "1.5.1",設定 Kotlin 編譯器版本。
composeOptions {
    kotlinCompilerExtensionVersion = "1.5.1"
}
  1. dependencies 區段中新增 Compose 依附元件。您需要具備下列依附元件,才能將 Compose 新增至以 View 為基礎的應用程式。這些依附元件可協助整合 Compose 與 Activity、新增 Compose 設計元件庫、支援 Compose Jetpack 主題設定,並提供改善 IDE 支援功能的工具。
dependencies {
    implementation(platform("androidx.compose:compose-bom:2023.06.01"))
    // other dependencies
    // Compose
    implementation("androidx.activity:activity-compose:1.7.2")
    implementation("androidx.compose.material3:material3")
    implementation("com.google.accompanist:accompanist-themeadapter-material3:0.28.0")

    debugImplementation("androidx.compose.ui:ui-tooling")
}

新增 ComposeView

ComposeView 是可代管 Jetpack Compose UI 內容的 Android View。請使用 setContent 為 View 提供內容可組合函式。

  1. 開啟 layout/list_item.xml 並在「Split」分頁中查看預覽畫面。

完成本程式碼實驗室後,這個 View 會替換成可組合函式。

7a2df616fde1ec56.png

  1. JuiceListAdapter.kt 所有位置移除 ListItemBinding。在 JuiceListViewHolder 類別中,將 binding.root 替換為 composeView
import androidx.compose.ui.platform.ComposeView

class JuiceListViewHolder(
    private val onEdit: (Juice) -> Unit,
    private val onDelete: (Juice) -> Unit
): RecyclerView.ViewHolder(composeView)
  1. onCreateViewHolder() 資料夾中,根據下列程式碼更新 return() 函式:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JuiceListViewHolder {
   return JuiceListViewHolder(
       ComposeView(parent.context),
       onEdit,
       onDelete
   )
}
  1. JuiceListViewHolder 類別中,刪除所有 private 變數,並從 bind() 函式中移除所有程式碼。JuiceListViewHolder 類別現在會如以下程式碼所示:
class JuiceListViewHolder(
    private val onEdit: (Juice) -> Unit,
    private val onDelete: (Juice) -> Unit
) : RecyclerView.ViewHolder(composeView) {

   fun bind(juice: Juice) {

   }
}
  1. 此時,您可以刪除 com.example.juicetracker.databinding.ListItemBindingandroid.view.LayoutInflater 匯入內容。
// Delete
import com.example.juicetracker.databinding.ListItemBinding
import android.view.LayoutInflater
  1. 刪除 layout/list_item.xml 檔案。
  2. 在「Delete」對話方塊中選取「OK」

2954ed44c5827571.png

4. 新增可組合函式

接下來,您會建立一個用來發送清單項目的可組合函式。這個可組合函式使用 Juice 和兩個回呼函式來編輯及刪除清單項目。

  1. JuiceListAdapter.kt 中的 JuiceListAdapter 類別定義之後,建立名為 ListItem() 的可組合函式。
  2. ListItem() 函式接受 Juice 物件,以及用於刪除的 lambda 回呼。
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

@Composable
fun ListItem(
    input: Juice,
    onDelete: (Juice) -> Unit,
    modifier: Modifier = Modifier
) {
}

請查看要建立的清單項目預覽畫面。請注意,預覽畫面包含果汁圖示、果汁詳細資料和刪除按鈕圖示。您即將實作這些元件。

4ec7f82371c6bc15.png

建立果汁圖示可組合項

  1. JuiceListAdapter.kt 中的 ListItem() 可組合函式之後,建立另一個名為 JuiceIcon() 的可組合函式,並採用 colorModifier
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {

}
  1. JuiceIcon() 函式中,為 color 和內容說明新增變數,如以下程式碼所示:
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {
   val colorLabelMap = JuiceColor.values().associateBy { stringResource(it.label) }
   val selectedColor = colorLabelMap[color]?.let { Color(it.color) }
   val juiceIconContentDescription = stringResource(R.string.juice_color, color)

}

您可以使用 colorLabelMapselectedColor 變數,擷取與使用者選取項目相關聯的顏色資源。

  1. 新增 Box 版面配置,讓兩個圖示 ic_juice_coloric_juice_clear 彼此重疊顯示。ic_juice_color 圖示已上色,且置中對齊。
import androidx.compose.foundation.layout.Box

Box(
   modifier.semantics {
       contentDescription = juiceIconContentDescription
   }
) {
   Icon(
       painter = painterResource(R.drawable.ic_juice_color),
       contentDescription = null,
       tint = selectedColor ?: Color.Red,
       modifier = Modifier.align(Alignment.Center)
   )
   Icon(painter = painterResource(R.drawable.ic_juice_clear), contentDescription = null)
}

由於您已熟悉可組合函式的實作方式,這裡就不再詳細說明。

  1. 新增用來預覽 JuiceIcon() 的函式,並傳遞顏色為 Yellow
import androidx.compose.ui.tooling.preview.Preview

@Preview
@Composable
fun PreviewJuiceIcon() {
    JuiceIcon("Yellow")
}

c016198f82a5d199.png

建立果汁詳細資料可組合函式

JuiceListAdapter.kt 中,您需要新增其他可組合函式來顯示果汁詳細資料。此外,還需要 Column 版面配置,用來顯示名稱和說明的兩個 Text 可組合函式,以及評等指標。若要這樣做,請完成下列步驟:

  1. 新增名為 JuiceDetails() 的可組合函式,該函式會採用 Juice 物件和 Modifier,以及分別用於果汁名稱和說明的兩個可組合函式,如以下程式碼所示:
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.text.font.FontWeight

@Composable
fun JuiceDetails(juice: Juice, modifier: Modifier = Modifier) {
   Column(modifier, verticalArrangement = Arrangement.Top) {
       Text(
           text = juice.name,
           style = MaterialTheme.typography.h5.copy(fontWeight = FontWeight.Bold),
       )
       Text(juice.description)
       RatingDisplay(rating = juice.rating, modifier = Modifier.padding(top = 8.dp))
   }
}
  1. 如要排解未解決的參照錯誤,請建立名為 RatingDisplay() 的可組合函式。

536030e2ecb01a4e.png

在 View 系統中,您可以使用 RatingBar 顯示以下評等列。Compose 沒有評等列可組合函式,因此您需要從頭開始實作這項元素。

  1. 定義 RatingDisplay() 函式,依照評等顯示星號。這個可組合函式會根據評等顯示星號數量。

含有四顆星的評等列

import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource

@Composable
fun RatingDisplay(rating: Int, modifier: Modifier = Modifier) {
   val displayDescription = pluralStringResource(R.plurals.number_of_stars, count = rating)
   Row(
       // Content description is added here to support accessibility
       modifier.semantics {
           contentDescription = displayDescription
       }
   ) {
       repeat(rating) {
           // Star [contentDescription] is null as the image is for illustrative purpose
           Image(
               modifier = Modifier.size(32.dp),
               painter = painterResource(R.drawable.star),
               contentDescription = null
           )
       }
   }
}

如要在 Compose 中建立星型可繪項目,您需要建立星型向量素材資源。

  1. 在「Project」窗格的「drawable」上按一下滑鼠右鍵,依序點選「New」>「Vector Asset」

201431ca3d212113.png

  1. 在「Asset Studio」對話方塊中搜尋星號圖示,選取實心星號圖示。

9956ed24371f61ac.png

5a79bac6f3982b72.png

  1. 將星號的顏色值變更為 625B71

44d4bdfa93bc369a.png

  1. 依序點選「Next」>「Finish」。
  2. 請注意,可繪項目會顯示在 res/drawable 資料夾中。

64bb8d9f05019229.png

  1. 新增預覽可組合函式,用來預覽 JuiceDetails 可組合函式。
@Preview
@Composable
fun PreviewJuiceDetails() {
    JuiceDetails(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4))
}

含有果汁名稱、果汁說明和星級評等列

建立 DeleteButton 可組合函式

  1. JuiceListAdapter.kt 中,新增另一個名為 DeleteButton() 的可組合函式,該函式會採用 lambda 回呼函式和修飾符。
  2. 將 lambda 設為 onClick 引數並傳入 Icon(),如以下程式碼所示:
import androidx.compose.ui.res.painterResource
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton

@Composable
fun DeleteButton(onDelete: () -> Unit, modifier: Modifier = Modifier) {
    IconButton(
        onClick = { onDelete() },
        modifier = modifier
    ) {
        Icon(
            painter = painterResource(R.drawable.ic_delete),
            contentDescription = stringResource(R.string.delete)
        )
    }
}
  1. 新增預覽函式,用來預覽刪除按鈕。
@Preview
@Composable
fun PreviewDeleteIcon() {
    DeleteButton({})
}

Android Studio 的「刪除」圖示預覽畫面

5. 實作 ListItem 函式

現在,您已擁有顯示清單項目的所有必要可組合函式,可以在版面配置中排列這些項目了。請留意您在先前步驟中定義的 ListItem() 函式。

@Composable
fun ListItem(
   input: Juice,
   onEdit: (Juice) -> Unit,
   onDelete: (Juice) -> Unit,
   modifier: Modifier = Modifier
) {
}

JuiceListAdapter.kt 中,完成下列步驟來實作 ListItem() 函式。

  1. Mdc3Theme {} lambda 中加入 Row 版面配置。
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import com.google.accompanist.themeadapter.material3.Mdc3Theme

Mdc3Theme {
   Row(
       modifier = modifier,
       horizontalArrangement = Arrangement.SpaceBetween
   ) {

   }
}
  1. Row lambda 中,呼叫建立為子項元素的三個可組合函式 JuiceIconJuiceDetailsDeleteButton
JuiceIcon(input.color)
JuiceDetails(input, Modifier.weight(1f))
DeleteButton({})

Modifier.weight(1f) 傳遞至 JuiceDetails() 可組合函式,確保在測量未加權的子項元素後,果汁詳細資料會佔用剩餘的水平空間。

  1. onDelete(input) lambda 和靠上對齊修飾符做為參數,傳入至 DeleteButton 可組合函式。
DeleteButton(
   onDelete = {
       onDelete(input)
   },
   modifier = Modifier.align(Alignment.Top)
)
  1. 編寫預覽函式,用來預覽 ListItem 可組合函式。
@Preview
@Composable
fun PreviewListItem() {
   ListItem(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4), {})
}

Android Studio 清單項目預覽畫面,內含甜菜根果汁的詳細資料

  1. ListItem 可組合函式繫結至 View Holder。在 clickable() lambda 函式中呼叫 onEdit(input),即可在使用者點選該清單項目時,開啟編輯對話方塊。

JuiceListViewHolder 類別的 bind() 函式中,您需要代管可組合函式。請使用 ComposeView,這種 Android View 能使用其 setContent 方法代管 Compose UI 內容。

fun bind(input: Juice) {
    composeView.setContent {
        ListItem(
            input,
            onDelete,
            modifier = Modifier
                .fillMaxWidth()
                .clickable {
                    onEdit(input)
                }
                .padding(vertical = 8.dp, horizontal = 16.dp),
       )
   }
}
  1. 執行應用程式。新增您喜歡的果汁。請欣賞您製作的全新 Compose 清單項目。

aadccf32ab952d0f.png 8aa751f4cf63bf98.png

恭喜!您剛剛建立了第一個具有 Compose 互通性的應用程式,也就是在以 View 為基礎的應用程式中使用 Compose 元素的應用程式。

6. 取得解決方案程式碼

完成程式碼研究室後,如要下載當中用到的程式碼,您可以使用這些 git 指令:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git
$ cd basic-android-kotlin-compose-training-juice-tracker
$ git checkout views-with-compose

另外,您也可以下載存放區為 ZIP 檔案,然後解壓縮並在 Android Studio 中開啟。

如要查看解決方案程式碼,請前往 GitHub

7. 瞭解詳情

Android 開發人員說明文件

程式碼研究室 [中級]