当前位置:   article > 正文

compose--附带效应、传统项目集成、导航_remembercoroutinescope

remembercoroutinescope

该文章将是compose基础系列中最后一篇,附带效应是这篇文章的重点,其余补充内容为如何在传统xml中集成composecompose导航的使用

一、附带效应

有了前面的了解,我们知道compose中是由State状态发生改变来使得可组函数发生重组,状态的改变应该是在可组合函数作用域中,但有时我们需要它发生在别的作用域,如定时弹出一个消息,这就需要附带效应出场了,compose定义了一系列附带效应API,来运用在可组合函数作用域内外,发生状态改变的不同场景

1.LaunchedEffect

LaunchedEffect我们之前就已经使用过了,特别是在低级别动画时,LaunchedEffect用于安全地调用挂起函数,本质就是启动一个协程,LaunchedEffect的调用需要在可组合函数作用域内

LaunchedEffect的执行分为以下三种,优先级由上到下:

  • 当发生重组时LaunchedEffect退出组合,将取消协程
  • 当发生重组时如果LaunchedEffect使用的同一个key,只会执行第一次,如果上次LaunchedEffect没执行结束,不重新执行
  • 当发生重组时如果LaunchedEffect使用的不同的key,并且上次LaunchedEffect没执行结束,则取消上次执行,启动新的协程执行该次任务

例子:

  1. @Preview
  2. @Composable
  3. fun MyLaunchEffect() {
  4. // 0cd5ba83-97c1-485e-87ff-272e4fd6afa7
  5. var state by remember { mutableStateOf(false) }
  6. var count by remember { mutableStateOf(0) }
  7. if (state) {
  8. // keyUnit唯一值
  9. LaunchedEffect(Unit) {
  10. delay(3000)
  11. count++
  12. }
  13. }
  14. Box(modifier = Modifier
  15. .size(50.dp)
  16. .background(Color.Cyan)
  17. .clickable { state = !state }
  18. ) {
  19. Text("执行了${count}次")
  20. }
  21. }

先是点击两下的效果,由于statefalse时,没有LaunchedEffect的代码块,此时LaunchedEffect会取消:

稍微改变下例子的代码,一旦状态发生改变,那么重复执行LaunchedEffect

  1. @Preview
  2. @Composable
  3. fun MyLaunchEffect2() {
  4. var state by remember { mutableStateOf(0) }
  5. var count by remember { mutableStateOf(0) }
  6. if (state > 0) {
  7. // keyUnit唯一值
  8. LaunchedEffect(Unit) {
  9. delay(3000)
  10. count++
  11. }
  12. }
  13. Box(modifier = Modifier
  14. .size(50.dp)
  15. .background(Color.Cyan)
  16. .clickable { state++ }
  17. ) {
  18. Text("执行了${count}次")
  19. }
  20. }

点击三下的效果,LaunchedEffectkey唯一,重复触发重组,key唯一时只会执行第一次的LaunchedEffect

改变例子代码,每次执行的key不同:

  1. @Preview
  2. @Composable
  3. fun MyLaunchEffect3() {
  4. var state by remember { mutableStateOf(0) }
  5. var count by remember { mutableStateOf(0) }
  6. if (state > 0) {
  7. // key为随机值
  8. LaunchedEffect(UUID.randomUUID()) {
  9. delay(3000)
  10. // 置为0,防止不断重组导致一直执行LaunchedEffect
  11. state = 0
  12. count++
  13. }
  14. }
  15. Box(modifier = Modifier
  16. .size(50.dp)
  17. .background(Color.Cyan)
  18. .clickable { state++ }
  19. ) {
  20. Text("执行了${count}次")
  21. }
  22. }

效果,取消了之前的LaunchedEffect,隔了3秒后才发生count状态改变:

2.rememberCoroutineScope

rememberCoroutineScope也是使用过的,它返回一个remember的协程作用域,可以在可组合函数外使用,调用几次执行几次

