Jetpack Compose如何实现4个Composable组件的单选互斥效果?

需求说明
一行有4个Button或Composable组件,需要实现选中一个时自动取消其他选中状态的效果,外观不需要和原生单选按钮一致,但交互逻辑和单选按钮完全相同。
原代码核心问题
- 为每个按钮单独维护选中状态,需要手动实现互斥逻辑,冗余且容易出错
- 子组件内部额外维护了本地状态,和父组件传入的参数脱节,导致状态不同步
- 原有互斥判断逻辑存在分支优先级错误,无法实现预期效果
最优实现方案
核心思路为状态上移+受控组件:整组仅维护一个选中标识状态,所有子按钮的选中态直接和该标识对比判断,点击时直接更新该标识即可自动实现互斥效果。
@Composable fun ButtonSet(modifier: Modifier = Modifier) { // 整组只维护一个选中状态,0~3对应四个按钮,-1表示默认无选中 var selectedIndex by remember { mutableStateOf(-1) } Row( modifier = modifier .padding(start = 60.dp, end = 60.dp) .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly, ) { // 四个按钮,分别传入对应选中状态和点击回调 GoalkeeperButton( isSelected = selectedIndex == 0, onClick = { selectedIndex = 0 } ) GoalkeeperButton( isSelected = selectedIndex == 1, onClick = { selectedIndex = 1 } ) GoalkeeperButton( isSelected = selectedIndex == 2, onClick = { selectedIndex = 2 } ) GoalkeeperButton( isSelected = selectedIndex == 3, onClick = { selectedIndex = 3 } ) } } @Composable fun GoalkeeperButton( isSelected: Boolean, modifier: Modifier = Modifier, onClick: () -> Unit ) { Column( modifier = modifier .wrapContentWidth() .height(130.dp), horizontalAlignment = Alignment.CenterHorizontally ) { // 选中时才显示手套图标 if (isSelected) { Image( painter = painterResource(id = R.drawable.blue_hands), contentDescription = null, contentScale = ContentScale.FillBounds, modifier = Modifier.size(width = 135.dp, height = 70.dp) ) } // 根据选中状态加载不同按钮资源 Image( painter = painterResource( id = if (isSelected) R.drawable.bttn_pressed else R.drawable.bttn ), contentDescription = null, contentScale = ContentScale.FillBounds, modifier = Modifier .clickable(onClick = onClick) .size(width = 107.dp, height = 65.dp) ) } }
优化点说明
- 代码量减少近一半,没有冗余的状态维护逻辑,单选逻辑天然生效
- 所有子组件都是受控组件,内部不维护私有状态,不会出现状态不同步问题
- 合并了重复的激活/未激活按钮组件,减少冗余代码
- 可扩展性强,后续需要新增按钮时仅需新增对应调用即可,不需要修改互斥逻辑
- 直接读取
selectedIndex即可获取当前选中的选项,不需要额外做状态判断
内容的提问来源于stack exchange,提问作者Ashish Gautam
相关产品推荐
相关产品推荐

