跳转到内容

OverlayIconDropdownMenu

OverlayIconDropdownMenu 是基于 IconButton 的封装,点击图标按钮后会展开 OverlayDropdownPopup。适用于工具栏的 action 槽位,例如 TopAppBar 的右侧动作按钮——单个图标按钮展开后呈现一组动作、排序选项或筛选开关。

使用前提

此组件依赖 Scaffold 提供的 COUIPopupHost 以显示弹出内容。必须在 Scaffold 中使用,否则弹出内容无法正常渲染。

引入

kotlin
import io.github.suqi8.coui.kmp.menu.OverlayIconDropdownMenu
import io.github.suqi8.coui.kmp.basic.DropdownEntry
import io.github.suqi8.coui.kmp.basic.DropdownItem

基本用法

OverlayIconDropdownMenu 放在 TopAppBar(或 SmallTopAppBar)的 actions 槽中,点击图标即可展开弹出框。这是最典型的使用场景——工具栏上的一个图标按钮,展开后呈现一组菜单项。

kotlin
val entry = DropdownEntry(
    items = listOf("Edit", "Duplicate", "Share", "Delete").map { text ->
        DropdownItem(text = text, onClick = { /* handle action */ })
    }
)

Scaffold(
    topBar = {
        SmallTopAppBar(
            title = "Inbox",
            actions = {
                OverlayIconDropdownMenu(entry = entry) {
                    Icon(imageVector = COUIIcons.Edit, contentDescription = "Action menu")
                }
            }
        )
    }
) { padding ->
    // page content
}

也可以将 OverlayIconDropdownMenu 用在 TopAppBar 之外的位置——只要外层位于 Scaffold 中,任何能放 IconButton 的地方都能放它。

排序 / 单选

对于排序菜单或单选场景,在每个 DropdownItem 上设置 selected,并保持 collapseOnSelection = true(entry 重载的默认值),让弹出框在每次选中后自动关闭。

kotlin
var sortIndex by remember { mutableStateOf(0) }
val entry = DropdownEntry(
    items = listOf("Name", "Date", "Size").mapIndexed { index, text ->
        DropdownItem(text = text, selected = sortIndex == index, onClick = { sortIndex = index })
    }
)

Scaffold {
    OverlayIconDropdownMenu(entry = entry) {
        Icon(imageVector = COUIIcons.Sort, contentDescription = "Sort")
    }
}

多选

用一个 Set 跟踪选中值,在每个条目的 onClick 中切换状态,并设置 collapseOnSelection = false 让弹出框在多次选择之间保持打开。

kotlin
var selected by remember { mutableStateOf(setOf("Photos")) }
val entry = DropdownEntry(
    items = listOf("Photos", "Videos", "Files").map { text ->
        DropdownItem(
            text = text,
            selected = text in selected,
            onClick = {
                selected = if (text in selected) selected - text else selected + text
            }
        )
    }
)

Scaffold {
    OverlayIconDropdownMenu(entry = entry, collapseOnSelection = false) {
        Icon(imageVector = COUIIcons.SelectAll, contentDescription = "Multiple selection")
    }
}

分组菜单

传入 entries: List<DropdownEntry> 即可显示由分割线隔开的多个分组。

kotlin
val entries = listOf(
    DropdownEntry(items = listOf("Item A-1", "Item A-2").map { DropdownItem(text = it) }),
    DropdownEntry(items = listOf("Item B-1", "Item B-2", "Item B-3").map { DropdownItem(text = it) })
)

Scaffold {
    OverlayIconDropdownMenu(entries = entries) {
        Icon(imageVector = COUIIcons.MoreCircle, contentDescription = "More")
    }
}

带图标与摘要的选项

每个 DropdownItem 都可以在文本前显示图标,并在文本下方显示一行摘要。icon lambda 会收到一个预设尺寸的 Modifier,应将其应用到图标组件上。

kotlin
val entry = DropdownEntry(
    items = listOf(
        DropdownItem(
            text = "Rename",
            summary = "Change the display name",
            icon = { modifier ->
                Icon(
                    modifier = modifier,
                    imageVector = COUIIcons.Rename,
                    contentDescription = null,
                )
            },
            onClick = { /* handle action */ },
        ),
        DropdownItem(text = "Delete", onClick = { /* handle action */ }),
    )
)

Scaffold {
    OverlayIconDropdownMenu(entry = entry) {
        Icon(imageVector = COUIIcons.More, contentDescription = "More")
    }
}

带提示槽的选项

hint 槽渲染在标题区块与选中指示图标之间,最大宽度 40dp,适合放红点、计数徽标或极短标签。与 ColorOS 一致,行被禁用时提示槽整体隐藏。

kotlin
val entry = DropdownEntry(
    items = listOf(
        DropdownItem(text = "Inbox", hint = { Badge(count = 12) }),
        DropdownItem(text = "Updates", hint = { Badge() }),
        // 该行被禁用,因此徽标不会显示。
        DropdownItem(text = "Archive", hint = { Badge(count = 3) }, enabled = false),
    )
)

Scaffold {
    OverlayIconDropdownMenu(entry = entry) {
        Icon(imageVector = COUIIcons.More, contentDescription = "More")
    }
}