例子:

  1. @Preview
  2. @Composable
  3. fun MyRememberCoroutineScope() {
  4. val scope = rememberCoroutineScope()
  5. var count by remember { mutableStateOf(0) }
  6. Box(modifier = Modifier
  7. .size(50.dp)
  8. .background(Color.Cyan)
  9. .clickable {
  10. scope.launch {
  11. delay(3000)
  12. count++;
  13. }
  14. }
  15. ) {
  16. Text("执行了${count}次")
  17. }
  18. }

效果:

3.rememberUpdatedState

LaunchedEffect一旦启动,同一个key其内部的方法调用和引用都是final的,即无法更改,如果LaunchedEffect内使用的外部引用可能发生改变,应该使用rememberUpdatedState

3.1 不使用remember

先来看一个例子,我在重组时生成一个随机数,并作为onTimeout()的打印参数,将onTimeout()传给MyRememberUpdatedStateLaunchedEffect内调用onTimeout()打印这个随机数:

  1. @Preview
  2. @Composable
  3. fun MyTimeout() {
  4. var state by remember { mutableStateOf(false) }
  5. Column {
  6. // 1.生成随机数
  7. val random = Random.nextInt()
  8. Log.i("onTimeout", "return : $random")
  9. MyRememberUpdatedState(state) {
  10. // 4.打印随机数
  11. Log.i("onTimeout", "onTimeout() return : $random")
  12. }
  13. Button(onClick = { state = !state }) {
  14. Text("click")
  15. }
  16. }
  17. }
  18. @Composable
  19. fun MyRememberUpdatedState(enable: Boolean, onTimeout: () -> Unit) {
  20. // 使用rememberUpdatedState
  21. // val rememberUpdatedState by rememberUpdatedState(onTimeout)
  22. val rememberUpdatedState = onTimeout
  23. // 2.key唯一发生重组,不会重新执行
  24. LaunchedEffect(true) {
  25. delay(5000)
  26. // 3.延迟5s,调用外部传入的onTimeout()
  27. rememberUpdatedState()
  28. }
  29. if (enable)
  30. Text("hi")
  31. else
  32. Text("hello")
  33. }

我点击多次,这次的效果直接看日志即可:

可以看到最后打印的结果,是第一次生成的随机数

3.2 使用remember

我们尝试使用remember,将onTimeout作为State状态并记住,并以onTimeout作为key使得每次onTimeout发生改变,触发值的更新:

  1. @Preview
  2. @Composable
  3. fun MyTimeout() {
  4. var state by remember { mutableStateOf(false) }
  5. Column {
  6. // 1.生成随机数
  7. val random = Random.nextInt()
  8. Log.i("onTimeout", "return : $random")
  9. MyRememberUpdatedState(state) {
  10. // 4.打印随机数
  11. Log.i("onTimeout", "onTimeout() return : $random")
  12. }
  13. Button(onClick = { state = !state }) {
  14. Text("click")
  15. }
  16. }
  17. }
  18. @Composable
  19. fun MyRememberUpdatedState(enable: Boolean, onTimeout: () -> Unit) {
  20. // 使用rememberUpdatedState
  21. // val rememberUpdatedState by rememberUpdatedState(onTimeout)
  22. val rememberUpdatedState by remember(onTimeout) { mutableStateOf(onTimeout) }
  23. // val rememberUpdatedState = onTimeout
  24. // 2.key唯一发生重组,不会重新执行
  25. LaunchedEffect(true) {
  26. delay(5000)
  27. // 3.延迟5s,调用外部传入的onTimeout()
  28. rememberUpdatedState()
  29. }
  30. if (enable)
  31. Text("hi")
  32. else
  33. Text("hello")
  34. }

打印的结果,依然是第一次生成的随机数:

3.3 使用rememberUpdatedState

