使用 Compose Navigation 导航时 TopAppBar 闪烁
TopAppBar flashing when navigating with Compose Navigation
-
android
-
android-jetpack
-
android-jetpack-navigation
-
android-jetpack-compose
-
android-jetpack-compose-scaffold
我有 2 个屏幕,它们都有自己的 Scaffold
和 TopAppBar
。当我使用 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)
}
)
}
}
}
待办事项列表屏幕 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")
}
}
}
)
},
[...]
Add/edit 屏幕 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)
)
}
}
这是预期的行为。您正在为两个屏幕构建两个单独的应用程序栏,因此它们必然会闪烁。这不是正确的方法。正确的方法是实际将脚手架放在您的主 activity 中,并按其内容放置 NavHost。如果你想修改应用栏,创建变量来保存状态。然后从可组合项中修改它们。理想情况下,然后存储在视图模型中。这就是它在 compose 中的完成方式。通过变量。
谢谢
我在使用“每屏支架”架构时遇到了同样的问题。令我惊讶的是,有帮助的是将 androidx.navigation:navigation-compose
版本降低到 2.4.0-alpha04
。
闪烁是由 navigation-compose
库的较新版本中的 默认交叉淡入淡出动画 引起的。现在摆脱它的唯一方法(不降级依赖)是使用 Accompanist 动画库:
implementation "com.google.accompanist:accompanist-navigation-animation:0.20.0"
然后将正常的 NavHost
替换为伴奏者的 AnimatedNavHost
,将 rememberNavController()
替换为 rememberAnimatedNavController()
并禁用过渡动画:
AnimatedNavHost(
navController = navController,
startDestination = bottomNavDestinations[0].fullRoute,
enterTransition = { _, _ -> EnterTransition.None },
exitTransition = { _, _ -> ExitTransition.None },
popEnterTransition = { _, _ -> EnterTransition.None },
popExitTransition = { _, _ -> ExitTransition.None },
modifier = modifier,
) {
[...}
}
使用较新的库 implementation "com.google.accompanist:accompanist-navigation-animation:0.24.1-alpha"
你需要像这样 AnimatedNavHost
AnimatedNavHost(
navController = navController,
startDestination = BottomNavDestinations.TimerScreen.route,
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
popEnterTransition = { EnterTransition.None },
popExitTransition = { ExitTransition.None },
modifier = Modifier.padding(innerPadding)
还有
将rememberNavController()
替换为rememberAnimatedNavController()
将NavHost
替换为AnimatedNavHost
将import androidx.navigation.compose.navigation
替换为import com.google.accompanist.navigation.animation.navigation
将import androidx.navigation.compose.composable
替换为import com.google.accompanist.navigation.animation.composable
除了删除动画之外,您还可以更改动画,例如:
@Composable
private fun ScreenContent() {
val navController = rememberAnimatedNavController()
val springSpec = spring<IntOffset>(dampingRatio = Spring.DampingRatioMediumBouncy)
val tweenSpec = tween<IntOffset>(durationMillis = 2000, easing = CubicBezierEasing(0.08f, 0.93f, 0.68f, 1.27f))
...
) { innerPadding ->
AnimatedNavHost(
navController = navController,
startDestination = BottomNavDestinations.TimerScreen.route,
enterTransition = { slideInHorizontally(initialOffsetX = { 1000 }, animationSpec = springSpec) },
exitTransition = { slideOutHorizontally(targetOffsetX = { -1000 }, animationSpec = springSpec) },
popEnterTransition = { slideInHorizontally(initialOffsetX = { 1000 }, animationSpec = tweenSpec) },
popExitTransition = { slideOutHorizontally(targetOffsetX = { -1000 }, animationSpec = tweenSpec) },
modifier = Modifier.padding(innerPadding)
) {}
我想我找到了解决该问题的简单方法(适用于 Compose 版本 1.4.0)。
我的设置 - 闪烁
我的所有屏幕都有自己的工具栏包裹在脚手架中:
// Some Composable screnn
Scaffold(
topBar = { TopAppBar(...) }
) {
ScreenContent()
}
包含导航主机的主要 activity 定义如下:
// Activity with NavHost
setContent {
AppTheme {
NavHost(...) { }
}
}
解决方案 - 不闪烁!
将您的 NavHost 包裹在 activity 的 Surface 中:
setContent {
AppTheme {
Surface {
NavHost(...) { }
}
}
}
其余屏幕保持不变。目的地之间没有闪烁和过渡动画几乎与片段相同(微妙的淡入淡出in/fade)。到目前为止,我还没有发现任何负面影响。
android
android-jetpack
android-jetpack-navigation
android-jetpack-compose
android-jetpack-compose-scaffold
我有 2 个屏幕,它们都有自己的 Scaffold
和 TopAppBar
。当我使用 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)
}
)
}
}
}
待办事项列表屏幕 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")
}
}
}
)
},
[...]
Add/edit 屏幕 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)
)
}
}
这是预期的行为。您正在为两个屏幕构建两个单独的应用程序栏,因此它们必然会闪烁。这不是正确的方法。正确的方法是实际将脚手架放在您的主 activity 中,并按其内容放置 NavHost。如果你想修改应用栏,创建变量来保存状态。然后从可组合项中修改它们。理想情况下,然后存储在视图模型中。这就是它在 compose 中的完成方式。通过变量。
谢谢
我在使用“每屏支架”架构时遇到了同样的问题。令我惊讶的是,有帮助的是将 androidx.navigation:navigation-compose
版本降低到 2.4.0-alpha04
。
闪烁是由 navigation-compose
库的较新版本中的 默认交叉淡入淡出动画 引起的。现在摆脱它的唯一方法(不降级依赖)是使用 Accompanist 动画库:
implementation "com.google.accompanist:accompanist-navigation-animation:0.20.0"
然后将正常的 NavHost
替换为伴奏者的 AnimatedNavHost
,将 rememberNavController()
替换为 rememberAnimatedNavController()
并禁用过渡动画:
AnimatedNavHost(
navController = navController,
startDestination = bottomNavDestinations[0].fullRoute,
enterTransition = { _, _ -> EnterTransition.None },
exitTransition = { _, _ -> ExitTransition.None },
popEnterTransition = { _, _ -> EnterTransition.None },
popExitTransition = { _, _ -> ExitTransition.None },
modifier = modifier,
) {
[...}
}
使用较新的库 implementation "com.google.accompanist:accompanist-navigation-animation:0.24.1-alpha"
你需要像这样 AnimatedNavHost
AnimatedNavHost(
navController = navController,
startDestination = BottomNavDestinations.TimerScreen.route,
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
popEnterTransition = { EnterTransition.None },
popExitTransition = { ExitTransition.None },
modifier = Modifier.padding(innerPadding)
还有
将rememberNavController()
替换为rememberAnimatedNavController()
将NavHost
替换为AnimatedNavHost
将import androidx.navigation.compose.navigation
替换为import com.google.accompanist.navigation.animation.navigation
将import androidx.navigation.compose.composable
替换为import com.google.accompanist.navigation.animation.composable
除了删除动画之外,您还可以更改动画,例如:
@Composable
private fun ScreenContent() {
val navController = rememberAnimatedNavController()
val springSpec = spring<IntOffset>(dampingRatio = Spring.DampingRatioMediumBouncy)
val tweenSpec = tween<IntOffset>(durationMillis = 2000, easing = CubicBezierEasing(0.08f, 0.93f, 0.68f, 1.27f))
...
) { innerPadding ->
AnimatedNavHost(
navController = navController,
startDestination = BottomNavDestinations.TimerScreen.route,
enterTransition = { slideInHorizontally(initialOffsetX = { 1000 }, animationSpec = springSpec) },
exitTransition = { slideOutHorizontally(targetOffsetX = { -1000 }, animationSpec = springSpec) },
popEnterTransition = { slideInHorizontally(initialOffsetX = { 1000 }, animationSpec = tweenSpec) },
popExitTransition = { slideOutHorizontally(targetOffsetX = { -1000 }, animationSpec = tweenSpec) },
modifier = Modifier.padding(innerPadding)
) {}
我想我找到了解决该问题的简单方法(适用于 Compose 版本 1.4.0)。
我的设置 - 闪烁
我的所有屏幕都有自己的工具栏包裹在脚手架中:
// Some Composable screnn
Scaffold(
topBar = { TopAppBar(...) }
) {
ScreenContent()
}
包含导航主机的主要 activity 定义如下:
// Activity with NavHost
setContent {
AppTheme {
NavHost(...) { }
}
}
解决方案 - 不闪烁!
将您的 NavHost 包裹在 activity 的 Surface 中:
setContent {
AppTheme {
Surface {
NavHost(...) { }
}
}
}
其余屏幕保持不变。目的地之间没有闪烁和过渡动画几乎与片段相同(微妙的淡入淡出in/fade)。到目前为止,我还没有发现任何负面影响。