Compose版本的Navigation组件的使用

说明

在原生中,官方比较推荐使用单Activity+多Fragment模式来构建一个APP,而Fragment的切换需要使用FragmentManager来进行管理,比如add replace等方法,对于开发者来说,也是比较繁琐,于是官方在Jetpack组件中就是推出了Navigation的库,可以方便开发者快速去管理多Fragment的页面堆栈问题

而今天的主要针对compose,compose的架构也是一个Activity+多个可组合项,而如何去切换可组合项?

以往的做法就是定义一个选中下标,下标更改了,重新渲染一下页面,从而达到切换页面的效果

而有了Navigation,则是可以在使用层面更为优雅和规范,也是方便管理,不易出现奇奇怪怪的堆栈问题等

基本使用

这里我们以一个使用了NavigationBar组件的页面进行改造

PS:这里我是新版本的AS创建的项目,默认使用的是Material3的依赖包,所以底部导航栏组件就是NavigationBar,如果是Material2的依赖包,则是叫BottomNavigation

效果如下图所示:
img

就是实现了两个底部菜单,然后可以进行页面的切换,代码如下所示:

@OptIn(ExperimentalMaterial3Api::class)
@Preview(showBackground = true)
@Composable
fun MyApp() {
    
    
    
    var currentSelect by remember {
    
    
        mutableStateOf(0)
    }

    val menuData = listOf(
        BottomItemData("首页", Icons.Filled.Home),
        BottomItemData("设置", Icons.Filled.Settings)
    )

    Scaffold(modifier = Modifier.fillMaxSize()
    , bottomBar = {
    
    
        NavigationBar() {
    
    
            menuData.forEachIndexed {
    
     index, bottomItemData ->
                NavigationBarItem(
                    selected = index == currentSelect,
                    onClick = {
    
    
                        currentSelect = index
                    },
                    icon = {
    
    
                        Icon(
                            imageVector = bottomItemData.icon,
                            contentDescription = "点击按钮"
                        )
                    },
                    label = {
    
    
                        Text(
                            text = (bottomItemData.label)
                        )
                    },
                )
            }
        }
    }
    ) {
    
     innerPadding ->
        //下面第3步就是对此部分代码进行更改
        
        //IDE强制要使用者innerPadding,这里就简单的打印一下
        println(innerPadding)
        if (currentSelect == 0) {
    
    
            Text("首页")
        }else{
    
    
            Text("设置")
        }

    }
}

从上面代码中,我们可以看到我们是通过currentSelect这个变量来进行页面的显示管理,实际上这里管理会比较繁琐,这个时候就得用到今天的主角Navigation

1.引入依赖

implementation "androidx.navigation:navigation-compose:2.5.3"

2.声明NavController

首先,就是使用rememberNavController获得NavController对象

//导航
val navController = rememberNavController()

PS: 之后的跳转和返回上一级就是使用此对象过来就行

3.使用NavHost,声明页面路由

在布局content参数的函数里使用下面这段代码:

NavHost(navController = navController, startDestination = "MainPage") {
    
    
    //声明名为MainPage的页面路由
    composable("MainPage"){
    
    
        //页面路由对应的页面组件
        MainPage()
    }
    composable("SettingPage"){
    
    
        SettingPage()
    }
}

上面是声明了两个页面,MainPage和SettingPage则是它两的路由名,后面页面跳转需要使用到

NavHost方法中,参数需要上一步的NavController对象,startDestination则是开始的页面为MainPage

简单的解释,就是可以把NavHost也看出一个页面组件,这个组件默认显示的是MainPage页面

源码如下,点击展开

3.页面跳转

下面则是两个重要的方法:

  • navController.navigate() 页面跳转
  • navController.popBackStack() 回上一级,相当于点击返回键

这里,我们结合NavigationBar实现点击底部按钮切换不同的页面

实现效果如下图所示:
img

代码如下图所示
img

PS:这里需要注意,如果使用了navController.navigate()来进行页面跳转,实际上就是和打开一个新的Activity类似,存在一个页面堆栈

比如说上图的效果,如果点返回键,则是回到上次的页面,而不会立即退出APP

而且,每次调用navigate()会创建一个新的页面对象

如果需要单一创建一个对象,可以这样写

navController.navigate("MainPage"){
    
    
    launchSingleTop = true
}

之后不过怎么跳转,都会用的同一个页面对象

不过一般我们会是在页面之间跳转的,比如说要再MainPage页面跳转到SettingPage页面,要怎么写呢?

img

MainPage传递NavController的参数,之后页面中的按钮来可以使用此对象来完成页面跳转的功能

