Android笔记(十):结合Navigation组件实现Compose界面的导航

这篇具有很好参考价值的文章主要介绍了Android笔记(十):结合Navigation组件实现Compose界面的导航。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在Android笔记(七)搭建Android JetPack Compose组件中Scaffold脚手架 一文中通过定义一个导航的函数来实现不同界面的切换。如果没有传递任何参数,这样的导航处理也是可以接受的,处理方式也非常简单。但是,如果考虑到不同Compose界面的切换且传递参数,或者有更复杂地处理情况,就可以考虑使用导航组件Navigation来实现。

一、导航组件的配置

新建一个项目模块,在模块对应的build.gradle.kt中中增加依赖:

implementation(“androidx.navigation:navigation-compose:2.7.4”)

注意:采用2.7.4版本,必须设置android的目标SDK和编译SDK为34版本

在本笔记中通过一个展示机器人相关信息来说明导航组件的实现。为此,分别定义实体类和不同的界面。

二、定义机器人实体类

@Parcelize
data class Robot(val name:String,val description:String,val icon:Int):Parcelable

三、定义要显示的主要界面

1.定义机器人列表界面

compose 导航,android,笔记,navigation组件,compose
图1 显示机器人列表
(1)定义单独一行机器人卡片
注意:
因为使用了ConstraintLayout布局,需要在项目模块的build.gradle.kt中增加依赖:

implementation(“androidx.constraintlayout:constraintlayout-compose:1.0.1”)

具体的代码如下:

//定义单独的机器人单独一行的卡片
@Composable
fun RobotCard(robot:Robot){
      Card(modifier = Modifier
          .fillMaxWidth()
          .wrapContentHeight()
          .padding(5.dp),
       colors = CardDefaults.elevatedCardColors(contentColor = Color.Green, containerColor = Color.Blue),
       elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp)){

       ConstraintLayout(modifier= Modifier.fillMaxWidth()) {
           val (imageRef,nameRef,descRef) = remember{createRefs()}
           val vguideLine = createGuidelineFromStart(0.3f)
           val hguideLine = createGuidelineFromTop(0.4f)

           Image(modifier= Modifier
               .constrainAs(imageRef) {
                   top.linkTo(parent.top)
                   bottom.linkTo(parent.bottom)
                   start.linkTo(parent.start)
                   end.linkTo(vguideLine)
               }
               .clickable {

               },painter = painterResource(id = robot.icon), contentDescription = robot.name )

           Text(modifier = Modifier.constrainAs(nameRef){
                  top.linkTo(parent.top)
                  bottom.linkTo(hguideLine)
                  start.linkTo(vguideLine)
                  end.linkTo(parent.end)
              },
               text = robot.name,fontSize = 18.sp)

           Text(modifier = Modifier.constrainAs(descRef){
                  top.linkTo(hguideLine)
                  bottom.linkTo(parent.bottom)
                  start.linkTo(vguideLine)
                  end.linkTo(parent.end)
               },
               text = robot.description,fontSize = 20.sp)
       }
  }
}

(2)定义组合多个机器人卡片生成机器人列表界面

@Preview
@Composable
fun RobotListScreen(){
    val robots = mutableListOf<Robot>()
    for(i in 1..10)
        robots.add(Robot("机器人${i}号","机器人${i}号的世界",android.R.mipmap.sym_def_app_icon))

    LazyColumn{
        items(robots){robot:Robot->
            RobotCard(robot)
        }
    }
}

2.定义机器人单独显示界面

compose 导航,android,笔记,navigation组件,compose
图2

@Composable
fun RobotDetailScreen(){
    val robot = Robot("机器人测试","机器人世界",android.R.mipmap.sym_def_app_icon)

    Box(contentAlignment = Alignment.Center,
        modifier= Modifier.fillMaxSize()){

        Column(horizontalAlignment = Alignment.CenterHorizontally){
            Text(robot.name,fontSize = 28.sp)

            Image(modifier = Modifier.size(100.dp,100.dp),
                  painter = painterResource(id = robot.icon),
                  contentDescription = robot.name)

            Text(robot.description,fontSize=24.sp,maxLines = 5)

            Button(onClick = {

            }){
                Text("跳转APP说明")
            }
        }
    }
}

