Jetpack Compose - Material Design 断点范围、WindowSizeClass、针对不同屏幕尺寸创建预览、四类导航栏

一、Material Design 断点范围

  • 官方文档:https://m3.material.io/foundations/layout/breakpoints/overview
断点 屏幕宽度(dp) 典型设备
Compact(紧凑) 0 - 599 竖屏手机
Medium(中等) 600 - 839 横屏手机、竖屏折叠屏、小平板
Expanded(扩展) 840 - 1199 横屏平板、桌面大窗口
Large(大) 1200 - 1599 桌面、ChromeBook
Extra-large(超大) 1600 及以上 大桌面显示器

二、WindowSizeClass

1、基本介绍
  1. Compose 引入的 WindowSizeClass API 简化了 Material Design 断点的实现

  2. WindowSizeClass 将断点简化为 Compact、Medium、Expanded 三类

  3. WindowSizeClass API 仍处于需要 @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) 注解的 Alpha 版

2、基本使用
  1. 在模块级 build.gradle 文件中添加依赖项
kotlin 复制代码
implementation("androidx.compose.material3:material3-window-size-class")
  1. 在代码中使用
kotlin 复制代码
val windowSize = calculateWindowSizeClass(this)

Log.i(TAG, "windowSize: ${windowSize}")

三、针对不同屏幕尺寸创建预览

  • 在 Preview 注解形参中设置 widthDp 值,结合 WindowSizeClass
kotlin 复制代码
@Preview(showBackground = true)
@Composable
fun ReplyAppCompactPreview() {
    ReplyTheme {
        Surface {
            ReplyApp(WindowWidthSizeClass.Compact)
        }
    }
}
kotlin 复制代码
@Preview(showBackground = true, widthDp = 700)
@Composable
fun ReplyAppMediumPreview() {
    ReplyTheme {
        Surface {
            ReplyApp(windowSize = WindowWidthSizeClass.Medium)
        }
    }
}
kotlin 复制代码
@Preview(showBackground = true, widthDp = 1000)
@Composable
fun ReplyAppExpandedPreview() {
    ReplyTheme {
        Surface {
            ReplyApp(windowSize = WindowWidthSizeClass.Expanded)
        }
    }
}

四、四类导航栏

