Compose 和其他程式庫

您可以在 Compose 中使用自己偏好的程式庫。本節將說明如何加入一些最為實用的程式庫。

Activity

如要在活動中使用 Compose,您必須使用 ComponentActivity,這是 Activity 的子類別,可向 Compose 提供適當的 LifecycleOwner 和元件。該類別還可提供其他 API,以便將程式碼與活動類別的覆寫方法分離。Activity Compose 會將向可組合項公開這些 API,這樣您就無需覆寫可組合項之外的方法,或是擷取明確的 Activity 執行個體。此外,這些 API 可確保只需將其初始化一次,在重構後仍有效,並且可在從組合內容中移除可組合項後妥善清理。

Activity 結果

rememberLauncherForActivityResult() API 可讓您從可組合項取得 Activity 結果

@Composable
fun GetContentExample() {
    var imageUri by remember { mutableStateOf<Uri?>(null) }
    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
        imageUri = uri
    }
    Column {
        Button(onClick = { launcher.launch("image/*") }) {
            Text(text = "Load Image")
        }
        Image(
            painter = rememberAsyncImagePainter(imageUri),
            contentDescription = "My Image"
        )
    }
}

本範例說明了簡單的 GetContent() 合約。輕觸按鈕即可啟動要求。使用者選取圖片並返回正在啟動的 Activity 後,系統就會叫用 rememberLauncherForActivityResult() 的結尾 lambda。這個指令會使用 Coil 的 rememberImagePainter() 函式載入所選圖片。

ActivityResultContract 的所有子類別都可以做為 rememberLauncherForActivityResult() 的第一個引數使用。也就是說,您可以利用這項技術向架構和其他常見模式要求內容。您也可以建立自己的自訂合約,並搭配這項技術使用。

要求執行階段權限

您可以使用上述的 Activity Result API 和 rememberLauncherForActivityResult() 要求執行階段權限,使用 RequestPermission 合約可用於請求單個權限,使用 RequestMultiplePermissions 合約可用於請求多個權限。

此外,Accompanist 權限程式庫也可以用在這些 API 上方的層,將目前授予的狀態對應至 Compose UI 可使用的狀態。

處理系統返回按鈕

如要提供自訂返回導覽功能並覆寫可組合項的系統返回按鈕的預設行為,您的可組合項可以使用 BackHandler 以攔截該事件:

var backHandlingEnabled by remember { mutableStateOf(true) }
BackHandler(backHandlingEnabled) {
    // Handle back press
}

第一個引數可控制目前是否要啟用 BackHandler;您可以根據元件狀態使用此引數暫時停用處理常式。如果使用者觸發系統返回事件,且目前已啟用 BackHandler,則系統會叫用結尾 lambda。

ViewModel

如果您使用架構元件 ViewModel 程式庫,可以呼叫 viewModel() 函式,從任何可組合項存取 ViewModel。將以下依附元件新增至 Gradle 檔案:

Groovy

dependencies {
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5'
}

Kotlin

dependencies {
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5")
}

接著,您就可以在程式碼中使用 viewModel() 函式。

class MyViewModel : ViewModel() { /*...*/ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    // use viewModel here
}

viewModel() 會傳回現有的 ViewModel 或建立新的。根據預設,傳回的 ViewModel 範圍僅限於包含活動、片段或導覽目的地,且只要範圍有效,就會保留該範圍。

例如,如果在活動中使用可組合項,viewModel() 會傳回相同的例項,直到活動完成或程序結束為止。

class MyViewModel : ViewModel() { /*...*/ }
// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    // Returns the same instance as long as the activity is alive,
    // just as if you grabbed the instance from an Activity or Fragment
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

@Composable
fun MyScreen2(
    viewModel: MyViewModel = viewModel() // Same instance as in MyScreen
) { /* ... */ }

使用指南

您通常會在畫面層級的可組合函式中存取 ViewModel 例項,也就是類似於從活動、片段或導覽圖目的地呼叫的根可組合項。這是因為 ViewModel 預設的範圍是那些畫面層級物件。如要進一步瞭解 ViewModel 的生命週期和範圍,請參閱這篇文章

請盡量避免將 ViewModel 例項向下傳遞至其他可組合項,因為這可能會使這些可組合項更難測試,並可能會破壞預覽畫面。而是僅傳遞所需資料,以及可執行所需必要的函式做為參數。

可以使用 ViewModel 例項來管理子畫面層級的可組合項狀態,但請注意 ViewModel生命週期和範圍。如果可組合項是自給自足的,建議您使用 Hilt 插入 ViewModel,避免必須從父項可組合項傳遞依附元件。

