Compose页面切换的几种方式:Navigation、NavigationBar+HorizontalPager,会导致LaunchedEffect执行?

比如,我们要做成底部导航栏去动态切换。

kt 复制代码
// 1. 定义密封类或对象来存储目的地信息
sealed class BottomNavItem(
    val route: String,
    val icon: ImageVector,
    val title: String
) {
    object Home : BottomNavItem("home", Icons.Default.Home, "Home")
    object Search : BottomNavItem("search", Icons.Default.Search, "Search")
    object Profile : BottomNavItem("profile", Icons.Default.Person, "Profile")
}

// 2. 在主界面 Scaffold 中设置
@Composable
fun MainScreen() {
    val navController = rememberNavController()
    val currentBackStack by navController.currentBackStackEntryAsState()
    val currentDestination = currentBackStack?.destination

    Scaffold(
        bottomBar = {
            NavigationBar {
                // 获取所有底部导航项
                val navItems = listOf(
                    BottomNavItem.Home,
                    BottomNavItem.Search,
                    BottomNavItem.Profile
                )
                navItems.forEach { item ->
                    NavigationBarItem(
                        // 高亮选中当前目的地对应的Item
                        selected = currentDestination?.route == item.route,
                        onClick = {
                            // 导航到该目的地,并采用弹出到起始点的策略
                            // 避免在点击底部栏Item时在栈中堆积多个同一目的地的实例
                            navController.navigate(item.route) {
                                popUpTo(navController.graph.findStartDestination().id) {
                                    saveState = true
                                }
                                // 如果点击的是当前已选中的Item,则避免重复导航
                                launchSingleTop = true
                                // 恢复之前保存的状态(如果存在)
                                restoreState = true
                            }
                        },
                        icon = { Icon(item.icon, contentDescription = item.title) },
                        label = { Text(item.title) }
                    )
                }
            }
        }
    ) { innerPadding ->
        // 3. 设置 NavHost
        NavHost(
            navController = navController,
            startDestination = BottomNavItem.Home.route,//设置主页
            modifier = Modifier.padding(innerPadding)
        ) {
            // 为每个目的地定义可组合函数
            composable(BottomNavItem.Home.route) { HomeScreen(navController) }
            composable(BottomNavItem.Search.route) { SearchScreen(navController) }
            composable(BottomNavItem.Profile.route) { ProfileScreen(navController) }
        }
    }
}

当然,它不一定需要导航栏,比如有一种情况就是,我们希望通过页面的一些按钮或逻辑来切换,那么我们可以去掉底部导航栏。直接这样就行。

kt 复制代码
@Composable
fun MainScreen() {
    val navController = rememberNavController()
    
        NavHost(
            navController = navController,
            startDestination = BottomNavItem.Home.route,//设置主页
            modifier = Modifier.padding(innerPadding)
        ) {
            // 为每个目的地定义可组合函数
            composable(BottomNavItem.Home.route) { HomeScreen(navController) }
            composable(BottomNavItem.Search.route) { SearchScreen(navController) }
            composable(BottomNavItem.Profile.route) { ProfileScreen(navController) }
        }


}

还有一种页面切换的方式,NavigationBar+HorizontalPager

二、NavigationBar+HorizontalPager

kt 复制代码
@Composable
fun MainScaffold(navHostController: NavHostController) {
    // 创建页面状态(使用Accompanist Pager)
    val pagerState = rememberPagerState(
        pageCount = { MainScreen.values().size } // 页面数量
    )
    // 创建协程作用域
    val scope = rememberCoroutineScope()

    // 当前选中的页面索引(状态)
    var selectedScreen by remember { mutableIntStateOf(0) }

    // 监听页面变化并更新选中状态
    LaunchedEffect(pagerState) {
        // 使用snapshotFlow将页面状态转换为Flow
        snapshotFlow { pagerState.currentPage }.collect { page ->
            selectedScreen = page
        }
    }

    // 使用Scaffold布局
    Scaffold(
        bottomBar = {
            // 自定义底部导航栏
            WanBottomBar(
                selectedScreen = selectedScreen, // 当前选中项
                onClick = { screenIndex ->
                    // 点击导航项时滚动到对应页面
                    scope.launch {
                        pagerState.scrollToPage(screenIndex)
                    }
                }
            )
        }
    ) { innerPadding ->
        // 水平分页器(实现页面滑动)
        HorizontalPager(
            state = pagerState,
            userScrollEnabled = false, // 禁用用户滑动(仅通过底部导航切换)
            contentPadding = innerPadding // 内边距
        ) { pageIndex ->
            // 根据页面索引显示对应内容
            when (MainScreen.values()[pageIndex]) {
                MainScreen.Home -> HomeScreen(navHostController) // 首页
                MainScreen.Project -> ProjectScreen() // 项目页
                MainScreen.Navigator -> NavigatorScreen() // 导航页
                MainScreen.Group -> GroupScreen() // 分组页
                else -> ProfileScreen() // 个人中心页
            }
        }
    }
}

三、这两种方式的区别在哪里呢?

我们可以类比一下原生的

  • 方案一 (Navigation Component)​ ​ 类似于使用 ​​一个 Activity + 多个 Fragment​ ​,并且用 ​NavHostFragment+ 导航图​​ 来管理它们。当你导航时,发生的是 Fragment 事务(Transaction),当前 Fragment 被替换,Fragment就会被销毁了。

  • ​方案二 (Pager)​ ​ 类似于使用 ​​一个 Activity + 一个 ViewPager2​ ​,并且使用 ​FragmentStateAdapter ​ 或 ​FragmentPagerAdapter​。所有 Fragment 都已经被创建并添加到 ViewPager 中,滑动只是切换显示。

所以,切换会导致重新执行吗?

  • ​Navigation 组件​ ​:​​会​ ​。composable { ... }中的代码块会在目的地成为当前目的地时执行,离开时销毁。LaunchedEffect也会。

  • ​HorizontalPager​ ​:​​不会​ ​。每个页面的 Composable只在 HorizontalPager首次进入组合时执行一次,之后不会被重新执行,LaunchedEffect也不会,除非整个 MainScaffold重组。

相关推荐
sweet丶6 小时前
iOS内存映射技术:mmap如何用有限内存操控无限数据
ios·操作系统·app
a3158238069 小时前
Android Framework开发知识点整理
android·java·linux·服务器·framework·android源码开发
k***82519 小时前
图文详述:MySQL的下载、安装、配置、使用
android·mysql·adb
小七有话说10 小时前
DevUI与企业级中后台系统融合:低代码表单构建器实战
android·rxjava·devui
暗碳11 小时前
安卓abx二进制xml文件转换普通xml文件
android·xml
4z3311 小时前
Android15 Framework(3):系统服务进程 SystemServer 解析
android·源码阅读
没有了遇见12 小时前
Android 之Google Play bundletool 校验 AAB包
android·google
yuanhello12 小时前
【Android】Android的键值对存储方案对比
android·java·android studio
Ditglu.12 小时前
CentOS7 MySQL5.7 主从复制最终版搭建流程(避坑完整版)
android·adb
恋猫de小郭12 小时前
Android Studio Otter 2 Feature 发布,最值得更新的 Android Studio
android·前端·flutter