1、底部导航栏
(1)源码
kotlin 复制代码
@Composable
fun NavigationBar(
    modifier: Modifier = Modifier,
    containerColor: Color = NavigationBarDefaults.containerColor,
    contentColor: Color = MaterialTheme.colorScheme.contentColorFor(containerColor),
    tonalElevation: Dp = NavigationBarDefaults.Elevation,
    windowInsets: WindowInsets = NavigationBarDefaults.windowInsets,
    content: @Composable RowScope.() -> Unit
)
kotlin 复制代码
@Composable
fun RowScope.NavigationBarItem(
    selected: Boolean,
    onClick: () -> Unit,
    icon: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    label: @Composable (() -> Unit)? = null,
    alwaysShowLabel: Boolean = true,
    colors: NavigationBarItemColors = NavigationBarItemDefaults.colors(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)
(2)演示
kotlin 复制代码
MaterialTheme {
    val paddingValues = WindowInsets.safeDrawing.asPaddingValues()
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        modifier = Modifier
            .padding(
                start = paddingValues.calculateStartPadding(layoutDirection),
                end = paddingValues.calculateEndPadding(layoutDirection)
            )
    ) {
        data class NavItem(
            val title: String,
            val icon: ImageVector,
        )

        val navItems = listOf(
            NavItem("Home", Icons.Filled.Home),
            NavItem("Call", Icons.Filled.Call),
            NavItem("Email", Icons.Filled.Email),
            NavItem("Person", Icons.Filled.Person)
        )

        var selectedItemIndex by remember { mutableStateOf(0) }

        Column(
            modifier = Modifier.fillMaxSize()
        ) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .weight(1f),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                when (selectedItemIndex) {
                    0 -> Text("Home")
                    1 -> Text("Call")
                    2 -> Text("Email")
                    3 -> Text("Person")
                }
            }

            NavigationBar {
                navItems.forEachIndexed { index, item ->
                    NavigationBarItem(
                        icon = { Icon(item.icon, contentDescription = item.title) },
                        label = { Text(item.title) },
                        selected = selectedItemIndex == index,
                        onClick = { selectedItemIndex = index },
                    )
                }
            }
        }
    }
}
2、侧边导航栏
(1)源码
kotlin 复制代码
@Composable
fun NavigationRail(
    modifier: Modifier = Modifier,
    containerColor: Color = NavigationRailDefaults.ContainerColor,
    contentColor: Color = contentColorFor(containerColor),
    header: @Composable (ColumnScope.() -> Unit)? = null,
    windowInsets: WindowInsets = NavigationRailDefaults.windowInsets,
    content: @Composable ColumnScope.() -> Unit
)
kotlin 复制代码
@Composable
fun NavigationRailItem(
    selected: Boolean,
    onClick: () -> Unit,
    icon: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    label: @Composable (() -> Unit)? = null,
    alwaysShowLabel: Boolean = true,
    colors: NavigationRailItemColors = NavigationRailItemDefaults.colors(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
)
(2)演示
kotlin 复制代码
MaterialTheme {
    val paddingValues = WindowInsets.safeDrawing.asPaddingValues()
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        modifier = Modifier
            .padding(
                start = paddingValues.calculateStartPadding(layoutDirection),
                end = paddingValues.calculateEndPadding(layoutDirection)
            )
    ) {
        data class NavItem(
            val title: String,
            val icon: ImageVector,
        )

        val navItems = listOf(
            NavItem("Home", Icons.Filled.Home),
            NavItem("Call", Icons.Filled.Call),
            NavItem("Email", Icons.Filled.Email),
            NavItem("Person", Icons.Filled.Person)
        )

        var selectedItemIndex by remember { mutableStateOf(0) }

        Row(
            modifier = Modifier.fillMaxSize()
        ) {
            NavigationRail {
                navItems.forEachIndexed { index, item ->
                    NavigationRailItem(
                        icon = { Icon(item.icon, contentDescription = item.title) },
                        label = { Text(item.title) },
                        selected = selectedItemIndex == index,
                        onClick = { selectedItemIndex = index },
                    )
                }
            }

            Column(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxHeight(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                when (selectedItemIndex) {
                    0 -> Text("Home")
                    1 -> Text("Call")
                    2 -> Text("Email")
                    3 -> Text("Person")
                }
            }
        }
    }
}
3、永久抽屉导航栏
(1)源码
kotlin 复制代码
@Composable
fun PermanentNavigationDrawer(
    drawerContent: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
)
kotlin 复制代码
@Composable
fun PermanentDrawerSheet(
    modifier: Modifier = Modifier,
    drawerShape: Shape = RectangleShape,
    drawerContainerColor: Color = DrawerDefaults.containerColor,
    drawerContentColor: Color = contentColorFor(drawerContainerColor),
    drawerTonalElevation: Dp = DrawerDefaults.PermanentDrawerElevation,
    windowInsets: WindowInsets = DrawerDefaults.windowInsets,
    content: @Composable ColumnScope.() -> Unit
)
kotlin 复制代码
@Composable
fun NavigationDrawerItem(
    label: @Composable () -> Unit,
    selected: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    icon: (@Composable () -> Unit)? = null,
    badge: (@Composable () -> Unit)? = null,
    shape: Shape = NavigationDrawerTokens.ActiveIndicatorShape.value,
    colors: NavigationDrawerItemColors = NavigationDrawerItemDefaults.colors(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)
(2)演示
kotlin 复制代码
MaterialTheme {
    val paddingValues = WindowInsets.safeDrawing.asPaddingValues()
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        modifier = Modifier
            .padding(
                start = paddingValues.calculateStartPadding(layoutDirection),
                end = paddingValues.calculateEndPadding(layoutDirection)
            )
    ) {
        data class NavItem(
            val title: String,
            val icon: ImageVector,
        )

        val navItems = listOf(
            NavItem("Home", Icons.Filled.Home),
            NavItem("Call", Icons.Filled.Call),
            NavItem("Email", Icons.Filled.Email),
            NavItem("Person", Icons.Filled.Person)
        )

        var selectedItemIndex by remember { mutableStateOf(0) }

        PermanentNavigationDrawer(
            drawerContent = {
                PermanentDrawerSheet(Modifier.width(200.dp)) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                    ) {
                        navItems.forEachIndexed { index, item ->
                            NavigationDrawerItem(
                                icon = { Icon(item.icon, contentDescription = item.title) },
                                label = { Text(item.title) },
                                selected = selectedItemIndex == index,
                                onClick = { selectedItemIndex = index },
                            )
                        }
                    }
                }
            }
        ) {
            Column(
                modifier = Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                when (selectedItemIndex) {
                    0 -> Text("Home")
                    1 -> Text("Call")
                    2 -> Text("Email")
                    3 -> Text("Person")
                }
            }
        }
    }
}
4、滑动抽屉导航栏
(1)源码
kotlin 复制代码
@Composable
fun ModalNavigationDrawer(
    drawerContent: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
    gesturesEnabled: Boolean = true,
    scrimColor: Color = DrawerDefaults.scrimColor,
    content: @Composable () -> Unit
)
kotlin 复制代码
@Composable
fun ModalDrawerSheet(
    modifier: Modifier = Modifier,
    drawerShape: Shape = DrawerDefaults.shape,
    drawerContainerColor: Color = DrawerDefaults.containerColor,
    drawerContentColor: Color = contentColorFor(drawerContainerColor),
    drawerTonalElevation: Dp = DrawerDefaults.ModalDrawerElevation,
    windowInsets: WindowInsets = DrawerDefaults.windowInsets,
    content: @Composable ColumnScope.() -> Unit
)
kotlin 复制代码
@Composable
fun NavigationDrawerItem(
    label: @Composable () -> Unit,
    selected: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    icon: (@Composable () -> Unit)? = null,
    badge: (@Composable () -> Unit)? = null,
    shape: Shape = NavigationDrawerTokens.ActiveIndicatorShape.value,
    colors: NavigationDrawerItemColors = NavigationDrawerItemDefaults.colors(),
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)
(2)演示
kotlin 复制代码
MaterialTheme {
    val paddingValues = WindowInsets.safeDrawing.asPaddingValues()
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        modifier = Modifier
            .padding(
                start = paddingValues.calculateStartPadding(layoutDirection),
                end = paddingValues.calculateEndPadding(layoutDirection)
            )
    ) {
        data class NavItem(
            val title: String,
            val icon: ImageVector,
        )

        val navItems = listOf(
            NavItem("Home", Icons.Filled.Home),
            NavItem("Call", Icons.Filled.Call),
            NavItem("Email", Icons.Filled.Email),
            NavItem("Person", Icons.Filled.Person)
        )

        var selectedItemIndex by remember { mutableStateOf(0) }

        ModalNavigationDrawer(
            drawerContent = {
                ModalDrawerSheet(Modifier.width(200.dp)) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                    ) {
                        navItems.forEachIndexed { index, item ->
                            NavigationDrawerItem(
                                icon = { Icon(item.icon, contentDescription = item.title) },
                                label = { Text(item.title) },
                                selected = selectedItemIndex == index,
                                onClick = {
                                    selectedItemIndex = index
                                },
                            )
                        }
                    }
                }
            }
        ) {
            Column(
                modifier = Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                when (selectedItemIndex) {
                    0 -> Text("Home")
                    1 -> Text("Call")
                    2 -> Text("Email")
                    3 -> Text("Person")
                }
            }
        }
    }
}
kotlin 复制代码
MaterialTheme {
    val paddingValues = WindowInsets.safeDrawing.asPaddingValues()
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        modifier = Modifier
            .padding(
                start = paddingValues.calculateStartPadding(layoutDirection),
                end = paddingValues.calculateEndPadding(layoutDirection)
            )
    ) {
        data class NavItem(
            val title: String,
            val icon: ImageVector,
        )

        val navItems = listOf(
            NavItem("Home", Icons.Filled.Home),
            NavItem("Call", Icons.Filled.Call),
            NavItem("Email", Icons.Filled.Email),
            NavItem("Person", Icons.Filled.Person)
        )

        var selectedItemIndex by remember { mutableStateOf(0) }

        val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
        val scope = rememberCoroutineScope()

        ModalNavigationDrawer(
            drawerState = drawerState,
            drawerContent = {
                ModalDrawerSheet(Modifier.width(200.dp)) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                    ) {
                        navItems.forEachIndexed { index, item ->
                            NavigationDrawerItem(
                                icon = { Icon(item.icon, contentDescription = item.title) },
                                label = { Text(item.title) },
                                selected = selectedItemIndex == index,
                                onClick = {
                                    selectedItemIndex = index
                                    scope.launch {
                                        drawerState.close()
                                    }
                                },
                            )
                        }
                    }
                }
            }
        ) {
            Column(
                modifier = Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                when (selectedItemIndex) {
                    0 -> Text("Home")
                    1 -> Text("Call")
                    2 -> Text("Email")
                    3 -> Text("Person")
                }
            }
        }
    }
}
相关推荐
linux-hzh1 小时前
百日算法修炼 · Day 05
java·算法
摇滚侠1 小时前
云原生 Java 架构师的第一课 K8s+Docker+KubeSphere+DevOps 89-129
java·云原生·kubernetes
captain3761 小时前
多线程进阶
java·开发语言·数据库
执明wa2 小时前
Android 开发中的设计模式入门:六大设计原则
android·设计模式
雨辰AI2 小时前
人大金仓 K8s 密评合规加固|国密全链路加密 + 权限收敛容器化落地(三级密评直通版)
java·云原生·容器·kubernetes·政务
郑州光合科技余经理8 小时前
代驾系统架构拆解:订单链路、权限组织与私有化源码交付
开发语言·后端·算法·架构·系统架构·uni-app·php
码哥DFS10 小时前
二叉树的直径
开发语言·javascript·算法
paopaokaka_luck11 小时前
基于springboot3+vue3的企业考勤管理系统(部门树递归、Echarts图形化分析)
开发语言·spring boot·学习·echarts·mybatis·需求分析·代码规范
Pluchon12 小时前
Java个人综合项目——萌部落社区V2.0
java·python·spring·spring cloud·postman·idea