3.定义应用介绍界面

compose 导航,android,笔记,navigation组件,compose
图3

@Composable
fun RobotAppScreen(){
    Box(contentAlignment = Alignment.Center,modifier = Modifier.fillMaxSize()){
        Column(modifier = Modifier.fillMaxWidth(),
            horizontalAlignment = Alignment.CenterHorizontally){
            Text("RobotApp是一个简单显示机器人信息的应用",fontSize = 28.sp, maxLines = 5)
            Button(
                modifier = Modifier.wrapContentSize(),
                onClick = {

            }){
                Text("返回",fontSize = 20.sp)
            }
        }
    }
}

四.定义界面的密封类

定义关于界面的密封类,在密封类对上述的三个界面基本特征如导航路径route、导航使用的标题title以及导航使用的图表icon进行分别定义,代码如下所示:

sealed class Screen(val route:String,val title:String,val icon: ImageVector){
    object RobotListPage:Screen(route="robotlist",title="机器人列表",icon= Icons.Filled.List)
    object RobotPage:Screen(route = "robot",title="机器人",icon = Icons.Filled.Face)
    object AppPage:Screen(route="aboutApp",title = "APP介绍",icon = Icons.Filled.Info)
}

在此前提的基础上,定义一个列表,将三个核心显示的界面定义在列表中,如下所示:

val screens = listOf(Screen.RobotListPage,Screen.RobotPage,Screen.AppPage)

五、定义导航图

在导航图中需要确定导航图中宿主和要切换的界面。要让定义好的导航图发挥作用

(1)需要在活动中指定导航宿主NavHost,通过指定NavHost表示导航宿主,用于定义用户界面的屏幕目标的预留位置;
(2) 需要利用NavController导航控件实现导航。
val navController = rememberNavController()

值得注意的是:NavController可以调用navigate()函数来实现导航,常见有三种形式:

//在导航到route的目的地之前,要将路径home之前所有的内容退出后退堆栈
navController.navigate(“route”){
popUpTo(“home”)
}

//在导航到route的目的地之前,要将包括路径home之前所有的内容退出后退堆栈
navController.navigate(“route”){
popUpTo(“home”) {inclusive = true}
}

//当没有导航到route时,才导航到route路径后退堆栈只有一个route导航路径
navController.navigate(“route”){
launchSingleTop = true
}

在下列定义了一个简单的导航图,代码如下:

@Composable
fun NavigationGraphScreen(navController:NavHostController,startDestination:String){
    NavHost(navController = navController,startDestination=Screen.RobotListPage.route){
        composable(Screen.RobotListPage.route){
            RobotListScreen()
        }

        composable(Screen.RobotPage.route){
            RobotDetailScreen()
        }

        composable(Screen.AppPage.route){
            RobotAppScreen()
        }
    }
}

六、在脚手架结构的底部导航栏中使用导航图

compose 导航,android,笔记,navigation组件,compose

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(){
    val currentScreen: MutableState<Screen> = remember{mutableStateOf(Screen.RobotListPage)}
    val navController = rememberNavController()
    Scaffold(
        topBar={
            TopAppBar(
                title = {
                       Text(text = currentScreen.value.title )
                },
                navigationIcon = {
                    Icon(imageVector = currentScreen.value.icon,contentDescription = null)
                }
            )
        },
        bottomBar = {
            BottomAppBar {
                screens.forEach {screen:Screen->
                    NavigationBarItem(
                        selected = screen.route == currentScreen.value.route ,
                        onClick = {
                                  currentScreen.value = screen
                                  navController.navigate(screen.route){
                                      popUpTo(Screen.RobotListPage.route)
                                      launchSingleTop
                                  }
                                  },
                        icon = {
                            Icon(imageVector = screen.icon,contentDescription = screen.title)
                        })
                }
            }
       },
       content={it:PaddingValues->
           Box(modifier = Modifier.padding(it)){
                NavigationGraphScreen(
                    navController = navController,
                    startDestination =Screen.RobotListPage.route )
           }
       })
}

