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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 21:37:48