如果 ViewModel 有依附元件,viewModel() 會使用選用的 ViewModelProvider.Factory 做為參數。

如要進一步瞭解 Compose 中的 ViewModel,以及如何將執行個體與 Navigation Compose 程式庫或活動和片段搭配使用,請參閱互通性說明文件

資料串

Compose 帶有擴充功能,適用於 Android 最熱門的串流式解決方案。這些擴充功能均由不同的構件提供:

這些構件會註冊為事件監聽器,並顯示值為 State。每次發出新值時,Compose 都會重組 UI 中使用該 state.value 的部分。例如,在下列程式碼中,ShowData 會在每次 exampleLiveData 發出新值時進行重組。

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    val dataExample = viewModel.exampleLiveData.observeAsState()

    // Because the state is read here,
    // MyScreen recomposes whenever dataExample changes.
    dataExample.value?.let {
        ShowData(dataExample)
    }
}

Compose 中的非同步作業

Jetpack Compose 可助您透過可組合項中的協同程式執行非同步作業。

詳情請參閱副作用說明文件中的 LaunchedEffectproduceStaterememberCoroutineScope API。

Navigation 元件可支援 Jetpack Compose 應用程式。詳情請參閱「使用 Compose 進行導覽」和「將 Jetpack Navigation 遷移至 Navigation Compose」。

Hilt

我們建議您在向 Android 應用程式插入依附元件時採用 Hilt 解決方案,並且 Hilt 可與 Compose 完美搭配運作。

ViewModel 部分中提及的 viewModel() 函式會自動採用 Hilt 透過 @HiltViewModel 註解建構的 ViewModel。我們在說明文件中提供了 Hilt 的 ViewModel 整合相關資訊。

@HiltViewModel
class MyViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: ExampleRepository
) : ViewModel() { /* ... */ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

Hilt 與 Navigation

Hilt 也整合了 Navigation Compose 程式庫。將以下額外依附元件新增至 Gradle 檔案:

Groovy

dependencies {
    implementation 'androidx.hilt:hilt-navigation-compose:1.2.0'
}

Kotlin

dependencies {
    implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
}

使用 Navigation Compose 程式庫時,請一律使用 hiltViewModel 可組合函式來取得帶有 @HiltViewModel 註解的 ViewModel 的執行個體。該函式可與帶有 @AndroidEntryPoint 註解的 Fragment 或 Activity 搭配使用。

例如,如果 ExampleScreen 是導覽圖表中的目的地,請呼叫 hiltViewModel() 來取得限定為目的地的 ExampleViewModel 執行個體,如以下程式碼片段所示:

// import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    NavHost(navController, startDestination = startRoute) {
        composable("example") { backStackEntry ->
            // Creates a ViewModel from the current BackStackEntry
            // Available in the androidx.hilt:hilt-navigation-compose artifact
            val viewModel = hiltViewModel<MyViewModel>()
            MyScreen(viewModel)
        }
        /* ... */
    }
}

如果您需要改為擷取範圍為導覽路徑導覽圖表ViewModel 執行個體,請使用 hiltViewModel 可組合函式,並將對應的 backStackEntry 做為參數傳遞:

// import androidx.hilt.navigation.compose.hiltViewModel
// import androidx.navigation.compose.getBackStackEntry

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    val innerStartRoute = "exampleWithRoute"
    NavHost(navController, startDestination = startRoute) {
        navigation(startDestination = innerStartRoute, route = "Parent") {
            // ...
            composable("exampleWithRoute") { backStackEntry ->
                val parentEntry = remember(backStackEntry) {
                    navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}

分頁

分頁程式庫可助您更輕鬆地以漸進方式載入資料,並且 Compose 亦支援此程式庫。分頁發布頁面包含需要新增至專案及其版本的額外 paging-compose 依附元件的資訊。

以下是分頁程式庫的 Compose API 範例:

@Composable
fun MyScreen(flow: Flow<PagingData<String>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it }
        ) { index ->
            val item = lazyPagingItems[index]
            Text("Item is $item")
        }
    }
}

請參閱清單和格線文件,瞭解關於在 Compose 中使用分頁的更多資訊。

Maps

您可以使用 Maps Compose 程式庫,以在應用程式中提供 Google 地圖。以下是使用範例:

@Composable
fun MapsExample() {
    val singapore = LatLng(1.35, 103.87)
    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(singapore, 10f)
    }
    GoogleMap(
        modifier = Modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState
    ) {
        Marker(
            state = remember { MarkerState(position = singapore) },
            title = "Singapore",
            snippet = "Marker in Singapore"
        )
    }
}