注意:
(1)导航navController.navigate(screen.route)中设置popUpTo(Screen.RobotListPage.route),表示导航到screen.route的界面会将Screen.RobotListPage.route之前(不包含)的所有其他界面退出返回堆栈BackStack。可以观察到,通过底部导航栏,如上图依次从左到右访问三个界面,在最后一个界面APP介绍界面时,选择手机模拟器的Back键,会直接返回第一个界面RobotListPage.route路径指向的界面。
(2)在脚手架Scaffold的content属性设置核心显示内容为导航图NavigationGraphScreen

七、实现导航传递参数

在上述的运行效果中,可以发现如下不足:

从机器人列表Screen.RobotListPage.route跳转到单个机器人界面Screen.RobotPage.route路径时,只能固定的显示机器人测试信息,显然不合实际情况;

解决之道:就是在不同导航中设置参数传递。

(1)发送方:
navController.navigate(“route/${参数}”)

(2)数据接受方:
在导航图中指定接受参数名和参数类型

  composable(route = "route/{参数名}",
             arguments = listOf(arguments = listOf(navArgument(“参数名"){
                                                        type =NavType.StringType})
                                                        ){

        val data = it.arguments?.getString(“参数名”)?:"默认值"
        }

在传递数据如果是基本数据类型比较容易,但是对于自定义的数据类,则可以采用转换的方式实现数据传递:
(1)数据发送方,把自定义类型的对象通过Gson库转换成字符串;
(2)数据接受方,将接受的数据字符串通过Gson库再转换成自定义类型的对象
为了使用Gson库,需要在模块的build.gradle.kt中增加依赖,如下所示:

implementation(“com.google.code.gson:gson:2.10.1”)

1.数据发送方

将机器人列表界面RobotListScreen作为数据的发送方,每次点击列表的图标,则传递数据到下一个界面RobotDetailScreen;由于点击实际的图标定义在RobotCard组合项中,因此,为RobotCard定义的图片内容增加一个点击处理的功能;因为要实现导航,因此RobotListScreen和RobotCard都需要传递NavController参数,下面是修改的代码:

@Composable
fun RobotCard(navController:NavController,robot:Robot){
      Card(modifier = Modifier
          .fillMaxWidth()
          .wrapContentHeight()
          .padding(5.dp),
       colors = CardDefaults.elevatedCardColors(contentColor = Color.Green, containerColor = Color.Blue),
       elevation = CardDefaults.elevatedCardElevation(defaultElevation = 3.dp)){

       ConstraintLayout(modifier= Modifier.fillMaxWidth()) {
           val (imageRef,nameRef,descRef) = remember{createRefs()}
           val vguideLine = createGuidelineFromStart(0.3f)
           val hguideLine = createGuidelineFromTop(0.4f)

           Image(modifier= Modifier
               .constrainAs(imageRef) {
                   top.linkTo(parent.top)
                   bottom.linkTo(parent.bottom)
                   start.linkTo(parent.start)
                   end.linkTo(vguideLine)
               }
               .clickable {
                    val robotStr = Gson().toJson(robot)
                    navController.navigate(Screen.RobotPage.route+"/${robotStr}")
               },painter = painterResource(id = robot.icon), contentDescription = robot.name )

           Text(modifier = Modifier.constrainAs(nameRef){
                  top.linkTo(parent.top)
                  bottom.linkTo(hguideLine)
                  start.linkTo(vguideLine)
                  end.linkTo(parent.end)
              },
               text = robot.name,fontSize = 18.sp)

           Text(modifier = Modifier.constrainAs(descRef){
                  top.linkTo(hguideLine)
                  bottom.linkTo(parent.bottom)
                  start.linkTo(vguideLine)
                  end.linkTo(parent.end)
               },
               text = robot.description,fontSize = 20.sp)
       }
  }
}
@Composable
fun RobotListScreen(navController: NavController){
    val robots = mutableListOf<Robot>()
    for(i in 1..10)
        robots.add(Robot("机器人${i}号","机器人${i}号的世界",android.R.mipmap.sym_def_app_icon))

    LazyColumn{
        items(robots){robot:Robot->
            RobotCard(navController,robot)
        }
    }
}

2.数据接受方

在这个应用中RobotDetailScreen是数据的接受方,因此需要修改导航图,在导航图中增加接受数据的处理,代码如下:

@Composable
fun NavigationGraphScreen(navController:NavHostController,startDestination:String){
    NavHost(navController = navController,startDestination=Screen.RobotListPage.route){
        composable(Screen.RobotListPage.route){
            RobotListScreen(navController = navController)
        }

        composable(Screen.RobotPage.route+"/{robot}", arguments = listOf(navArgument("robot"){
            type = NavType.StringType
        })){
            val robotStr = it.arguments?.getString("robot")
            val robot = Gson().fromJson(robotStr,Robot::class.java)
            RobotDetailScreen(robot)
        }

        composable(Screen.AppPage.route){
            RobotAppScreen()
        }
    }
}

相应的RobotDetailScreen也需要修改,增加一个Robot参数,可以对具体的Robot对象进行显示,代码修改如下:

@Composable
fun RobotDetailScreen(robot:Robot){
    Box(contentAlignment = Alignment.Center,
        modifier= Modifier.fillMaxSize()){

        Column(horizontalAlignment = Alignment.CenterHorizontally){
            Text(robot.name,fontSize = 28.sp)

            Image(modifier = Modifier.size(100.dp,100.dp),
                  painter = painterResource(id = robot.icon),
                  contentDescription = robot.name)

            Text(robot.description,fontSize=24.sp,maxLines = 5)

            Button(onClick = {

            }){
                Text("跳转APP说明")
            }
        }
    }
}

3.调整底部栏导航处理

因为增加了数据发送和接受的处理,相应的针对脚手架的底部栏导航的处理也需要调整,需要增加发送和接受数据的处理:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(){
    val currentScreen: MutableState<Screen> = remember{mutableStateOf(Screen.RobotListPage)}
    val currentRobot:MutableState<Robot> = remember{mutableStateOf(Robot("机器人${1}号","机器人${1}号的世界",android.R.mipmap.sym_def_app_icon))}
    val navController = rememberNavController()
    Scaffold(
        topBar={
            TopAppBar(
                title = {
                       Text(text = currentScreen.value.title )
                },
                navigationIcon = {
                    Icon(imageVector = currentScreen.value.icon,contentDescription = null)
                }
            )
        },
        bottomBar = {
            BottomAppBar {
                screens.forEach {screen:Screen->
                    NavigationBarItem(
                        selected = screen.route == currentScreen.value.route ,
                        onClick = {
                                  currentScreen.value = screen
                                  if(screen.route == Screen.RobotPage.route){
                                      val robotStr = Gson().toJson(currentRobot.value)
                                      navController.navigate(Screen.RobotPage.route+"/${robotStr}"){
                                          popUpTo(Screen.RobotListPage.route)
                                      }
                                  }else {
                                      navController.navigate(screen.route) {
                                          popUpTo(Screen.RobotListPage.route)
                                          launchSingleTop
                                      }
                                  }
                                  },
                        icon = {
                            Icon(imageVector = screen.icon,contentDescription = screen.title)
                        })
                }
            }
       },
       content={it:PaddingValues->
           Box(modifier = Modifier.padding(it)){
                NavigationGraphScreen(
                    navController = navController,
                    startDestination =Screen.RobotListPage.route )
           }
       })
}

在MainScreen中增加判断是否跳转到RobotDetailScreen界面路径的判断,如果是,就增加参数传递的处理。在上述代码中,增加了一个状态currentRobot来记住当前要显示的机器人对象。这里的处理并不是非常好,因为并没有提供处理修改currentRobot的状态值的操作。最好的方式,是将这个状态值提升,并通过机器人列表界面点击图片达到修改这个状态值的目的。在此处,就不再修改了,由读者自行调整。文章来源地址https://www.toymoban.com/news/detail-813434.html

到了这里,关于Android笔记(十):结合Navigation组件实现Compose界面的导航的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android笔记(九):Compose组件的状态(一)

    在使用Compose定义UI界面时,可以发现界面的变换往往与Compose组件内部的状态相关,当状态值发生变化时,Compose构成的可组合的界面也会刷新发生相应的变化。将在本笔记中将对可组合项的状态的定义、状态提升、状态丢失和状态的保存进行简单介绍。。 Compose采用了单向数据

    2024年02月06日
    浏览(46)
  • Android笔记(六):JetPack Compose常见的UI组件

    Text显示的文本来源可以引用res-values-strings.xml中的资源,如第一个显示文本所示。 点击按钮前: 点击按钮后: 点击第一个圆角按钮不放时,显示为按钮:true Button有两方面需要注意: (1) Buttton有一个参数interactionSource,用来监听组件状态的事件源,通过它获取组件的状态来

    2024年02月04日
    浏览(54)
  • Android笔记(七)Android JetPack Compose组件搭建Scaffold脚手架

    在去年2022年曾发布一篇关于脚手架的文章:“Android JetPack Compose组件中Scaffold的应用” 。但是Android的版本从12变更到13及以上版本,导致一些细节的实现存在不同。在本文中,将从头开始介绍整个脚手架的搭建过程。 在Android Studio(版本是Graffie)中新建模块,选择“Empty Activ

    2024年02月04日
    浏览(47)
  • 安卓学习笔记之五:Android Studio_骰子案例3(Kotlin搭配 Jetpack Compose实现)

    使用 Compose 创建一款交互式  Dice Roller  Android 应用。 完成: 定义可组合函数。 使用组合创建布局。 使用  Button  可组合项创建按钮。 导入  drawable  资源。 使用  Image  可组合项显示图片。 使用可组合项构建交互式界面。 使用  remember  可组合项将组合中的对象存储到内

    2024年02月20日
    浏览(49)
  • Android实现底部导航栏方法(Navigation篇)

    底部导航栏一直是大部分App不可缺失的一部分 最近注意到Jetpack中的Navigation支持Fragment的切换操作 特此浅研究一下 选择性跳过 此处使用Google开发者文档中介绍 使用nav文件配合 FragmentContainerView组件 实现Fragment的切换操作 创建nav文件 导入后,在项目的res文件夹下,右键选择

    2024年02月06日
    浏览(89)
  • 利用Jetpack Compose进行导航(Navigation)

    Jetpack Compose是一个现代化的,声明式的UI工具包,它让我们可以更快、更简便地构建Android的界面。今天,我们要讨论如何使用Jetpack Compose和它的导航库(Navigation Compose)来进行应用导航。 Navigation Compose是一个用于管理Compose界面中的导航的库,它不仅提供了丰富的API以支持不

    2024年02月12日
    浏览(47)
  • 探索Android Jetpack Compose的Surface组件

    随着声明性 UI 框架 Jetpack Compose 的出现,Android 开发变得更加简洁和直观。在这篇博客中,我们将深入探讨其中的一项基本构建块 —— Surface 组件,了解它如何影响 UI 的显示和设计。 一、Jetpack Compose和Surface组件 二、Surface组件的基本使用 三、影响Surface的属性 一、Jetpack Co

    2024年02月11日
    浏览(58)
  • Android笔记(二十一):Room组件实现Android应用的持久化处理

    Room是Android JetPack架构组件之一,是一个持久处理的库。Room提供了在SQLite数据库上提供抽象层,使之实现数据访问。 (1)实体类(Entity):映射并封装了数据库对应的数据表中对应的结构化数据。实体定义了数据库中的数据表。实体类中的数据域与表的列一一对应。 (2)数

    2024年01月20日
    浏览(54)
  • android界面开发详解,Unity实战问题--Loading更好的实现方式,714页PDF的鸿蒙学习笔记,

    最后进度条的效果显示如下: 进度条并没有连续的显示加载的进度,而是停顿一下切换一个数字,再停顿一下切换一个数子,最后在没有显示100%就情况下就切换到主场景了。究其原因在于 Application.LoadLevelAsync 并不是真正的后台加载,它在每一帧加载一些游戏资源,并给出一个

    2024年04月26日
    浏览(37)
  • Android Jetpack组件库(第七部分)---UI工具包 Compose

    Android Jetpack 是 Google 推出的一整套帮助 Android 应用程序开发的库、工具包和架构指南,旨在为 Android 应用程序提供更快,更轻松,更稳定的开发体验。自推出以来已经发展成了一个庞大的技术生态系统,包括了许多使用方便、功能强大的库,以下是其中一些新特性、新组件:

    2024年01月16日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包