如何实现覆盖在底部导航栏上且使导航栏背景产生凹陷效果的购物车图标
如何实现覆盖在底部导航栏上且使导航栏背景产生凹陷效果的购物车图标
嘿,我看你现在的问题是购物车图标只是浮在底部导航栏上面,但没有像示例那样让导航栏背景产生凹陷的视觉效果对吧?其实核心问题在于你之前的思路是把图标做成独立的悬浮组件,但示例里的效果是让底部导航栏的背景本身就带有适配图标的凹陷形状,然后把购物车图标刚好嵌在这个凹陷里,这样看起来就像是图标把导航栏“压弯”了。
第一步:自定义带凹陷的背景形状
我们需要用Jetpack Compose的Shape来自定义底部导航栏的背景,让它顶部中央有一个和购物车图标大小匹配的圆形凹陷。代码如下:
class NotchedBottomNavShape(private val notchRadius: Dp) : Shape { override fun createOutline( size: Size, layoutDirection: LayoutDirection, density: Density ): Outline { val notchRadiusPx = with(density) { notchRadius.toPx() } return Outline.Generic( path = Path().apply { // 先绘制底部导航栏的矩形主体 addRect( Rect( left = 0f, top = 0f, right = size.width, bottom = size.height ) ) // 在顶部中央减去一个圆形,形成凹陷 addOval( Rect( left = (size.width / 2) - notchRadiusPx, top = -notchRadiusPx / 2, // 让半圆一半在导航栏外,一半在内 right = (size.width / 2) + notchRadiusPx, bottom = notchRadiusPx / 2 ) ) // 设置路径运算为差集,这样就得到带凹陷的形状 op(PathOperation.Difference) } ) } }
第二步:修改底部导航栏的实现
接下来调整你的BottomNavigationBar组件,用自定义的形状作为背景,然后在中央位置添加购物车图标,让它向上偏移刚好嵌入凹陷:
@Composable fun BottomNavigationBar(navController: NavController) { // 定义凹陷半径,要和购物车图标的大小适配 val notchRadius = 24.dp val iconSize = 48.dp // 购物车图标整体大小,要比凹陷半径大一点 BottomNavigation( backgroundColor = Color.White, modifier = Modifier .fillMaxWidth() // 使用自定义的带凹陷形状 .clip(NotchedBottomNavShape(notchRadius)) .border( width = 1.dp, color = Color.Gray, shape = NotchedBottomNavShape(notchRadius) ) .shadow( elevation = 4.dp, shape = NotchedBottomNavShape(notchRadius) ), // 去掉默认的内边距,避免影响凹陷位置的布局 contentPadding = PaddingValues(0.dp) ) { // 左侧导航项(比如首页、分类) BottomNavigationItem( icon = { Icon(Icons.Filled.Home, contentDescription = "Home") }, label = { Text("Home") }, selected = navController.currentDestination?.route == "home", onClick = { navController.navigate("home") }, // 调整导航项的位置,避免被中央凹陷影响 modifier = Modifier.weight(1f) ) // 中央的购物车图标区域,这里我们不使用BottomNavigationItem,而是自定义 Box( modifier = Modifier .weight(1f) .padding(top = -(iconSize / 2)) // 向上偏移,让图标一半在导航栏外,一半在内 .size(iconSize) .clip(CircleShape) .background(Color.White) .border(1.dp, Color.Gray, CircleShape) .clickable { navController.navigate("cart") }, contentAlignment = Alignment.Center ) { Icon( Icons.Filled.ShoppingCart, contentDescription = "Shopping Cart", modifier = Modifier.size(24.dp), tint = Color.Black ) } // 右侧导航项(比如我的、设置) BottomNavigationItem( icon = { Icon(Icons.Filled.Person, contentDescription = "Profile") }, label = { Text("Profile") }, selected = navController.currentDestination?.route == "profile", onClick = { navController.navigate("profile") }, modifier = Modifier.weight(1f) ) } }
关键细节说明
- 自定义
NotchedBottomNavShape的时候,要注意凹陷的位置和大小:我们让凹陷的一半在导航栏的顶部之外,这样视觉上图标是“嵌”在导航栏里的。 - 购物车图标的偏移量要刚好和凹陷的位置对应,这里用
padding(top = -(iconSize / 2))让图标向上偏移一半的大小,刚好和凹陷对齐。 - 导航项的
weight设置为1f,确保左右导航项和中央图标区域均匀分布宽度。
这样修改之后,你的底部导航栏背景就会有适配购物车图标的凹陷,图标也会刚好嵌在里面,视觉效果就和示例一致了!
备注:内容来源于stack exchange,提问作者Cem Oner




