如何在 Compose 中安排 BottomNavigationItems?
How to arrange BottomNavigationItems in Compose?
如何安排两个内部 BottomNav 项,使它们不那么靠近“+”FAB?
我尝试围绕显示带有行的项目的 forEach 并使用 Arrangement 修饰符,如下所示:
Row(horizontalArrangement = Arrangement.SpaceBetween) { //Not working :(
items.forEach { item ->
BottomNavigationItem(
icon = { Icon(painterResource(id = item.icon), contentDescription = item.title) },
label = { Text(text = item.title) },
selectedContentColor = Color.White,
unselectedContentColor = Color.White.copy(0.4f),
alwaysShowLabel = true,
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
不幸的是,这不起作用
您可以使用 BottomAppBar
并在中间给它 cutoutShape
和一个虚拟项目。它会给你想要的结果。
输出:
代码示例:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
BottomBarWithFabDem()
}
}
}
}
}
val items = listOf(
Screen.PickUp,
Screen.Explore,
Screen.Camera,
Screen.Favorites,
Screen.Profile
)
sealed class Screen(val route: String?, val title: String?, val icon: ImageVector?) {
object PickUp : Screen("pickup", "PickUp", Icons.Default.ShoppingCart)
object Explore : Screen("explore", "Explore", Icons.Default.Info)
object Camera : Screen("camera", null, null)
object Favorites : Screen("favorites", "Fav", Icons.Default.Favorite)
object Profile : Screen("profile", "Profile", Icons.Default.Person)
}
@Composable
fun BottomBarWithFabDem() {
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNav(navController)
},
floatingActionButtonPosition = FabPosition.Center,
isFloatingActionButtonDocked = true,
floatingActionButton = {
FloatingActionButton(
shape = CircleShape,
onClick = {
Screen.Camera.route?.let {
navController.navigate(it) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
Screen.Camera.route?.let { navController.navigate(it) }
},
contentColor = Color.White
) {
Icon(imageVector = Icons.Filled.Add, contentDescription = "Add icon")
}
}
) {
MainScreenNavigation(navController)
}
}
@Composable
fun MainScreenNavigation(navController: NavHostController) {
NavHost(navController, startDestination = Screen.Profile.route!!) {
composable(Screen.Profile.route) {}
composable(Screen.Explore.route!!) {}
composable(Screen.Favorites.route!!) {}
composable(Screen.PickUp.route!!) {}
composable(Screen.Camera.route!!) {}
}
}
@Composable
fun BottomNav(navController: NavController) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination
BottomAppBar(cutoutShape = CircleShape, modifier = Modifier.height(64.dp)) {
Row {
items.forEachIndexed { index, it ->
if (index != 2) {
// Main item
BottomNavigationItem(
icon = {
it.icon?.let {
Icon(
imageVector = it,
contentDescription = "",
modifier = Modifier.size(35.dp),
tint = Color.White
)
}
},
label = {
it.title?.let {
Text(
text = it,
color = Color.White
)
}
},
selected = currentRoute?.hierarchy?.any { it.route == it.route } == true,
selectedContentColor = Color(R.color.purple_700),
unselectedContentColor = Color.White.copy(alpha = 0.4f),
onClick = {}
)
} else {
// placeholder for center fab
BottomNavigationItem(
icon = {},
label = { },
selected = false,
onClick = { },
enabled = false
)
}
}
}
}
}
Arrangement.SpaceBetween
按预期工作 - 它在项目之间添加了一个间隔符:
Place children such that they are spaced evenly across the main axis, without free space before the first child or after the last child. Visually: 1##2##3
您需要让您的 Row
知道 FAB 的位置。您可以在行的中间添加一个带 Modifier.weight
的间隔符,例如:
items.forEachIndexed { i, item ->
if (i == items.count() / 2) {
Spacer(Modifier.weight(1f))
}
BottomNavigationItem(
// ...
如何安排两个内部 BottomNav 项,使它们不那么靠近“+”FAB? 我尝试围绕显示带有行的项目的 forEach 并使用 Arrangement 修饰符,如下所示:
Row(horizontalArrangement = Arrangement.SpaceBetween) { //Not working :(
items.forEach { item ->
BottomNavigationItem(
icon = { Icon(painterResource(id = item.icon), contentDescription = item.title) },
label = { Text(text = item.title) },
selectedContentColor = Color.White,
unselectedContentColor = Color.White.copy(0.4f),
alwaysShowLabel = true,
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
不幸的是,这不起作用
您可以使用 BottomAppBar
并在中间给它 cutoutShape
和一个虚拟项目。它会给你想要的结果。
输出:
代码示例:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
BottomBarWithFabDem()
}
}
}
}
}
val items = listOf(
Screen.PickUp,
Screen.Explore,
Screen.Camera,
Screen.Favorites,
Screen.Profile
)
sealed class Screen(val route: String?, val title: String?, val icon: ImageVector?) {
object PickUp : Screen("pickup", "PickUp", Icons.Default.ShoppingCart)
object Explore : Screen("explore", "Explore", Icons.Default.Info)
object Camera : Screen("camera", null, null)
object Favorites : Screen("favorites", "Fav", Icons.Default.Favorite)
object Profile : Screen("profile", "Profile", Icons.Default.Person)
}
@Composable
fun BottomBarWithFabDem() {
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNav(navController)
},
floatingActionButtonPosition = FabPosition.Center,
isFloatingActionButtonDocked = true,
floatingActionButton = {
FloatingActionButton(
shape = CircleShape,
onClick = {
Screen.Camera.route?.let {
navController.navigate(it) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
Screen.Camera.route?.let { navController.navigate(it) }
},
contentColor = Color.White
) {
Icon(imageVector = Icons.Filled.Add, contentDescription = "Add icon")
}
}
) {
MainScreenNavigation(navController)
}
}
@Composable
fun MainScreenNavigation(navController: NavHostController) {
NavHost(navController, startDestination = Screen.Profile.route!!) {
composable(Screen.Profile.route) {}
composable(Screen.Explore.route!!) {}
composable(Screen.Favorites.route!!) {}
composable(Screen.PickUp.route!!) {}
composable(Screen.Camera.route!!) {}
}
}
@Composable
fun BottomNav(navController: NavController) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination
BottomAppBar(cutoutShape = CircleShape, modifier = Modifier.height(64.dp)) {
Row {
items.forEachIndexed { index, it ->
if (index != 2) {
// Main item
BottomNavigationItem(
icon = {
it.icon?.let {
Icon(
imageVector = it,
contentDescription = "",
modifier = Modifier.size(35.dp),
tint = Color.White
)
}
},
label = {
it.title?.let {
Text(
text = it,
color = Color.White
)
}
},
selected = currentRoute?.hierarchy?.any { it.route == it.route } == true,
selectedContentColor = Color(R.color.purple_700),
unselectedContentColor = Color.White.copy(alpha = 0.4f),
onClick = {}
)
} else {
// placeholder for center fab
BottomNavigationItem(
icon = {},
label = { },
selected = false,
onClick = { },
enabled = false
)
}
}
}
}
}
Arrangement.SpaceBetween
按预期工作 - 它在项目之间添加了一个间隔符:
Place children such that they are spaced evenly across the main axis, without free space before the first child or after the last child. Visually: 1##2##3
您需要让您的 Row
知道 FAB 的位置。您可以在行的中间添加一个带 Modifier.weight
的间隔符,例如:
items.forEachIndexed { i, item ->
if (i == items.count() / 2) {
Spacer(Modifier.weight(1f))
}
BottomNavigationItem(
// ...