如何使用 Jetpack Compose 创建带有溢出菜单的工具栏?

How can Toolbar with overflow menu be created with Jetpack Compose?

如何在Compose中将工具栏的菜单图标变成溢出?

Scaffold(
    topBar = {
        TopAppBar(
            title = {
                Text(text = "LayoutsCodelab")
            },
            actions = {
                IconButton(onClick = { /* doSomething() */ }) {
                    Icon(Icons.Filled.Favorite)
                }

                IconButton(onClick = { /* doSomething() */ }) {
                    Icon(Icons.Filled.Refresh)
                }

                IconButton(
                    onClick = { /* doSomething() */ }) {
                    Icon(Icons.Filled.Call)
                }

            }
        )
    },
    bottomBar = {
        BottomNavigationLayout()
    }
) { innerPadding ->
    PhotoCard(Modifier.padding(innerPadding))
}

我只希望工具栏菜单中的一个图标可见,其他图标添加到溢出菜单中,就像 xml 使用 app:showAsAction="never"

所做的那样
<item
    android:id="@+id/action_sign_out"
    android:title="@string/toolbar_sign_out"
    app:showAsAction="never"/>

OverFlowMenu必须自己提供,例如:

@Preview
@Composable
fun PreviewOverflowMenu() {
    OverflowMenuTest()
}

@Composable
fun OverflowMenuTest() {
    var showMenu by remember { mutableStateOf(false) }

    TopAppBar(
        title = { Text("Title") },
        actions = {
            IconButton(onClick = { /*TODO*/ }) {
                Icon(Icons.Default.Favorite)
            }
            IconButton(onClick = { showMenu = !showMenu }) {
                Icon(Icons.Default.MoreVert)
            }
            DropdownMenu(
                expanded = showMenu,
                onDismissRequest = { showMenu = false }
            ) {
                DropdownMenuItem(onClick = { /*TODO*/ }) {
                    Icon(Icons.Filled.Refresh)
                }
                DropdownMenuItem(onClick = { /*TODO*/ }) {
                    Icon(Icons.Filled.Call)
                }
            }
        }
    )
}

编辑:为 Compose 1.0.0-beta08 更新

@jns 回答的启发,我制作了一个 ActionMenu 可组合项,其中包含 ActionItemSpec 个对象的列表。并在必要时使用溢出菜单显示它们。我为 ActionItemSpec 建模有点像旧的 XML 菜单项条目,但添加了一个 onClick lambda。

是这样用的

@Preview
@Composable
fun PreviewActionMenu() {
    val items = listOf(
        ActionItemSpec("Call", Icons.Default.Call, ActionItemMode.ALWAYS_SHOW) {},
        ActionItemSpec("Send", Icons.Default.Send, ActionItemMode.IF_ROOM) {},
        ActionItemSpec("Email", Icons.Default.Email, ActionItemMode.IF_ROOM) {},
        ActionItemSpec("Delete", Icons.Default.Delete, ActionItemMode.IF_ROOM) {},
    )
    TopAppBar(
        title = { Text("App bar") },
        navigationIcon = {
            IconButton(onClick = {}) {
                Icon(Icons.Default.Menu, "Menu")
            }
        },
        actions = {
            // show 3 icons including overflow
            ActionMenu(items, defaultIconSpace = 3)
        }
    )
}

预览是这样的

完整的 pastebin 在这里:https://gist.github.com/MachFour/369ebb56a66e2f583ebfb988dda2decf

我修改了一点@jns 的答案,使其更加模块化和可重用。 这是可重复使用的 OverflowMenu:

@Composable
fun OverflowMenu(content: @Composable () -> Unit) {
    var showMenu by remember { mutableStateOf(false) }

    IconButton(onClick = {
        showMenu = !showMenu
    }) {
        Icon(
            imageVector = Icons.Outlined.MoreVert,
            contentDescription = stringResource(R.string.more),
        )
    }
    DropdownMenu(
        expanded = showMenu,
        onDismissRequest = { showMenu = false }
    ) {
        content()
    }
}

这就是它在 TopAppBar 中的使用方式:

TopAppBar(
        title = {
            Text(text = stringResource(R.string.my_title))
        },
        actions = {
            OverflowMenu {
                DropdownMenuItem(onClick = { /*TODO*/ }) {
                    Text("Settings")
                }
                DropdownMenuItem(onClick = { /*TODO*/ }) {
                    Text("Bookmarks")
                }
            }
        }
    )

如果需要,我们可以向 DropDownMenuItems 添加图标。这些项目也可以提取为可重复使用的可组合物。如果还有其他操作按钮要在菜单上显示为图标化按钮(即显示为操作),则应将它们放在 OverflowMenu 之前。

TopAppBar(
        title = {
            Text(text = stringResource(R.string.bookmark))
        },
        actions = {
            //This icon will be shown on the top bar, on the left of the overflow menu
            IconButton(onClick = { /*TODO*/ }) {
                Icon(Icons.Filled.FavoriteBorder, stringResource(R.string.cd_favorite_item))
            }
            OverflowMenu {
                SettingsDropDownItem(onClick = { /*TODO*/ })
                BookmarksDropDownItem(onClick = { /*TODO*/ })
            }
        }
    )

.

@Composable
   fun SettingsDropDownItem(onClick : () -> Unit) {
      //Drop down menu item with an icon on its left
      DropdownMenuItem(onClick = onClick) {
         Icon(Icons.Filled.Settings,
            contentDescription = stringResource(R.string.settings),
            modifier = Modifier.size(24.dp))
         Spacer(modifier = Modifier.width(8.dp))
         Text(stringResource(R.string.settings))
     }
  }

  @Composable
  fun BookmarksDropDownItem(onClick : () -> Unit) {
     //Drop down menu item with an icon on its left
     DropdownMenuItem(onClick = onClick) {
        Icon(painter = painterResource(R.drawable.ic_bookmark_filled),
            contentDescription = stringResource(R.string.bookmark),
            modifier = Modifier.size(24.dp))
        Spacer(modifier = Modifier.width(8.dp))
        Text(stringResource(R.string.bookmark))
    }
}