rememberUpdatedState可以始终保持最新的值,从而改变LaunchedEffect运行时的引用的值

  1. @Preview
  2. @Composable
  3. fun MyTimeout() {
  4. var state by remember { mutableStateOf(false) }
  5. Column {
  6. // 1.生成随机数
  7. val random = Random.nextInt()
  8. Log.i("onTimeout", "return : $random")
  9. MyRememberUpdatedState(state) {
  10. // 4.打印随机数
  11. Log.i("onTimeout", "onTimeout() return : $random")
  12. }
  13. Button(onClick = { state = !state }) {
  14. Text("click")
  15. }
  16. }
  17. }
  18. @Composable
  19. fun MyRememberUpdatedState(enable: Boolean, onTimeout: () -> Unit) {
  20. // 使用rememberUpdatedState
  21. val rememberUpdatedState by rememberUpdatedState(onTimeout)
  22. // val rememberUpdatedState by remember{ mutableStateOf(onTimeout) }
  23. // val rememberUpdatedState = onTimeout
  24. // 2.key唯一发生重组,不会重新执行
  25. LaunchedEffect(true) {
  26. delay(5000)
  27. // 3.延迟5s,调用外部传入的onTimeout()
  28. rememberUpdatedState()
  29. }
  30. if (enable)
  31. Text("hi")
  32. else
  33. Text("hello")
  34. }

打印结果:

原理:首先我们知道remember相当于创建了一个静态变量,如果不指定key,只会初始化一次,重复调用remember并不会更新引用,指定key时,当key发生变化,则会更新引用LaunchedEffect运行时会复制引用,新建变量指向传入的引用,所以此时无论外部变量的引用发生如何改变,并不会改变LaunchedEffect内部变量的引用rememberUpdatedStateremember的基础上做了更新值处理,每次调用到rememberUpdatedState时,将值更新,也就是引用的值的更新,此时不管外部变量还是LaunchedEffect内部变量的值引用都会发生变化,LaunchedEffect调用的自然就是最新的方法了,下面是rememberUpdatedState的源码:

  1. @Composable
  2. fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
  3. mutableStateOf(newValue)
  4. }.apply { value = newValue }

4.DisposableEffect

DisposableEffect可以在key变化和移除时做一些善后工作,需实现onDispose

例子:

  1. @Preview
  2. @Composable
  3. fun MyDisposableEffect() {
  4. var state by remember { mutableStateOf(false) }
  5. var text by remember { mutableStateOf("click") }
  6. val scope = rememberCoroutineScope()
  7. if (state) {
  8. // 重组或移除时会调用onDispose
  9. DisposableEffect(Unit) {
  10. val job = scope.launch {
  11. delay(3000)
  12. text = "点了"
  13. }
  14. onDispose {
  15. job.cancel()
  16. text = "取消了"
  17. }
  18. }
  19. }
  20. Button(onClick = { state = !state }) {
  21. Text(text)
  22. }
  23. }

效果,在3s内点击了两次,导致重组时移除DisposableEffect而触发onDispose

5.SideEffect

SideEffect会在可组合函数重组完成时调用,可以进行用户行为分析、日志记录等操作

例子:

  1. @OptIn(ExperimentalAnimationApi::class)
  2. @Preview
  3. @Composable
  4. fun MySideEffect() {
  5. var enable by remember { mutableStateOf(false) }
  6. Column {
  7. AnimatedVisibility(
  8. visible = enable,
  9. enter = scaleIn(tween(2000)),
  10. exit = scaleOut(tween(2000))
  11. ) {
  12. MySideEffectText("hello world")
  13. }
  14. Button(onClick = { enable = !enable }) {
  15. Text("click")
  16. }
  17. }
  18. }
  19. @Composable
  20. fun MySideEffectText(text: String) {
  21. SideEffect {
  22. Log.i("SideEffect", "重组完成")
  23. }
  24. Text(text)
  25. }

效果,如果组件重组完成了,连续点击导致动画重复执行,则不会触发重组:

