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重组。

相关推荐
前行的小黑炭3 小时前
Android :Comnpose各种副作用的使用
android·kotlin·app
XeonYu4 小时前
Kotlin 协程之 Flow 操作符大全
kotlin·flow·flow 操作符
BD_Marathon17 小时前
【MySQL】函数
android·数据库·mysql
西西学代码17 小时前
安卓开发---耳机的按键设置的UI实例
android·ui
maki07721 小时前
虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理
android·游戏引擎·vr·虚幻·pico·htc vive·大空间
千里马学框架1 天前
音频焦点学习之AudioFocusRequest.Builder类剖析
android·面试·智能手机·车载系统·音视频·安卓framework开发·audio
fundroid1 天前
掌握 Compose 性能优化三步法
android·android jetpack
TeleostNaCl1 天前
如何在 IDEA 中使用 Proguard 自动混淆 Gradle 编译的Java 项目
android·java·经验分享·kotlin·gradle·intellij-idea
旷野说1 天前
Android Studio Narwhal 3 特性
android·ide·android studio