使用 Compose Navigation 导航时 TopAppBar 闪烁

Flo*_*her 19 android android-jetpack android-jetpack-navigation android-jetpack-compose android-jetpack-compose-scaffold

我有 2 个屏幕,它们都有自己的ScaffoldTopAppBar。当我使用 Jetpack Navigation Compose 库在它们之间导航时,应用程序栏会闪烁。为什么会发生这种情况以及我该如何摆脱这种情况?

在此输入图像描述

代码:

导航:

@Composable
fun TodoNavHost(
    navController: NavHostController,
    modifier: Modifier = Modifier
) {
    NavHost(
        navController = navController,
        startDestination = TodoScreen.TodoList.name,
        modifier = modifier
    ) {
        composable(TodoScreen.TodoList.name) {
            TodoListScreen(
                onTodoEditClicked = { todo ->
                    navController.navigate("${TodoScreen.AddEditTodo.name}?todoId=${todo.id}")
                },
                onFabAddNewTodoClicked = {
                    navController.navigate(TodoScreen.AddEditTodo.name)
                }
            )
        }
        composable(
            "${TodoScreen.AddEditTodo.name}?todoId={todoId}", 
            arguments = listOf(
                navArgument("todoId") {
                    type = NavType.LongType
                    defaultValue = -1L
                }
            )
        ) {
            AddEditTodoScreen(
                onNavigateUp = {
                    navController.popBackStack() 
                },
                onNavigateBackWithResult = { result ->
                    navController.navigate(TodoScreen.TodoList.name)
                }
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

待办事项列表Scaffold屏幕TopAppBar

@Composable
fun TodoListBody(
    todos: List<Todo>,
    todoExpandedStates: Map<Long, Boolean>,
    onTodoItemClicked: (Todo) -> Unit,
    onTodoCheckedChanged: (Todo, Boolean) -> Unit,
    onTodoEditClicked: (Todo) -> Unit,
    onFabAddNewTodoClicked: () -> Unit,
    onDeleteAllCompletedConfirmed: () -> Unit,
    modifier: Modifier = Modifier,
    errorSnackbarMessage: String = "",
    errorSnackbarShown: Boolean = false
) {

    var menuExpanded by remember { mutableStateOf(false) }
    var showDeleteAllCompletedConfirmationDialog by rememberSaveable { mutableStateOf(false) }

    Scaffold(
        modifier,
        topBar = {
            TopAppBar(
                title = { Text("My Todos") },
                actions = {
                    IconButton(
                        onClick = { menuExpanded = !menuExpanded },
                        modifier = Modifier.semantics {
                            contentDescription = "Options Menu"
                        }
                    ) {
                        Icon(Icons.Default.MoreVert, contentDescription = "Show menu")
                    }
                    DropdownMenu(
                        expanded = menuExpanded,
                        onDismissRequest = { menuExpanded = false }) {
                        DropdownMenuItem(
                            onClick = {
                                showDeleteAllCompletedConfirmationDialog = true
                                menuExpanded = false
                            },
                            modifier = Modifier.semantics {
                                contentDescription = "Option Delete All Completed"
                            }) {
                            Text("Delete all completed")
                        }
                    }
                }

            )
        },
[...]
Run Code Online (Sandbox Code Playgroud)

添加/编辑Scaffold屏幕TopAppBar

@Composable
fun AddEditTodoBody(
    todo: Todo?,
    todoTitle: String,
    setTitle: (String) -> Unit,
    todoImportance: Boolean,
    setImportance: (Boolean) -> Unit,
    onSaveClick: () -> Unit,
    onNavigateUp: () -> Unit,
    modifier: Modifier = Modifier
) {
    Scaffold(
        modifier,
        topBar = {
            TopAppBar(
                title = { Text(todo?.let { "Edit Todo" } ?: "Add Todo") },
                actions = {
                    IconButton(onClick = onSaveClick) {
                        Icon(Icons.Default.Save, contentDescription = "Save Todo")
                    }
                },
                navigationIcon = {
                    IconButton(onClick = onNavigateUp) {
                        Icon(Icons.Default.ArrowBack, contentDescription = "Back")
                    }
                }
            )
        },
    ) { innerPadding ->
        BodyContent(
            todoTitle = todoTitle,
            setTitle = setTitle,
            todoImportance = todoImportance,
            setImportance = setImportance,
            modifier = Modifier.padding(innerPadding)
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

Flo*_*her 8

闪烁是由最新版本的库中默认的交叉淡入淡出动画navigation-compose引起的。现在摆脱它的唯一方法(不降低依赖关系)是使用Accompanist动画库:

implementation "com.google.accompanist:accompanist-navigation-animation:0.20.0"

然后NavHost用 Accompanist替换法线AnimatedNavHost,替换rememberNavController()rememberAnimatedNavController()禁用过渡动画:

AnimatedNavHost(
        navController = navController,
        startDestination = bottomNavDestinations[0].fullRoute,
        enterTransition = { _, _ -> EnterTransition.None },
        exitTransition = { _, _ -> ExitTransition.None },
        popEnterTransition = { _, _ -> EnterTransition.None },
        popExitTransition = { _, _ -> ExitTransition.None },
        modifier = modifier,
    ) {
        [...}
    }
Run Code Online (Sandbox Code Playgroud)


raf*_*kob 8

我想我找到了一个简单的解决方案来解决这个问题(适用于 Compose 版本 1.4.0)。

我的设置-闪烁

我的所有屏幕都有自己的工具栏,包裹在脚手架中:

// Some Composable screnn

Scaffold(
    topBar = { TopAppBar(...) }
) {
    ScreenContent()
}
Run Code Online (Sandbox Code Playgroud)

持有导航主机的主要活动定义如下:

// Activity with NavHost

setContent {
    AppTheme {
        NavHost(...) { }
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案——不眨眼!

将 NavHost 包裹在 Surface 中的活动中:

setContent {
    AppTheme {
        Surface {
            NavHost(...) { }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

其余屏幕保持不变。目的地之间没有闪烁和过渡动画几乎与片段相同(微妙的淡入/淡出)。到目前为止,我还没有发现任何负面影响。


Ric*_*per 0

这是预期的行为。您正在为两个屏幕构建两个单独的应用程序栏,因此它们必然会闪烁。这不是正确的方法。正确的方法是将脚手架实际放置在您的主要活动中,并将 NavHost 作为其内容放置。如果您想修改应用栏,请创建变量来保存状态。然后从可组合项修改它们。理想情况下,将其存储在视图模型中。这就是在 compose 中完成的。通过变量。

谢谢

  • 在旧的碎片时代,为单独的屏幕设置单独的工具栏始终是一个可行的选择。特别是当您需要不同类型的工具栏(例如普通工具栏和可扩展工具栏)时。是什么让您认为现在所有屏幕都应该有一个工具栏? (8认同)
  • 我不相信这个。每个应用程序只有一个 Scaffold/AppBar 似乎管理起来就像一场噩梦 (4认同)