6.produceState

produceState 会启动一个协程,并返回一个State对象,用来将非 Compose 状态转换为 Compose 状态,即执行一些耗时操作,如网络请求,并将结果作为State对象返回

例子:

  1. @Preview
  2. @Composable
  3. fun MyProduceState() {
  4. var visiable by remember { mutableStateOf(false) }
  5. Column {
  6. if (visiable)
  7. Text(load().value)
  8. Button(onClick = { visiable = !visiable }) {
  9. Text("load")
  10. }
  11. }
  12. }
  13. @Composable
  14. fun load(): State<String> {
  15. return produceState(initialValue = "", producer = {
  16. delay(2000);
  17. value = "hi"
  18. })
  19. }

效果:

7.derivedStateOf

derivedStateOf可以将一个或多个状态对象转变为其他的状态对象,一旦状态发生改变,只会在用到该derivedStateOf状态的地方进行重组

例子,根据传入的list,过滤高亮的元素,并展示到列表中:

  1. val alpha = arrayOf("a", "b", "c", "d", "e", "f", "g", "h")
  2. @Preview
  3. @Composable
  4. fun MyDerivedStateOf() {
  5. val items = remember { mutableStateListOf<String>() }
  6. Column {
  7. Button(onClick = { items.add(alpha[Random.nextInt(alpha.size)]) }) {
  8. Text("Add")
  9. }
  10. DerivedStateOf(items, highPriorityKeywords = listOf("a", "b"))
  11. }
  12. }
  13. /**
  14. * 拥有highPriorityKeywords的优先显示
  15. */
  16. @Composable
  17. fun DerivedStateOf(
  18. lists: List<String>,
  19. highPriorityKeywords: List<String> = listOf("Review", "Unblock", "Compose")
  20. ) {
  21. // 需要高亮置顶的items
  22. val highPriorityLists by remember(highPriorityKeywords) {
  23. derivedStateOf { lists.filter { it in highPriorityKeywords } }
  24. }
  25. LazyColumn {
  26. items(highPriorityLists) { value ->
  27. Text(value, color = Color.Red)
  28. }
  29. items(lists) { value ->
  30. Text(value)
  31. }
  32. }
  33. }

效果:

8.snapshotFlow

snapshotFlow可以将 ComposeState 转为Flow,当在 snapshotFlow 块中读取的 State 对象之一发生变化时,如果新值与之前发出的值不相等,Flow 会向其收集器发出新值

  1. @Preview
  2. @Composable
  3. fun MySnapshotFlow() {
  4. val listState = rememberLazyListState()
  5. val list = remember {
  6. mutableListOf<Int>().apply {
  7. repeat(1000) { index ->
  8. this += index
  9. }
  10. }
  11. }
  12. LazyColumn(state = listState) {
  13. items(list) {
  14. Text("hi:${it}")
  15. }
  16. }
  17. LaunchedEffect(Unit) {
  18. snapshotFlow {
  19. listState.firstVisibleItemIndex
  20. }.collect { index ->
  21. Log.i("collect", "${index}")
  22. }
  23. }
  24. }

滚动查看日志:

9.重启效应

Compose 中有一些效应(如 LaunchedEffectproduceStateDisposableEffect)会采用可变数量的参数和键来取消运行效应,并使用新的键启动一个新的效应。在实际开发中,灵活运用key是否唯一来使得是否需要重启效应

二、传统项目集成

官方推荐一次性替换整个布局,也可以替换部分布局,本身compose就兼容传统xml的方式,所以在传统的项目上集成compose很容易

1.xml中使用compose