不过官方比较推荐我们传递一个函数参数,如下代码所示:

//方法需要传函数参数
@Composable
fun MainPage(toSettingPage:()->Unit) {
    
    
    Column() {
    
    
        Text(text = "主页")
        Button(onClick = {
    
     toSettingPage.invoke() }) {
    
    
            Text("跳转到设置页面")
        }
    }
}

NavHost(navController = navController, startDestination = "MainPage") {
    
    
    composable("MainPage"){
    
    
        //这里也要同步修改
        //下面的是kotlin中的lambda的简化写法,不明白的可以先去了解下
        MainPage(){
    
    
            navController.navigate("SettingPage")
        }
    }
    composable("SettingPage"){
    
    
        SettingPage()
    }
}

如何传参

上面的基本使用,例子也是比较简单

一般情况下,我们可能跳转到新页面的时候需要传递数据,比如说从列表页到详情页,需要传递点击的item项的id,从而详情页可以拿到id去请求接口从而获得详情的数据并展示,这个时候该如何操作呢?

针对上面的代码,我们假设进入Setting页面,需要传递一个字符串的参数名为id,则有下面的步骤:

1.修改NavHost定义路由

NavHost(navController = navController, startDestination = "MainPage") {
    
    
    composable("MainPage"){
    
    
        MainPage(){
    
    
            navController.navigate("SettingPage")
        }
    }
    composable("SettingPage/{id}"){
    
    
        //这里获取传参过来数据
        val id = it.arguments?.getString("id")
        //可以传入SettingPage中,然后在页面中使用数据,这里我就没写了
        SettingPage()
    }
}

这里上面,可以看到加了个{id}(有点类似Spring Boot里的接口写法),就是标明需要传参的意思

这里实际上还可以声明类型,默认不写的话,就是说id是String类型的

arguments这个变量实际上就是我们Intent中常用的Bundle,所以获取数值的是调用了getString()方法

设置参数的类型

如果想要id是整数的话,应该怎么做呢?

如下面代码所示:

composable("SettingPage/{id}", arguments = listOf(navArgument("id") {
    
    
    type = NavType.IntType
})) {
    
    
  //省略....
}

通过arguments参数来设置传参对应配置,接收的是一个列表,也很好理解,因为会有多个参数嘛

而list里的元素的类型,则是NamedNavArgument,官方提供navArgument()方法方便我们快速构造NamedNavArgument类型

我们可以看看navArgument方法的参数

navArgument(
    name: String,
    builder: NavArgumentBuilder.() -> Unit
)

name则是需要与我们路由上定义的参数名一致,而builder则是一个函数,我们可以写个函数来快速进行参数的配置,主要可以设置以下三个属性:

  • type 类型
  • defaultValue 默认值
  • nullable 是否可空

PS:改了参数类型,对应的取值调用的getXX等方法也要修改哦!

2.调用传参

上面说了那么多,那么应该如何去调用呢?

NavHost(navController = navController, startDestination = "MainPage") {
    
    
    composable("MainPage") {
    
    
        MainPage() {
    
    
            //跳转的时候传参
            navController.navigate("SettingPage/11?age=18")
        }
    }

    composable("SettingPage/{id}?age={age}", arguments = listOf(
        navArgument("id") {
    
    
            type = NavType.IntType
            defaultValue = 0
            nullable = true
        }, navArgument("age") {
    
    
            type = NavType.IntType
            defaultValue = 0
        }
    )) {
    
    
        //这里获取传参过来数据
        val id = it.arguments?.getInt("id")
        Log.d("starsone", "MyApp: $id")
        SettingPage()
    }
}

由于上面,我们是将跳转封装在了函数,所以只需要调整MainPage路由里的函数即可navController.navigate("SettingPage/11?age=18")

当然,这里age是可以不传的,默认则是0。

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件,需要的小伙伴可以私信我【dd】免费领取或者点击下方二维码免费获取!

![](https://img-blog.csdnimg.cn/img_convert/ea3903bac86d15f9d73df40ce1db2073.jpeg)

navigate(“SettingPage/11?age=18”)`

当然,这里age是可以不传的,默认则是0。

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件,需要的小伙伴可以私信我【面试】免费领取或者点击下方二维码免费获取!

[外链图片转存中…(img-QVlqup4A-1678432162616)]

[外链图片转存中…(img-4I9RLtqN-1678432162616)]

[外链图片转存中…(img-jBBztoPW-1678432162616)]

猜你喜欢

转载自blog.csdn.net/m0_56255097/article/details/129443290#comments_26120526