android - 使用 Jetpack Compose 中的抽屉导航在可组合项之间导航

标签 android kotlin navigation-drawer android-jetpack-compose

我正在尝试在 jetpack compose 中为抽屉图标/文本字段设置导航,但不确定如何正确执行。如何设置导航,以便每当我单击其中一个图标时,我都会导航到该可组合屏幕?这是目前我的 MainDrawer 布局:


@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}")
            }
        }
    ) {

    }
}

抽屉主体: 这包含主体元素

@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"

  • 首先为应用程序创建 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
        )
    }
    
  • 接下来,创建一个密封类。这个类将代表你想要放在抽屉里的元素。我举以下例子:
  • 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")
    }
    
  • 现在,创建荧光笔。当抽屉中的某个项目被按下时,这将突出显示被按下的项目。
  • @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
            )
        }
    }
    
  • 现在我们需要使用 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()
            }
        }
    }
    
    
  • 之后,使用所有项目、协程和 navbackstack 创建 DrawerLayout。
  • @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()
                    }
                })
            }
        }
    }
    
    
  • 最后,我们创建了 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)
        }
    }
    

    希望这可以帮助任何正在寻找功能性抽屉导航的人!如果这对您有用,请随意给它一个向上箭头。快乐编码!

    关于android - 使用 Jetpack Compose 中的抽屉导航在可组合项之间导航,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72921484/

    相关文章:

    java - 未在 onClickListener 上设置 boolean 值

    java - AutoCompleteTextView选择结果后返回模型

    android - 在 Kotlin 中检查两个对象的某些(不是全部)属性是否相等的惯用方法

    kotlin - 任何人都可以激发为什么 Arrow (Kotlin) 选择弃用更高级的类型?

    java - 在 Android 应用程序中发送电子邮件

    android - 在android中使用json显示来自url的图像

    android - 如何使用 Kotlin 在 ListAdapter 中使用 Filterable?

    android - 单击应用程序图标打开抽屉导航

    android - 自定义抽屉布局 - 点击透明度

    java - 根据 if else 语句更改字符串数组项