xml中使用ComposeView,表示一个加载compose的控件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".ComposeIntegrateActivity">
  8. <TextView
  9. android:id="@+id/textView2"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:text="hello android"
  13. app:layout_constraintBottom_toBottomOf="parent"
  14. app:layout_constraintEnd_toEndOf="parent"
  15. app:layout_constraintStart_toStartOf="parent"
  16. app:layout_constraintTop_toTopOf="parent" />
  17. <androidx.compose.ui.platform.ComposeView
  18. android:id="@+id/composeView"
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. app:layout_constraintEnd_toEndOf="parent"
  22. app:layout_constraintStart_toStartOf="parent"
  23. app:layout_constraintTop_toBottomOf="@id/textView2" />
  24. </androidx.constraintlayout.widget.ConstraintLayout>

Activity中调用ComposeViewsetContent()方法,并使用compose:

  1. class ComposeIntegrateActivity : ComponentActivity() {
  2. override fun onCreate(savedInstanceState: Bundle?) {
  3. super.onCreate(savedInstanceState)
  4. setContentView(R.layout.activity_compose_integrate)
  5. val composeView = findViewById<ComposeView>(R.id.composeView)
  6. composeView.setContent {
  7. MyComposeApplicationTheme {
  8. MyText1()
  9. }
  10. }
  11. }
  12. @Composable
  13. fun MyText1() {
  14. Text("hi compose")
  15. }
  16. }

启动效果:

2.fragment中使用

fragment中要多一步绑定View树生命周期:

  1. class BlankFragment : Fragment() {
  2. override fun onCreateView(
  3. inflater: LayoutInflater, container: ViewGroup?,
  4. savedInstanceState: Bundle?
  5. ): View? {
  6. val disposeOnViewTreeLifecycleDestroyed =
  7. ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
  8. val root = inflater.inflate(R.layout.fragment_blank, container, false)
  9. root.findViewById<ComposeView>(R.id.fragment_composeView).apply {
  10. setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
  11. setContent {
  12. MaterialTheme() {
  13. // In Compose world
  14. Text("Hello Compose!")
  15. }
  16. }
  17. }
  18. return root
  19. }
  20. }

三、导航

compose定义了全新的导航API,下面来开始使用它

1.导入依赖

  1. def nav_version = "2.5.3"
  2. implementation "androidx.navigation:navigation-compose:$nav_version"

2.创建 NavHost

NavHost需要一个navController用于控制导航到那个可组合项,startDestination 初始的可组合项,以及NavGraphBuilder导航关系图

  1. class NaviActivity : ComponentActivity() {
  2. override fun onCreate(savedInstanceState: Bundle?) {
  3. super.onCreate(savedInstanceState)
  4. setContent {
  5. MyComposeApplicationTheme {
  6. MyNavi()
  7. }
  8. }
  9. }
  10. }
  11. @Preview
  12. @Composable
  13. fun MyNavi() {
  14. val navController = rememberNavController()
  15. NavHost(navController = navController, startDestination = "home") {
  16. composable("home") { Home() }
  17. composable("message") { Message() }
  18. composable("mine") { Mine() }
  19. }
  20. }
  21. @Composable
  22. fun Home() {
  23. Box(
  24. modifier = Modifier.fillMaxSize(),
  25. contentAlignment = Alignment.Center
  26. ) {
  27. Text("Home")
  28. }
  29. }
  30. @Composable
  31. fun Message() {
  32. Box(
  33. modifier = Modifier.fillMaxSize(),
  34. contentAlignment = Alignment.Center
  35. ) {
  36. Text("Message")
  37. }
  38. }
  39. @Composable
  40. fun Mine() {
  41. Box(
  42. modifier = Modifier.fillMaxSize(),
  43. contentAlignment = Alignment.Center
  44. ) {
  45. Text("Mine")
  46. }
  47. }

效果:

3.navController

接下来使用navController来导航到不同的可组合项,下面是官方给出的示例的几种方式:

  • 在导航到“friendslist”并加到返回堆栈中
