Jetpack Compose 抽屉式导航栏

320 阅读2分钟

抽屉式导航栏组件是一种滑入式菜单,可让用户导航到应用的各个部分。用户可以通过从侧面滑动或点按菜单图标来激活它。

请考虑以下三个实现抽屉式导航栏的用例:

  • 内容整理:让用户能够在不同类别(例如新闻或博客应用)之间切换。
  • 帐号管理:提供指向具有用户帐号的应用中的帐号设置和个人资料部分的快速链接。
  • 功能发现:在单个菜单中整理多项功能和设置,以便用户在复杂应用中发现和访问这些功能。

在 Material Design 中,有两种类型的抽屉式导航栏:

  • 标准:与其他内容共享屏幕中的空间。
  • 模态:在屏幕中其他内容的顶部展示。

示例

您可以使用 [ModalNavigationDrawer] 可组合项实现抽屉式导航栏。

 并提供抽屉式导航栏的内容,如以下示例所示:

SVID_20240407_114014_1_20240407_114054.gif

package com.lujianfei.composeui.page.drawer

import MyTopAppBar
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import kotlinx.coroutines.launch


/**
 * Author: IT 乐手
 * Date: 2024/3/27 11:25
 * Description:
 */

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun DrawerPage(navController: NavHostController?= null) {
    val drawerState = rememberDrawerState(DrawerValue.Closed)
    val scope = rememberCoroutineScope()
    Scaffold(
        topBar = {
            MyTopAppBar(
                title = {
                    Text("抽屉式导航栏")
                },
                navigationIcon = {
                    IconButton(onClick = { navController?.popBackStack() }) {
                        Icon(Icons.Filled.ArrowBack,"")
                    }
                },
                actions = {
                    IconButton(onClick = {
                        scope.launch {
                            if (drawerState.isClosed) {
                                drawerState.open()
                            } else {
                                drawerState.close()
                            }
                        }
                    }) {
                        Icon(
                            imageVector = Icons.Filled.Menu,
                            tint = Color.White,
                            contentDescription = "Menu"
                        )
                    }
                }
            )
        },
    ) { padding ->
        ModalNavigationDrawer(modifier = Modifier
            .padding(padding),
            drawerState = drawerState,
            drawerContent = {
                ModalDrawerSheet {
                    Text("Drawer title", modifier = Modifier.padding(16.dp))
                    NavigationDrawerItem(
                        label = { Text(text = "Drawer Item") },
                        selected = false,
                        onClick = { /*TODO*/ }
                    )
                    // ...其他抽屉选项
                }
            }
        ) {
            // 主屏内容
        }
    }
}

自定义抽屉式导航栏

官方提供的抽屉式导航栏只有从左往右出来的,没有提供从右往左出来的设置方法,所以我这里直接重写自定义的抽屉式导航栏,能达到从右往左打开的效果

package com.purui.mobile.widget.compose

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.DrawerValue
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.onSizeChanged
import kotlin.math.absoluteValue

@Composable
fun MyModalNavigationDrawer(
    drawerContent: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    drawerState: MutableState<DrawerValue> = remember {
        mutableStateOf(DrawerValue.Closed)
    },
    content: @Composable () -> Unit
) {
    Box(
        modifier
            .fillMaxSize()
    ) {
        // 是否显示遮罩层
        val showMask = remember {
            mutableStateOf(false)
        }
        // 抽屉宽度
        val drawerWidth = remember {
            mutableStateOf(0)
        }
        // 抽屉的 x 位置
        val xOffset by animateFloatAsState(
            targetValue = if (drawerState.value == DrawerValue.Closed) screenWidth().toFloat() else screenWidth() - drawerWidth.value.toFloat(),
            animationSpec = tween(durationMillis=400)
        )
        // 半透明
        val maskLayerAlpha by animateFloatAsState(
            targetValue = if (drawerState.value == DrawerValue.Closed) 0f else 0.6f,
            animationSpec = tween(durationMillis=400),
            finishedListener = {
                showMask.value = it.absoluteValue > 0f
            }
        )
        // 内容
        Box {
            content()
        }
        // 遮罩
        if (showMask.value || drawerState.value == DrawerValue.Open) {
            Box(
                modifier = Modifier.fillMaxSize().alpha(maskLayerAlpha)
                    .background(color = Color(0xff000000)).clickable {
                    drawerState.value = DrawerValue.Closed
                }) {}
        }
        // 抽屉
        Box(modifier = Modifier.onSizeChanged {
            drawerWidth.value = it.width
        }.graphicsLayer {
            translationX = xOffset
        }) {
            drawerContent()
        }
    }
}

上一篇 Jetpack Compose 中的 ConstraintLayout

下一篇 Jetpack Compose 对话框