在 BottomNavigationItem 的图标和标签之间添加间距



我想建立我的底部导航像这样:

@Composable
fun BottomNavBar(navController: NavController) {
Column(
Modifier.background(colorResource(id = R.color.pastel_orange_white))
) {
BottomNavigation(
modifier = Modifier
.defaultMinSize(minHeight = 70.dp),
backgroundColor = colorResource(id = R.color.bottom_nav_dark)
) {
val navItems = arrayOf(
BottomNavItem(
stringResource(id = R.string.title_home),
R.drawable.ic_home,
Screen.Home.route
),
BottomNavItem(
stringResource(id = R.string.subjects_title),
R.drawable.ic_subject,
Screen.Subjects.route
),
BottomNavItem(
stringResource(id = R.string.grades_title),
R.drawable.ic_grade,
Screen.Grades.route
),
BottomNavItem(
"H/W",
R.drawable.ic_assignments,
Screen.Assignments.route
)
)
// observe the backstack
val navBackStackEntry by navController.currentBackStackEntryAsState()
// observe current route to change the icon
// color,label color when navigated
val currentRoute = navBackStackEntry?.destination?.route
navItems.forEach { navItem ->
BottomNavigationItem(
selected = currentRoute == navItem.route,
onClick = {
navController.navigate(navItem.route)
},
icon = {
Box(
Modifier
.width(70.dp)
.height(30.dp)
.background(
colorResource(id = if (currentRoute == navItem.route) R.color.bottom_nav_light else R.color.bottom_nav_dark),
RoundedCornerShape(32.dp)
),
contentAlignment = Alignment.Center
) {
Icon(
painter = painterResource(id = navItem.icon),
contentDescription = navItem.label,
tint = colorResource(id = R.color.black)
)
}
},
label = {
Text(text = navItem.label, fontSize = 14.sp)
},
alwaysShowLabel = true,
selectedContentColor = colorResource(id = R.color.black),
unselectedContentColor = colorResource(id = R.color.black)
)
}
}
}
}

我需要在标签和图标部分之间添加一些额外的空间,因为我要为选中的项目应用一个小的背景色。我尝试了填充,列安排等,但没有发现实际影响间距的东西。指针吗?

我不认为你可以使用BottomNavigationItem。你可以在这里看到,Compose的材质设计库遵循材质设计规范。

但是您不需要使用BottomNavigationItem来显示BottomAppBar中的项目。您可以使用任意组合,如下所示:

Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.weight(1f)
.clickable {
navController.navigate(navItem.route)
}) {
val color =
if (currentRoute == navItem.route) 
LocalContentColor.current 
else 
LocalContentColor.current.copy(alpha = ContentAlpha.medium)
Icon(
painter = painterResource(id = navItem.icon), 
navItem.label, 
tint = color
)
// Set the space that you want...
Spacer(modifier = Modifier.height(4.dp))
Text(text = navItem.label, color = color, fontSize = 12.sp)
}

然而,正如我上面提到的,库遵循材料设计规范,定义BottomAppBar的高度为56dp。因此,如果您想要更自定义的东西,您需要自己创建它(例如使用Row)。

为你的图标添加padding

items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(
modifier=Modifier.padding(6.dp), //add this line
painter = painterResource(id = item.icon),
tint = MaterialTheme.colorScheme.onSurface,
contentDescription = stringResource(id = item.title)
)
},
label = {
Text(
text = stringResource(id = item.title),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurface
)
},
selected = currentRoute == item.route,

)
}

我设法在图标和标签之间添加填充。事实上,当我最初尝试这个,我是应用填充到文本/标签,而不是图标,但如果我们应用填充到图标,它是可能应用的间距。只需添加一个Modifier.padding(bottom = YOURDP)到你的图标,它将工作。

我认为这是可能的,因为在分析了材料设计代码后,将标签与图标一起应用,它应用相对于图标的标签(你可以在下面的方法中看到),考虑到现有的空间,所以如果我们增加图标占用的空间,它会使标签更低。

/**
* Places the provided [labelPlaceable] and [iconPlaceable] in the correct position, depending on
* [iconPositionAnimationProgress].
*
* When [iconPositionAnimationProgress] is 0, [iconPlaceable] will be placed in the center, as with
* [placeIcon], and [labelPlaceable] will not be shown.
*
* When [iconPositionAnimationProgress] is 1, [iconPlaceable] will be placed near the top of item,
* and [labelPlaceable] will be placed at the bottom of the item, according to the spec.
*
* When [iconPositionAnimationProgress] is animating between these values, [iconPlaceable] will be
* placed at an interpolated position between its centered position and final resting position.
*
* @param labelPlaceable text label placeable inside this item
* @param iconPlaceable icon placeable inside this item
* @param constraints constraints of the item
* @param iconPositionAnimationProgress the progress of the icon position animation, where 0
* represents centered icon and no label, and 1 represents top aligned icon with label.
* Values between 0 and 1 interpolate the icon position so we can smoothly move the icon.
*/
private fun MeasureScope.placeLabelAndIcon(
labelPlaceable: Placeable,
iconPlaceable: Placeable,
constraints: Constraints,
/*@FloatRange(from = 0.0, to = 1.0)*/
iconPositionAnimationProgress: Float
): MeasureResult {
val height = constraints.maxHeight
// TODO: consider multiple lines of text here, not really supported by spec but we should
// have a better strategy than overlapping the icon and label
val baseline = labelPlaceable[LastBaseline]
val baselineOffset = CombinedItemTextBaseline.roundToPx()
// Label should be [baselineOffset] from the bottom
val labelY = height - baseline - baselineOffset
val unselectedIconY = (height - iconPlaceable.height) / 2
// Icon should be [baselineOffset] from the text baseline, which is itself
// [baselineOffset] from the bottom
val selectedIconY = height - (baselineOffset * 2) - iconPlaceable.height
val containerWidth = max(labelPlaceable.width, iconPlaceable.width)
val labelX = (containerWidth - labelPlaceable.width) / 2
val iconX = (containerWidth - iconPlaceable.width) / 2
// How far the icon needs to move between unselected and selected states
val iconDistance = unselectedIconY - selectedIconY
// When selected the icon is above the unselected position, so we will animate moving
// downwards from the selected state, so when progress is 1, the total distance is 0, and we
// are at the selected state.
val offset = (iconDistance * (1 - iconPositionAnimationProgress)).roundToInt()
return layout(containerWidth, height) {
if (iconPositionAnimationProgress != 0f) {
labelPlaceable.placeRelative(labelX, labelY + offset)
}
iconPlaceable.placeRelative(iconX, selectedIconY + offset)
}
}