navController.navigate("friendslist")
  • 在导航到“friendslist”之前,将所有内容从后堆栈中弹出到“home”(不包含home)
  1. navController.navigate("friendslist") {
  2. popUpTo("home")
  3. }
  • 在导航到“friendslist”之前,从堆栈中弹出所有内容,包括“home”
  1. navController.navigate("friendslist") {
  2. popUpTo("home") { inclusive = true }
  3. }
  • 只有当我们还不在“search”时,才能导航到“search”目标地,避免在后堆栈的顶部有多个副本
  1. navController.navigate("search") {
  2. launchSingleTop = true
  3. }

例子:

我们给App添加上Scaffold,并在底部导航栏进行navController导航的控制

  1. class NaviActivity : ComponentActivity() {
  2. override fun onCreate(savedInstanceState: Bundle?) {
  3. super.onCreate(savedInstanceState)
  4. setContent {
  5. MyComposeApplicationTheme {
  6. Scene()
  7. }
  8. }
  9. }
  10. }
  11. @OptIn(ExperimentalMaterial3Api::class)
  12. @Composable
  13. fun Scene() {
  14. val navController = rememberNavController()
  15. Surface(Modifier.background(MaterialTheme.colorScheme.surface)) {
  16. Scaffold(
  17. topBar = {
  18. TopAppBar(
  19. title = {
  20. Text(
  21. stringResource(id = R.string.app_name),
  22. color = MaterialTheme.colorScheme.onPrimaryContainer
  23. )
  24. },
  25. colors = TopAppBarDefaults.smallTopAppBarColors(
  26. containerColor = MaterialTheme.colorScheme.primaryContainer
  27. )
  28. )
  29. },
  30. bottomBar = {
  31. Row(
  32. modifier = Modifier
  33. .fillMaxWidth()
  34. .background(MaterialTheme.colorScheme.primaryContainer)
  35. .padding(10.dp),
  36. horizontalArrangement = Arrangement.SpaceAround
  37. ) {
  38. CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onPrimaryContainer) {
  39. Icon(
  40. Icons.Rounded.Home, contentDescription = null,
  41. modifier = Modifier.clickable {
  42. navController.navigate("home") {
  43. launchSingleTop = true
  44. popUpTo("home")
  45. }
  46. }
  47. )
  48. Icon(
  49. Icons.Rounded.Email, contentDescription = null,
  50. modifier = Modifier.clickable {
  51. navController.navigate("message") {
  52. launchSingleTop = true
  53. popUpTo("message")
  54. }
  55. }
  56. )
  57. Icon(
  58. Icons.Rounded.Face, contentDescription = null,
  59. modifier = Modifier.clickable {
  60. navController.navigate("mine") {
  61. launchSingleTop = true
  62. popUpTo("mine")
  63. }
  64. }
  65. )
  66. }
  67. }
  68. }
  69. ) { paddings ->
  70. MyNavi(
  71. modifier = Modifier.padding(paddings),
  72. navController = navController,
  73. startDestination = "home"
  74. ) {
  75. composable("home") { Home() }
  76. composable("message") { Message() }
  77. composable("mine") { Mine() }
  78. }
  79. }
  80. }
  81. }
  82. @Composable
  83. fun MyNavi(
  84. modifier: Modifier = Modifier,
  85. navController: NavHostController,
  86. startDestination: String,
  87. builder: NavGraphBuilder.() -> Unit
  88. ) {
  89. NavHost(
  90. modifier = modifier,
  91. navController = navController,
  92. startDestination = startDestination
  93. ) {
  94. builder()
  95. }
  96. }
  97. @Composable
  98. fun Home() {
  99. Box(
  100. modifier = Modifier.fillMaxSize(),
  101. contentAlignment = Alignment.Center
  102. ) {
  103. Text("Home")
  104. }
  105. }
  106. @Composable
  107. fun Message() {
  108. Box(
  109. modifier = Modifier.fillMaxSize(),
  110. contentAlignment = Alignment.Center
  111. ) {
  112. Text("Message")
  113. }
  114. }
  115. @Composable
  116. fun Mine() {
  117. Box(
  118. modifier = Modifier.fillMaxSize(),
  119. contentAlignment = Alignment.Center
  120. ) {
  121. Text("Mine")
  122. }
  123. }

