Jetpack Compose中实现MaterialButtonToggleGroup时如何去除按钮间的垂直重复边框?
嘿,我明白你遇到的问题了——用OutlinedButton组合成ToggleGroup时,相邻按钮的边框会叠在一起,出现两条边框(选中的蓝色和未选中的灰色同时显示),这确实和原生MaterialButtonToggleGroup的效果不一样。别担心,我给你两种可行的解决方案:
方案一:通过负偏移让按钮重叠(简单快速)
这个思路很直接:让每个按钮除了最左侧的那个,都向左偏移1dp,刚好覆盖左边按钮的右边框,这样中间位置只会显示顶部按钮的边框,完美解决重叠问题。
修改后的代码如下:
val cornerRadius = 8.dp Row( modifier = Modifier .fillMaxWidth() .padding(8.dp) ) { Spacer(modifier = Modifier.weight(1f)) items.forEachIndexed { index, item -> val buttonModifier = when (index) { // 第一个按钮不需要偏移 0 -> Modifier // 其他按钮向左偏移1dp,覆盖左侧按钮的右边框 else -> Modifier.offset(x = -1.dp) } OutlinedButton( onClick = { indexChanged(index) }, modifier = buttonModifier, shape = when (index) { 0 -> RoundedCornerShape(topStart = cornerRadius, topEnd = 0.dp, bottomStart = cornerRadius, bottomEnd = 0.dp) items.size - 1 -> RoundedCornerShape(topStart = 0.dp, topEnd = cornerRadius, bottomStart = 0.dp, bottomEnd = cornerRadius) else -> RoundedCornerShape(0.dp) }, border = BorderStroke(1.dp, if(selectedIndex == index) { MaterialTheme.colors.primary } else { Color.DarkGray.copy(alpha = 0.75f) }), colors = if(selectedIndex == index) { ButtonDefaults.outlinedButtonColors( backgroundColor = MaterialTheme.colors.primary.copy(alpha = 0.1f), contentColor = MaterialTheme.colors.primary ) } else { ButtonDefaults.outlinedButtonColors( backgroundColor = MaterialTheme.colors.surface, contentColor = MaterialTheme.colors.primary ) }, ) { Text( text = "Some text", color = if(selectedIndex == index) { MaterialTheme.colors.primary } else { Color.DarkGray.copy(alpha = 0.9f) }, modifier = Modifier.padding(horizontal = 8.dp) ) } } Spacer(modifier = Modifier.weight(1f)) }
这个方法的优点是改动极小,快速见效,而且能完美模拟原生组件的边框效果——选中按钮的边框会自然覆盖相邻未选中按钮的边框。
方案二:自定义边框绘制(更精细化控制)
如果你想要更精确地控制每一条边框的显示逻辑,可以放弃OutlinedButton自带的border参数,改用Modifier.drawBehind自定义绘制边框。这样你可以根据按钮的位置和选中状态,决定绘制哪些边。
示例代码如下:
val cornerRadius = 8.dp val borderWidth = 1.dp Row( modifier = Modifier .fillMaxWidth() .padding(8.dp) ) { Spacer(modifier = Modifier.weight(1f)) items.forEachIndexed { index, item -> val isSelected = selectedIndex == index val isFirst = index == 0 val isLast = index == items.size - 1 val showLeftBorder = isFirst || isSelected val showRightBorder = isLast || isSelected OutlinedButton( onClick = { indexChanged(index) }, shape = when (index) { 0 -> RoundedCornerShape(topStart = cornerRadius, topEnd = 0.dp, bottomStart = cornerRadius, bottomEnd = 0.dp) items.size - 1 -> RoundedCornerShape(topStart = 0.dp, topEnd = cornerRadius, bottomStart = 0.dp, bottomEnd = cornerRadius) else -> RoundedCornerShape(0.dp) }, // 去掉自带边框,我们自己绘制 border = BorderStroke(0.dp, Color.Transparent), colors = if(isSelected) { ButtonDefaults.outlinedButtonColors( backgroundColor = MaterialTheme.colors.primary.copy(alpha = 0.1f), contentColor = MaterialTheme.colors.primary ) } else { ButtonDefaults.outlinedButtonColors( backgroundColor = MaterialTheme.colors.surface, contentColor = MaterialTheme.colors.primary ) }, modifier = Modifier.drawBehind { val borderColor = if(isSelected) MaterialTheme.colors.primary else Color.DarkGray.copy(alpha = 0.75f) val halfBorder = borderWidth / 2 // 绘制上边框 drawLine( color = borderColor, start = Offset(0f, halfBorder.toPx()), end = Offset(size.width, halfBorder.toPx()), strokeWidth = borderWidth.toPx() ) // 绘制下边框 drawLine( color = borderColor, start = Offset(0f, size.height - halfBorder.toPx()), end = Offset(size.width, size.height - halfBorder.toPx()), strokeWidth = borderWidth.toPx() ) // 绘制左边框(仅当是第一个按钮或当前选中时) if(showLeftBorder) { drawLine( color = borderColor, start = Offset(halfBorder.toPx(), 0f), end = Offset(halfBorder.toPx(), size.height), strokeWidth = borderWidth.toPx() ) // 绘制左上角圆角 if(isFirst) { drawArc( color = borderColor, startAngle = 180f, sweepAngle = 90f, useCenter = false, topLeft = Offset(0f, 0f), size = Size(cornerRadius.toPx() * 2, cornerRadius.toPx() * 2), strokeWidth = borderWidth.toPx() ) } } // 绘制右边框(仅当是最后一个按钮或当前选中时) if(showRightBorder) { drawLine( color = borderColor, start = Offset(size.width - halfBorder.toPx(), 0f), end = Offset(size.width - halfBorder.toPx(), size.height), strokeWidth = borderWidth.toPx() ) // 绘制右上角圆角 if(isLast) { drawArc( color = borderColor, startAngle = 270f, sweepAngle = 90f, useCenter = false, topLeft = Offset(size.width - cornerRadius.toPx() * 2, 0f), size = Size(cornerRadius.toPx() * 2, cornerRadius.toPx() * 2), strokeWidth = borderWidth.toPx() ) } } } ) { Text( text = "Some text", color = if(isSelected) { MaterialTheme.colors.primary } else { Color.DarkGray.copy(alpha = 0.9f) }, modifier = Modifier.padding(horizontal = 8.dp) ) } } Spacer(modifier = Modifier.weight(1f)) }
这个方案的好处是你可以完全掌控每一条边框的显示逻辑,比如当中间按钮选中时,它的左右边框都会显示,而相邻未选中按钮的对应边框会隐藏,完全和原生MaterialButtonToggleGroup的行为一致。不过代码量会多一些,适合需要高度自定义的场景。
你可以根据自己的需求选择其中一种方案,第一种足够应对大多数情况,第二种适合需要更精细控制的场景。
内容的提问来源于stack exchange,提问作者LN-12
相关产品推荐
相关产品推荐

