如何在Kotlin Jetpack Compose中实现可复用递进式下拉菜单
实现可按需扩展的下拉菜单方案
核心思路
用可变状态列表保存已选项目,每次新增或修改选中项时自动过滤后续下拉菜单的选项(排除已选值),借助Jetpack Compose的重组机制自动更新UI。
完整代码实现
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material.DropdownMenuItem import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExposedDropdownMenuBox import androidx.compose.material.ExposedDropdownMenuDefaults import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @OptIn(ExperimentalMaterialApi::class) @Composable fun DynamicDropdowns() { // 原始选项列表 val options = rememberSaveable { listOf( "Song1", "Song2", "Song3", "Song4", "Song5", "Song6", "Song7", "Song8", "Song9", "Song10", "Song11", "Song12", "Song13", "Song14", "Song15", "Song16", "Song17" ) } // 保存已选项目的可变状态列表,修改时自动触发重组 val selectedItems = rememberSaveable { mutableStateListOf<String>() } Column(modifier = Modifier.padding(16.dp)) { // 渲染已选的每个下拉菜单 selectedItems.forEachIndexed { index, currentItem -> var expanded by remember { mutableStateOf(false) } // 当前下拉菜单的可用选项:保留当前选中项 + 排除其他已选值 val availableOptions = options.filter { it == currentItem || it !in selectedItems } ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { expanded = !expanded } ) { TextField( value = currentItem, onValueChange = {}, readOnly = true, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, modifier = Modifier.padding(bottom = 8.dp) ) ExposedDropdownMenu( expanded = expanded, onDismissRequest = { expanded = false } ) { availableOptions.forEach { option -> DropdownMenuItem( onClick = { selectedItems[index] = option expanded = false } ) { Text(text = option) } } } } } // 新增选择的下拉菜单(仅当还有未选选项时显示) val remainingOptions = options.filter { it !in selectedItems } if (remainingOptions.isNotEmpty()) { var expanded by remember { mutableStateOf(false) } var tempSelection by remember { mutableStateOf("") } ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { expanded = !expanded } ) { TextField( value = tempSelection.ifEmpty { "选择下一个歌曲" }, onValueChange = {}, readOnly = true, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, modifier = Modifier.padding(top = 8.dp) ) ExposedDropdownMenu( expanded = expanded, onDismissRequest = { expanded = false } ) { remainingOptions.forEach { option -> DropdownMenuItem( onClick = { tempSelection = option selectedItems.add(option) tempSelection = "" expanded = false } ) { Text(text = option) } } } } } else { Text(text = "所有歌曲已选择完毕") } } }
关键逻辑说明
- 状态管理:
mutableStateListOf<String>()是可观察集合,修改时自动触发Compose重组,更新整个下拉菜单列表。 - 选项过滤:
- 已存在的下拉菜单:保留当前选中项,同时排除其他已选值,支持用户修改已选内容。
- 新增下拉菜单:仅显示未被选中的选项,选中后将值加入
selectedItems,自动新增一个下拉菜单。
- UI交互:使用
ExposedDropdownMenuBox实现Material风格下拉菜单,符合Android设计规范。
内容的提问来源于stack exchange,提问作者The Quackiest
相关产品推荐
相关产品推荐