效果:

4.参数传递

Navigation Compose 还支持在可组合项目的地之间传递参数,方式为Restful风格,这种风格的参数为必填:

  1. MyNavi(
  2. modifier = Modifier.padding(paddings),
  3. navController = navController,
  4. startDestination = "home/b1254"
  5. ) {
  6. composable("home/{userId}") { Home() }
  7. composable("message/{count}") { Message() }
  8. composable("mine/{userId}") { Mine() }
  9. }
  10. ...
  11. // 导航时带入参数
  12. navController.navigate("mine/a1587")

参数类型默认为字符串,也可以通过navArgument指定参数的类型:

  1. composable(
  2. "home/{userId}",
  3. arguments = listOf(navArgument("userId") { type = NavType.StringType })
  4. ) { Home() }

通过 lambda 中提供的NavBackStackEntry中提取这些参数:

  1. composable(
  2. "home/{userId}",
  3. arguments = listOf(navArgument("userId") { type = NavType.StringType })
  4. ) {navBackStackEntry ->
  5. navBackStackEntry.arguments?.getString("userId")
  6. Home()
  7. }

可选参数可以使用:?argName={argName} 来添加:

  1. composable(
  2. "message?count={count}",
  3. arguments = listOf(navArgument("count") {
  4. type = NavType.IntType
  5. defaultValue = 0
  6. })
  7. ) { Message() }

5.深层链接

深层链接照搬了官方文档:深层链接

如果你想要将特定的网址、操作或 MIME 类型与导航绑定,实现对外提供跳转应用的功能,那么使用深层链接可以很方便的实现这个功能

url为例,通过deepLinksurl进行绑定:

  1. val uri = "https://www.example.com"
  2. composable(
  3. "profile?id={id}",
  4. deepLinks = listOf(navDeepLink { uriPattern = "$uri/{id}" })
  5. ) { backStackEntry ->
  6. Profile(navController, backStackEntry.arguments?.getString("id"))
  7. }

manifest中注册配置:

  1. <activity …>
  2. <intent-filter>
  3. ...
  4. <data android:scheme="https" android:host="www.example.com" />
  5. </intent-filter>
  6. </activity>

外部通过PendingIntent进行跳转:

  1. val id = "exampleId"
  2. val context = LocalContext.current
  3. val deepLinkIntent = Intent(
  4. Intent.ACTION_VIEW,
  5. "https://www.example.com/$id".toUri(),
  6. context,
  7. MyActivity::class.java
  8. )
  9. val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
  10. addNextIntentWithParentStack(deepLinkIntent)
  11. getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
  12. }

6.封装导航图

随着业务的越来越复杂,导航图也可能分为模块化,可以在NavHost作用域中使用navigation进行封装:

  1. NavHost(navController, startDestination = "home") {
  2. ...
  3. // Navigating to the graph via its route ('login') automatically
  4. // navigates to the graph's start destination - 'username'
  5. // therefore encapsulating the graph's internal routing logic
  6. navigation(startDestination = "username", route = "login") {
  7. composable("username") { ... }
  8. composable("password") { ... }
  9. composable("registration") { ... }
  10. }
  11. ...
  12. }

使用扩展函数将更好的对模块进行封装:

  1. fun NavGraphBuilder.loginGraph(navController: NavController) {
  2. navigation(startDestination = "username", route = "login") {
  3. composable("username") { ... }
  4. composable("password") { ... }
  5. composable("registration") { ... }
  6. }
  7. }
  1. NavHost(navController, startDestination = "home") {
  2. ...
  3. loginGraph(navController)
  4. ...
  5. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/254128
推荐阅读
相关标签
  

闽ICP备14008679号