分组标题

DropdownEntry 可声明 title,渲染为该分组各项之上的不可点击标题行(12sp 中等字重、次级标签 色、最多 2 行)。分组标题与已有的分组分割线可以共存。

kotlin
val entries = listOf(
    DropdownEntry(
        title = "Sort by",
        items = listOf("Name", "Date modified").map { DropdownItem(text = it) }
    ),
    DropdownEntry(
        title = "Order",
        items = listOf("Ascending", "Descending").map { DropdownItem(text = it) }
    )
)

Scaffold {
    OverlayIconDropdownMenu(entries = entries) {
        Icon(imageVector = COUIIcons.Sort, contentDescription = "Sort")
    }
}

警示项

设置 alert = true 可将某项标记为危险操作,其标题使用错误色而非常规标签色;被禁用的警示项仍回退 到禁用色。

kotlin
val entry = DropdownEntry(
    items = listOf(
        DropdownItem(text = "Rename"),
        DropdownItem(text = "Delete", alert = true),
    )
)

Scaffold {
    OverlayIconDropdownMenu(entry = entry) {
        Icon(imageVector = COUIIcons.More, contentDescription = "More")
    }
}

组件状态

禁用状态

kotlin
OverlayIconDropdownMenu(
    entry = DropdownEntry(items = listOf(DropdownItem(text = "Option 1"))),
    enabled = false
) {
    Icon(imageVector = COUIIcons.MoreCircle, contentDescription = "More")
}

当所有 DropdownEntry 都不包含任何条目时,菜单也会被隐式禁用。

禁用部分选项

通过 DropdownItem.enabled 可以禁用单个选项,通过 DropdownEntry.enabled 可以禁用整个分组。禁用的行会置灰并忽略点击。

kotlin
val entry = DropdownEntry(
    items = listOf(
        DropdownItem(text = "Available option"),
        DropdownItem(text = "Unavailable option", enabled = false),
    )
)

Scaffold {
    OverlayIconDropdownMenu(entry = entry) {
        Icon(imageVector = COUIIcons.MoreCircle, contentDescription = "More")
    }
}

属性

OverlayIconDropdownMenu 属性(Entries 重载)

属性名类型说明默认值是否必须
entriesList<DropdownEntry>由分割线隔开的下拉选项分组-
modifierModifier应用于外层 Box 的修饰符Modifier
enabledBoolean图标按钮是否可交互true
maxHeightDp?下拉菜单的最大高度null
dropdownColorsDropdownColors下拉选项的颜色配置DropdownDefaults.dropdownColors()
renderInRootScaffoldBoolean是否在根(最外层)Scaffold 中渲染弹窗。为 true 时,弹窗覆盖全屏。为 false 时,在当前 Scaffold 的范围内渲染并进行位置补偿true
collapseOnSelectionBoolean每次选中后是否关闭弹出框entries.size <= 1
onExpandedChange((Boolean) -> Unit)?展开状态变化时的回调null
backgroundColorColor底层 IconButton 的背景颜色Color.Unspecified
cornerRadiusDp底层 IconButton 的圆角半径IconButtonDefaults.CornerRadius
minHeightDp底层 IconButton 的最小高度IconButtonDefaults.MinHeight
minWidthDp底层 IconButton 的最小宽度IconButtonDefaults.MinWidth
content@Composable () -> Unit按钮内显示的图标(或其他可组合内容)-

Entry 重载属性

属性名类型说明默认值是否必须
entryDropdownEntry单个下拉选项分组-
collapseOnSelectionBoolean选中后是否关闭弹出框true

其余参数与上方 entries 重载完全一致。

属性名类型说明默认值是否必须
itemsList<DropdownItem>此分组中显示的条目-
enabledBoolean此分组是否启用。为 false 时禁用整组条目;为 true 时仍会遵循每个条目的 enabled 状态true
titleString?可选的不可点击分组标题,渲染在各项之上(12sp 中等字重、次级标签色、最多 2 行)null
属性名类型说明默认值是否必须
textString选项显示的文本-
enabledBoolean选项是否可点击,禁用时置灰true
selectedBoolean选项是否处于选中状态false
onClick(() -> Unit)?点击选项时触发的回调null
icon@Composable ((Modifier) -> Unit)?显示在选项文本前的图标null
summaryString?显示在选项文本下方的摘要文本null
childrenList<DropdownItem>?可选的子菜单项;仅级联变体null
hint@Composable (() -> Unit)?可选的尾部提示槽(徽标、红点、短计数),显示在选中指示图标之前,最大宽度 40dp。行被禁用时整体隐藏null
alertBoolean是否为警示(危险)项;其标题使用错误色false
属性名类型说明
contentColorColor选项标题颜色
summaryColorColor选项摘要颜色
containerColorColor选项背景颜色
selectedContentColorColor选中项标题颜色
selectedSummaryColorColor选中项摘要颜色
selectedContainerColorColor选中项背景颜色
selectedIndicatorColorColor选中指示图标颜色
disabledContentColorColor禁用项标题颜色
alertContentColorColor警示项标题颜色
headerColorColor分组标题行的标题颜色

变更日志

基于 Apache-2.0 许可发布