Compose 和其他库

您可以在 Compose 中使用您喜欢的库。本节介绍如何合并一些最实用的库。

活动

要在活动中使用 Compose,您必须使用 ComponentActivity,它是 Activity 的子类,它为 Compose 提供了适当的 LifecycleOwner 和组件。它还提供额外的 API,使您的代码与覆盖活动类中的方法分离。 活动 Compose 将这些 API 公开给可组合函数,因此不再需要在可组合函数之外覆盖方法或检索显式 Activity 实例。此外,这些 API 确保它们只初始化一次,在重新组合后保留,并在可组合函数从组合中移除时正确清理。

活动结果

rememberLauncherForActivityResult() API 允许您 从活动中获取结果 在您的可组合函数中

@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() 合同。点击按钮启动请求。该 rememberLauncherForActivityResult() 的尾部 lambda 在用户选择图像并返回到启动活动后被调用。这将使用 Coil 的 rememberImagePainter() 函数加载选定的图像。

任何 ActivityResultContract 的子类都可以用作 rememberLauncherForActivityResult() 的第一个参数。这意味着您可以使用此技术从框架和在其他常见模式中请求内容。您还可以创建自己的 自定义合同 并将它们与此技术一起使用。

请求运行时权限

上面解释的相同活动结果 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。将以下依赖项添加到您的 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 的范围限定为封闭的活动、片段或导航目的地,并在范围存在时保留。

例如,如果可组合函数在活动中使用,则 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 以及实例如何与导航 Compose 库、活动和片段一起使用的更多信息,请参阅 互操作性文档

数据流

Compose 附带了 Android 最流行的基于流的解决方案的扩展。这些扩展中的每一个都由不同的工件提供

这些工件注册为监听器并将值表示为 State。每当发出新值时,Compose 就会重新组合使用该 state.value 的 UI 部分。例如,在此代码中,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。

导航组件 为 Jetpack Compose 应用程序提供支持。有关更多信息,请参阅 使用 Compose 导航将 Jetpack Navigation 迁移到 Navigation Compose

Hilt

Hilt 是 Android 应用程序中推荐的依赖项注入解决方案,并且与 Compose 无缝协作。

视图模型部分 中提到的 viewModel() 函数自动使用 Hilt 使用 @HiltViewModel 注释构建的视图模型。我们提供了有关 Hilt 的视图模型集成 的文档。

@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 和导航

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 注释的片段或活动。

例如,如果 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 中使用分页的更多信息,请查看 列表和网格文档

地图

您可以使用 地图 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 = MarkerState(position = singapore),
            title = "Singapore",
            snippet = "Marker in Singapore"
        )
    }
}