Compose 中的 Navigation drawer

387 阅读2分钟

在 Jetpack Compose 中,Navigation Drawer 是一种侧边抽屉式导航组件,通常用于提供应用的导航菜单。它可以从屏幕的左侧滑出,显示导航选项和其他功能。

Navigation Drawer 的类型

  1. Modal Navigation Drawer: 覆盖大部分屏幕内容的抽屉,当抽屉打开时,主内容不可操作。
  2. Permanent Navigation Drawer: 始终显示在屏幕一侧,适用于大屏幕设备如平板电脑和桌面设备。

示例代码

以下是使用 ModalNavigationDrawerPermanentNavigationDrawer 的示例。

Modal Navigation Drawer

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NavigationDrawerExampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ModalNavigationDrawerExample()
                }
            }
        }
    }
}

data class DrawerItem(val label: String, val icon: ImageVector)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ModalNavigationDrawerExample() {
    val drawerState = rememberDrawerState(DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    val drawerItems = listOf(
        DrawerItem("Home", Icons.Default.Home),
        DrawerItem("Settings", Icons.Default.Settings)
    )

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ModalDrawerSheet {
                Column {
                    drawerItems.forEach { item ->
                        NavigationDrawerItem(
                            icon = { Icon(item.icon, contentDescription = item.label) },
                            label = { Text(item.label) },
                            selected = false,
                            onClick = {
                                scope.launch { drawerState.close() }
                            }
                        )
                    }
                }
            }
        },
        content = {
            Scaffold(
                topBar = {
                    TopAppBar(
                        title = { Text("Modal Navigation Drawer") },
                        navigationIcon = {
                            IconButton(onClick = {
                                scope.launch { drawerState.open() }
                            }) {
                                Icon(Icons.Default.Menu, contentDescription = "Menu")
                            }
                        }
                    )
                }
            ) { innerPadding ->
                Box(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(innerPadding),
                    contentAlignment = Alignment.Center
                ) {
                    Text("Content goes here")
                }
            }
        }
    )
}

@Preview(showBackground = true)
@Composable
fun ModalNavigationDrawerExamplePreview() {
    NavigationDrawerExampleTheme {
        ModalNavigationDrawerExample()
    }
}

Permanent Navigation Drawer

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PermanentNavigationDrawerExample() {
    val drawerItems = listOf(
        DrawerItem("Home", Icons.Default.Home),
        DrawerItem("Settings", Icons.Default.Settings)
    )

    PermanentNavigationDrawer(
        drawerContent = {
            PermanentDrawerSheet {
                Column {
                    drawerItems.forEach { item ->
                        NavigationDrawerItem(
                            icon = { Icon(item.icon, contentDescription = item.label) },
                            label = { Text(item.label) },
                            selected = false,
                            onClick = { /* Handle navigation */ }
                        )
                    }
                }
            }
        },
        content = {
            Scaffold(
                topBar = {
                    TopAppBar(
                        title = { Text("Permanent Navigation Drawer") }
                    )
                }
            ) { innerPadding ->
                Box(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(innerPadding),
                    contentAlignment = Alignment.Center
                ) {
                    Text("Content goes here")
                }
            }
        }
    )
}

@Preview(showBackground = true)
@Composable
fun PermanentNavigationDrawerExamplePreview() {
    PermanentNavigationDrawerExample()
}

解释

  1. 状态管理:

    • 使用 rememberDrawerState 来管理 ModalNavigationDrawer 的状态(打开或关闭)。
    • PermanentNavigationDrawer 不需要显式的状态管理,因为它始终显示。
  2. 布局:

    • ModalNavigationDrawerPermanentNavigationDrawer 组件包裹了主内容,并定义了抽屉的内容。
    • Scaffold 用于设置应用的整体布局结构,包括顶部应用栏和主内容区域。
  3. 导航项:

    • 使用 NavigationDrawerItem 组件定义抽屉中的导航项,包括图标和标签。
  4. 控制抽屉的打开和关闭:

    • 使用 CoroutineScopelaunch 函数控制 ModalNavigationDrawer 的打开和关闭。
    • drawerState.open()drawerState.close() 分别用于打开和关闭抽屉。

自定义样式

你可以通过修改 ModalDrawerSheetPermanentDrawerSheet 的参数来自定义抽屉的样式,例如背景颜色、内边距等。利用这些参数可以实现更符合设计需求的导航抽屉。