2024年已经过半了,我作为聋人独立开发者,我经常会时不时反思:自己这半年到底进步了多少?在这篇文章里,我分享一个用 Jetpack Compose、Material3和 Kotlin语言开发NimDrawaerMenuApp的案例。无论你有没有开发经验,相信这篇文章对你会非常有所帮助。
在现代应用中,导航是关键元素,特别是使用侧边栏(Drawer Menu)切换不同页面的场景。通过 Jetpack Compose,安卓开发已经从传统的 XML 布局转向了声明式 UI 方式,简化了很多工作。这个Demo演示利用 Jetpack Compose 和 Material 3 实现一个带有 Drawer 菜单的应用,帮助用户理解工作机制,通过此应用进行页面切换。
二、项目开发
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "eu.tutorials.drawermenuappdemo"
compileSdk = 34
defaultConfig {
applicationId = "eu.tutorials.drawermenuappdemo"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
}
buildTypes {
release {
isMinifyEnabled = false
}
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
kotlinOptions {
jvmTarget = "1.8"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
val compose_version = "1.6.0-alpha08"
val nav_version = "2.7.5"
implementation("androidx.compose.ui:ui:$compose_version")
implementation("androidx.compose.material3:material3:$compose_version")
implementation("androidx.navigation:navigation-compose:$nav_version")
implementation("androidx.activity:activity-compose:1.8.0")
}
Drawer 是侧边栏菜单,提供一种易于访问的页面切换方式,用于多功能的应用App,允许用户在不同页面之间快速切换。在 Jetpack Compose 中,使用 ModalNavigationDrawer 组件构建 Drawer 菜单,根据用户操作动态显示或隐藏菜单。
在项目中,每个 Drawer 菜单项对应一个独立页面,使用 sealed class 定义菜单项:
sealed class DrawerScreen(val title: String, val icon: ImageVector) {
object Account : DrawerScreen("账户", Icons.Filled.AccountCircle)
object Subscription : DrawerScreen("订阅", Icons.Filled.Subscriptions)
object AddAccount : DrawerScreen("添加账户", Icons.Filled.PersonAdd)
}
val drawerItems = listOf(
DrawerScreen.Account,
DrawerScreen.Subscription,
DrawerScreen.AddAccount
)
定义了DrawerScreen类为每个菜单项设置了标题和图标。通过drawerItems列表,drawerItems 是菜单项的集合,用于构建完整的 Drawer 菜单。
DrawerMenuApp 是应用的核心,它通过 ModalNavigationDrawer 和 Scaffold 组件构建侧边菜单布局和顶部应用栏:
@Composable
fun DrawerMenuApp() {
var selectedItem by remember { mutableStateOf<DrawerScreen>(DrawerScreen.Account) }
val drawerState = rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope()
ModalNavigationDrawer(
drawerContent = {
DrawerContent(
selectedItem = selectedItem,
onItemSelected = { selectedItem = it; scope.launch { drawerState.close() } }
)
},
drawerState = drawerState
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text(selectedItem.title) },
navigationIcon = {
IconButton(onClick = { scope.launch { drawerState.open() } }) {
Icon(Icons.Default.Menu, contentDescription = "Menu")
}
}
)
}
) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) {
// 显示对应的页面内容
when (selectedItem) {
DrawerScreen.Account -> AccountScreen()
DrawerScreen.Subscription -> SubscriptionScreen()
DrawerScreen.AddAccount -> AddAccountScreen()
}
}
}
}
}
DrawerMenuApp 通过 Scaffold 设置顶部栏,通过 ModalNavigationDrawer 进行页面切换。在点击菜单项时,会关闭抽屉并切换到对应的页面内容。
DrawerContent 负责展示菜单项内容。每个项都是一个 NavigationDrawerItem,当用户点击项时,调用 onItemSelected 切换页面。
@Composable
fun DrawerContent(
selectedItem: DrawerScreen,
onItemSelected: (DrawerScreen) -> Unit
) {
ModalDrawerSheet {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp),
contentAlignment = Alignment.Center
) {
Text("NimDrawer", modifier = Modifier.padding(10.dp))
}
drawerItems.forEach { item ->
NavigationDrawerItem(
label = { Text(item.title) },
icon = { Icon(item.icon, contentDescription = null) },
selected = item == selectedItem,
onClick = { onItemSelected(item) },
modifier = Modifier.padding(8.dp)
)
}
}
}
selectedItem: DrawerScreen, onItemSelected: (DrawerScreen) -> Unit
• selectedItem
:当前选中的菜单项,类型是 DrawerScreen,指示当前高亮显示的菜单项。
• onItemSelected
:一个回调函数,用于处理用户点击菜单项时的逻辑。它接收一个 DrawerScreen 对象,表示用户选中的菜单项。点击某个菜单项时会调用这个函数,然后选中的菜单项作为参数传递。
ModalDrawerSheet {
一. ModalDrawerSheet
(1) 场景:ModalDrawerSheet 是最常见的 Drawer 类型,用于显示在应用的内容之上,当 Drawer 打开时,主界面会被覆盖,需要用户手动关闭或点击其他地方来收起抽屉。
(2) 解释:
• ModalDrawerSheet 是用于模态抽屉的组件,它会覆盖住当前内容,用户必须关闭它才能回到主界面。
• 在代码中,我们通过 ModalDrawerSheet 包裹所有的菜单项,点击其中的任意一项可以触发不同的操作。
(3) 适用场景:
• 比如一个购物应用中的导航栏,当用户点击左上角的菜单按钮时,抽屉滑出,显示购物车、收藏夹、账户信息等。
二. DismissibleDrawerSheet
(1) 场景:DismissibleDrawerSheet 是用户可以通过滑动手势轻松关闭的 Drawer。它允许用户更自然和抽屉交互
(2) 解释:
• DismissibleDrawerSheet 的工作原理与 ModalDrawerSheet 类似,但不同的是它更注重用户手势关闭的体验。用户可以直接通过滑动关闭这个抽屉,而不是必须点击关闭按钮。
• 这为用户提供了更流畅的体验。
(3) 适用场景:
• 比如社交类应用中的消息推送列表,当用户从侧边滑动出菜单后,浏览完内容,用户可以随时通过手势关闭,不需要再次点击关闭按钮。
3. PermanentDrawerSheet
(1) 场景:PermanentDrawerSheet 是一种固定的抽屉,它不会因为用户交互而隐藏。一般来说用于大屏设备,比如平板或者桌面端的应用。
(2) 解释:
• PermanentDrawerSheet 与前两种抽屉不同,它是固定在界面左侧的,用户无法将它关闭。它适合那些需要一直显示导航的场景,比如电子邮件客户端或者文件管理器。
• 这种 Drawer 不会挡住主屏幕内容,而是始终内容排显示
(3) 适用场景:
• 比如在邮件应用中,你可以看到左边有固定的邮箱文件夹列表,右边是邮件内容。文件夹导航不会随着用户操作消失。
三种 Drawer 的使用场景各有不同:
• ModalDrawerSheet:适合短暂显示,需要用户手动关闭。
• DismissibleDrawerSheet:更便捷的用户体验,允许通过滑动手势关闭。
• PermanentDrawerSheet:在大屏设备上固定显示,适合信息密集型应用。
每个菜单项对应的页面是一个简单的 Composable,每个页面只是展示一个简单的文本,代表对应的菜单项:
@Composable
fun AccountScreen() {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("Nim账户页面")
}
}
@Composable
fun SubscriptionScreen() {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("Nim订阅页面")
}
}
@Composable
fun AddAccountScreen() {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("Nim添加账户页面")
}
}
@Preview(showBackground = true)
@Composable
fun PreviewDrawerContent() {
DrawerContent(
selectedItem = DrawerScreen.Account,
onItemSelected = {}
)
}
用 remember 和 mutableStateOf 存储用户点击的菜单项,通过 DrawerState 控制 Drawer 的显示和隐藏状态。这种方式为了是在用户点击菜单项时,可以同步显示对应的页面内容。
看这段代码理解是每次点击菜单项时,都会触发 onItemSelected 回调,这个回调函数通过 mutableStateOf 更新当前选中的菜单项,关闭 Drawer。
var selectedItem by remember { mutableStateOf<DrawerScreen>(DrawerScreen.Account) }
val drawerState = rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope()
ModalNavigationDrawer(
drawerContent = {
DrawerContent(
selectedItem = selectedItem,
onItemSelected = { selectedItem = it; scope.launch { drawerState.close() } }
)
},
drawerState = drawerState
)
核心逻辑是:每次用户点击菜单项时,都会通过 onItemSelected 修改 selectedItem,从而控制页面的显示和抽屉的关闭。
我深刻体会到了Material 3 中的 Drawer 的三种模式(ModalDrawerSheet、DismissibleDrawerSheet 和 PermanentDrawerSheet)让我对不同使用场景的 Drawer 有了更深入的理解。在交互体验和视觉展示上各有特色,可以适应不同应用场景。
通过这个DrawerMenuAppDemo,展示了如何在Jetpack Compose M3中实现一个带有抽屉菜单的安卓应用,添加更多的菜单项以及对应的页面功能。
有任何问题欢迎提问,感谢大家阅读 )
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有