在 Jetpack Compose 中使用导航抽屉在可组合对象之间导航



我试图在jetpack撰写抽屉图标/文本字段设置导航,但不完全确定如何正确地做到这一点。我如何设置导航,以便每当我点击其中一个图标时,我都能导航到可组合的屏幕?这是目前我的主抽屉布局:


@Composable
fun MainDrawer() {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
topBar = {
AppBar(
onNavigationIconClick = {
scope.launch {
scaffoldState.drawerState.isOpen
}
}
)
},
drawerContent = {
DrawerHeader()
DrawerBody(
items = listOf(
MenuItem(
id = "item1",
title = "item1",
contentDescription = "Go to item1 screen",
icon = Icons.Default.Home
),
MenuItem(
id = "item2",
title = "item2",
contentDescription = "Go to item2 screen",
icon = Icons.Default.Settings
),
MenuItem(
id = "item3",
title = "item3",
contentDescription = "Ge to item3",
icon = Icons.Default.Info
),
MenuItem(
id = "item4",
title = "item4",
contentDescription = "Go to Your item4",
icon = Icons.Default.Info
),
MenuItem(
id = "item5",
title = "item5",
contentDescription = "Your item5",
icon = Icons.Default.Info
),
MenuItem(
id = "item6",
title = "item6",
contentDescription = "Your item6",
icon = Icons.Default.Info
),
MenuItem(
id = "item7",
title = "item7",
contentDescription = "item7",
icon = Icons.Default.Info
),
MenuItem(
id = "item8",
title = "item8",
contentDescription = "item8",
icon = Icons.Default.Info
),
)
) {
println("Clicked on ${it.title}")
}
}
) {
}
}

抽屉身体:它包含body元素

@Composable
fun DrawerBody(
items: List<MenuItem>,
modifier: Modifier = Modifier,
itemTextStyle: TextStyle = TextStyle(fontSize = 18.sp),
onItemClick: (MenuItem) -> Unit
) {
LazyColumn(modifier) {
items(items) { item ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
onItemClick(item)
}
.padding(16.dp)
) {
Icon(
imageVector = item.icon,
contentDescription = item.contentDescription
)
Spacer(modifier = Modifier.width(16.dp))
Text(
text = item.title,
style = itemTextStyle,
modifier = Modifier.weight(1f)
)
}
}
}
}

我设法解决了这个问题。它是这样工作的:https://gyazo.com/4c32e855becff72f8979650545ad7f39

我是这样做的:

  1. 从添加依赖项到您的项目开始:
// Navigation with Compose
implementation "androidx.navigation:navigation-compose:2.5.0"
// Modal Drawer Layout
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
  1. 首先为应用程序创建TopAppBar。这是以下代码:

@Composable
fun TopBar(scope: CoroutineScope, scaffoldState: ScaffoldState) {
TopAppBar(
title = { Text(text = stringResource(R.string.app_name), fontSize = 18.sp) },
navigationIcon = {
IconButton(onClick = {
scope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(Icons.Filled.Menu, "")
}
},
backgroundColor = colorResource(id = R.color.purple_200),
contentColor = Color.White
)
}
  1. 下一步,创建一个密封类。这个类将表示您希望放在抽屉中的物品。我举如下例子:
sealed class NavDrawerItem(var route: String, var icon: Int, var title: String) {
object Add : NavDrawerItem("add", android.R.drawable.ic_menu_add, "Add")
object Edit : NavDrawerItem("edit", android.R.drawable.ic_menu_edit, "Edit")
object Search : NavDrawerItem("search", android.R.drawable.ic_menu_search, "Search")
object Location : NavDrawerItem("location", android.R.drawable.ic_menu_mylocation, "Location")
object Preferences : NavDrawerItem("preferences", android.R.drawable.ic_menu_preferences, "Preferences")
}
  1. 现在,创建荧光笔。当抽屉中的某项已被按下时,此选项将突出显示已按下的项。
@Composable
fun DrawerItem(item: NavDrawerItem, selected: Boolean, onItemClick: (NavDrawerItem) -> Unit) {
val background = if (selected) R.color.purple_200 else android.R.color.transparent
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = { onItemClick(item) })
.height(45.dp)
.background(colorResource(id = background))
.padding(start = 10.dp)
) {
Image(
painter = painterResource(id = item.icon),
contentDescription = item.title,
colorFilter = ColorFilter.tint(Color.White),
contentScale = ContentScale.Fit,
modifier = Modifier
.height(35.dp)
.width(35.dp)
)
Spacer(modifier = Modifier.width(7.dp))
Text(
text = item.title,
fontSize = 18.sp,
color = Color.White
)
}
}
  1. 这里我们为各自的项目创建每个屏幕。每个项目有一个可组合的屏幕。我创建了一个简单快捷的屏幕,但是你可以在这里做你自己的屏幕。
@Composable
fun AddScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.myColor))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Add Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
@Composable
fun EditScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.myOrange))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Edit Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
@Composable
fun SearchScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.purple_500))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Search Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
@Composable
fun LocationScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.myGreen))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Location Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
@Composable
fun PreferencesScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(colorResource(id = R.color.myGreen))
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Preference Screen",
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
fontSize = 25.sp
)
}
}
  1. 现在我们需要使用navcontroller和navHostController创建实际的导航。我们这样做的方式如下:
@Composable
fun ComposeNavigation(navController: NavHostController) {
NavHost(navController, startDestination = NavDrawerItem.Add.route) {
composable(NavDrawerItem.Add.route) {
AddScreen()
}
composable(NavDrawerItem.Edit.route) {
EditScreen()
}
composable(NavDrawerItem.Search.route) {
SearchScreen()
}
composable(NavDrawerItem.Location.route) {
LocationScreen()
}
composable(NavDrawerItem.Preferences.route) {
PreferencesScreen()
}
}
}
  1. 之后,创建drawlayout与所有的项目,协程和navbackstack。
@Composable
fun DrawerLayout(scope: CoroutineScope, scaffoldState: ScaffoldState, navController: NavController) {
val items = listOf(
NavDrawerItem.Add,
NavDrawerItem.Edit,
NavDrawerItem.Search,
NavDrawerItem.Location,
NavDrawerItem.Preferences
)
Column {
// Header
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = R.drawable.ic_launcher_foreground.toString(),
modifier = Modifier
.height(100.dp)
.fillMaxWidth()
.padding(10.dp)
)
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(5.dp)
)
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
DrawerItem(item = item, selected = currentRoute == item.route, onItemClick = {
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
scope.launch {
scaffoldState.drawerState.close()
}
})
}
}
}
  1. 最后,我们创建了mainLayout,它将作为我们应用程序的主框架。这里将使用一个支架来放置顶部栏和抽屉内容:
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun MainLayout() {
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
val scope = rememberCoroutineScope()
val navController = rememberNavController()
Scaffold(
scaffoldState = scaffoldState,
topBar = { TopBar(scope = scope, scaffoldState = scaffoldState) },
drawerBackgroundColor = colorResource(id = R.color.myColor),
drawerContent = {
DrawerLayout(scope = scope, scaffoldState = scaffoldState, navController = navController)
},
) {
ComposeNavigation(navController = navController)
}
}

希望这有助于任何人寻找一个功能导航抽屉!如果这对你有用,你可以给它一个向上的箭头。编码快乐!