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

相关推荐
小韩博10 分钟前
小迪之盲注第44课
android·网络安全·adb
夏沫琅琊1 小时前
Android TestDPC 工程详解
android
键来大师2 小时前
Android16 AP热点修改默认密码为12345678
android·framework·rk3576·android16
李坤林2 小时前
Android KGI (Generic Kernel Image)
android
十二测试录2 小时前
Android和iOS测试区别
android·经验分享·ios·职场发展·ab测试
柒许宁安2 小时前
在 Cursor 中运行 Android 项目指南
android·java·个人开发
技术小甜甜2 小时前
【Godot】【入门】GDScript 快速上手(只讲游戏里最常用的 20% 语法)
android·游戏·编辑器·游戏引擎·godot
aqi003 小时前
FFmpeg开发笔记(九十五)国产的开源视频美颜工具VideoEditorForAndroid
android·ffmpeg·音视频·直播·流媒体
sanggou3 小时前
基于Java实现的简易规则引擎(日常开发难点记录)
android·java
farewell-Calm3 小时前
Android Studio 添加第三方库
android·ide·android studio