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

cvxl0en2  于 2022-11-16  发布在  Kotlin
关注(0)|答案(2)|浏览(183)

我正在尝试构建我的底部导航如下:

@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)
                )
            }
        }
    }
}

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

bkhjykvo

bkhjykvo1#

我不认为你可以用BottomNavigationItem来完成它。合成的材料设计库遵循材料设计规范,正如你在这里看到的。
但是你不需要使用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)。

y4ekin9u

y4ekin9u2#

我设法在图标和标签之间添加了填充。事实上,当我开始尝试这个方法时,我是将填充应用于文本/标签而不是图标,但是如果我们将填充应用于图标,就可以应用该间距。只需在图标上添加一个Modifier.padding(bottom = YOURDP),它就可以工作了。
我认为这是可能的,因为在分析Material Design代码以应用Label with Icon之后,它应用了Label relative to Icon(你可以在下面的方法中看到),考虑到现有的空间,所以如果我们增加Icon占用的空间,它将使标签更低。

/**
 * 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)
    }
}

相关问题