You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中为BottomNavigationView设置itemActiveIndicatorStyle?

在Jetpack Compose中实现Material 3风格的胶囊形底部导航选中指示器

在Jetpack Compose的Material 3组件体系中,你可以通过自定义BottomNavigation的indicator参数来实现胶囊形选中指示器,以下是完整的可运行方案:

1. 定义底部导航项数据类

data class BottomNavItem(
    val title: String,
    val icon: ImageVector,
    val route: String
)

2. 完整实现带胶囊指示器的底部导航

@Composable
fun CapsuleIndicatorBottomNavigation() {
    val navItems = listOf(
        BottomNavItem("首页", Icons.Filled.Home, "home"),
        BottomNavItem("发现", Icons.Filled.Explore, "explore"),
        BottomNavItem("我的", Icons.Filled.Person, "profile")
    )
    var selectedItemIndex by remember { mutableIntStateOf(0) }
    // 存储每个导航项的位置边界
    val itemPositions = remember { mutableStateListOf<Rect>() }

    // 底部导航容器
    BottomNavigation(
        containerColor = MaterialTheme.colorScheme.surface,
        contentColor = MaterialTheme.colorScheme.onSurface
    ) {
        navItems.forEachIndexed { index, item ->
            BottomNavigationItem(
                icon = { Icon(item.icon, contentDescription = item.title) },
                label = { Text(item.title) },
                selected = selectedItemIndex == index,
                onClick = { selectedItemIndex = index },
                modifier = Modifier
                    .onGloballyPositioned { coordinates ->
                        // 记录当前项的位置信息
                        val bounds = coordinates.boundsInParent()
                        if (itemPositions.size > index) {
                            itemPositions[index] = bounds
                        } else {
                            itemPositions.add(bounds)
                        }
                    },
                // 隐藏默认指示器,使用自定义实现
                indicator = {}
            )
        }
    }

    // 自定义胶囊形指示器(带平滑动画)
    val selectedItemPosition = rememberDerivedStateOf {
        if (selectedItemIndex < itemPositions.size) itemPositions[selectedItemIndex] else Rect.Zero
    }
    val indicatorColor = MaterialTheme.colorScheme.primaryContainer
    val indicatorHeight = 40.dp
    val indicatorCornerRadius = 20.dp

    // 动画状态
    val targetOffsetX = selectedItemPosition.value.left.toDp()
    val targetWidth = selectedItemPosition.value.width.toDp()
    val animatedOffsetX by animateDpAsState(targetOffsetX, animationSpec = tween(300))
    val animatedWidth by animateDpAsState(targetWidth, animationSpec = tween(300))

    Box(
        modifier = Modifier
            .fillMaxWidth()
            .wrapContentHeight()
            .padding(horizontal = 16.dp)
    ) {
        Box(
            modifier = Modifier
                .offset(
                    x = animatedOffsetX,
                    y = (BottomNavigationDefaults.Height - indicatorHeight) / 2
                )
                .width(animatedWidth)
                .height(indicatorHeight)
                .background(
                    color = indicatorColor,
                    shape = RoundedCornerShape(indicatorCornerRadius)
                )
        )
    }
}

关键实现说明

  • 位置记录:通过Modifier.onGloballyPositioned获取每个导航项的坐标边界,存储到itemPositions中,用于定位指示器。
  • 自定义指示器:用Box绘制圆角矩形实现胶囊形状,通过offset属性定位到选中项的位置。
  • 平滑动画:使用animateDpAsState实现指示器切换时的位置和宽度过渡动画,符合Material 3的动效规范。
  • 样式自定义:可根据需求调整指示器的颜色(比如替换为MaterialTheme.colorScheme.primary)、高度、圆角大小,以及底部导航的内边距。

内容的提问来源于stack exchange,提问作者Dave Leo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 07:15:41