Combinado con el componente de navegación para implementar la navegación de la interfaz JetPack Compose

Android JetPack Compose puede usar el componente de navegación para implementar la navegación:
1. Configuración del componente de navegación
: Cree un nuevo proyecto y seleccione Actividad de composición vacía.
Luego, configure el siguiente contenido en build.gradle del módulo del proyecto:


	dependencies {
    
    
    	def nav_version = "2.5.2"
    	implementation("androidx.navigation:navigation-compose:$nav_version")
    	......
    }

2. Introducción a la aplicación y clases de entidad
Para ilustrar la aplicación de navegación del componente JetPack Compose, se define una aplicación simple: es decir, mostrar una lista de desplazamiento de robots y luego hacer clic en un solo elemento de la lista de desplazamiento para ingresar un interfaz de robot específica.
lista de robots
Figura 1: Lista de robots
Haga clic en el icono del robot en una fila de la lista para ingresar a la siguiente interfaz.
Insertar descripción de la imagen aquí

Figura 2: Visualización de información de robot individual

Cree una clase que represente la entidad del robot para este propósito, definida de la siguiente manera:

data class Robot(val imageId:Int,val name:String,val description:String):Parcelable{
    
    
    constructor(parcel: Parcel) : this(
        parcel.readInt(),
        parcel.readString()!!,
        parcel.readString()!!
    ) {
    
    
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
    
    
        parcel.writeInt(imageId)
        parcel.writeString(name)
        parcel.writeString(description)
    }

    override fun describeContents(): Int =0

    companion object CREATOR : Parcelable.Creator<Robot> {
    
    
        override fun createFromParcel(parcel: Parcel): Robot {
    
    
            return Robot(parcel)
        }
        override fun newArray(size: Int): Array<Robot?> {
    
    
            return arrayOfNulls(size)
        }
    }
}

3. Defina diferentes interfaces
Defina tres interfaces en esta aplicación:
(1) Defina cada elemento de la lista de desplazamiento en RobotItemView

/**
 * 定义列表单项的视图
 * @param robot Robot
 */
@Composable
fun RobotItemView(robot:Robot){
    
    
    Column{
    
    
        Row(modifier= Modifier
            .fillMaxWidth()
            .border(1.dp,Color.Black)
            .clip(RoundedCornerShape(10.dp))
            .background(colorResource(id = R.color.teal_200))
            .padding(5.dp)){
    
    
            Image(modifier = Modifier
                .width(80.dp)
                .height(80.dp)
                .clip(shape = CircleShape)
                .background(Color.Black)
                .clickable {
    
    
                    //增加导航处理
                },
                painter = painterResource(id = robot.imageId),
                contentDescription = "机器人")
            Column{
    
    
                Text("${robot.name}",fontSize=16.sp,color=Color.Blue)
                Text("${robot.description}",fontSize=20.sp,color=Color.DarkGray)
            }
         }
    }
}

(2) Defina una interfaz RobotListScreen que muestre una lista desplegable de robots

/**
 * Robot list screen
 * 定义显示机器人滚动列表的界面
 */
@Preview
@Composable
fun RobotListScreen(){
    
    
    val robots = mutableListOf<Robot>()
    for(i in 1..20)
        robots.add(Robot(android.R.mipmap.sym_def_app_icon,"第${i}机器人","进入机器人世界"))
    var reverseLayout = false
    Box(modifier= Modifier
        .background(Color.Black)
        .fillMaxSize()){
    
    
        LazyColumn(state= rememberLazyListState(),
            verticalArrangement = if(!reverseLayout) Arrangement.Top else Arrangement.Bottom){
    
    
            items(robots){
    
    robot->
                RobotItemView(robot = robot)
            }
        }
    }
}

(3) RobotScreen, la interfaz que define información específica del robot

/**
 * 定义机器人具体信息显示界面
 * @param robot Robot
 */
@Composable
fun RobotScreen(){
    
    
    val robot = Robot(android.R.mipmap.sym_def_app_icon,"第1号机器人","第1号机器人进入机器人的世界")

    Column(modifier = Modifier
        .background(Color.Black)
        .padding(20.dp)
        .fillMaxSize(),
        verticalArrangement = Arrangement.Center){
    
    
        Row(verticalAlignment = Alignment.CenterVertically){
    
    
            Image(modifier= Modifier
                .width(160.dp)
                .height(160.dp),
                painter= painterResource(id = robot.imageId),
                contentDescription = "${robot.description}")
            Text("${robot.name}",fontSize=36.sp,color=Color.White)
        }
        Text("${robot.description}",fontSize=24.sp,color=Color.Yellow)
    }
}

Para identificar y manejar mejor estas diferentes interfaces, la clase sellada Screens se define para crear objetos de entidad de las respectivas interfaces:

/**
 * 定义可显示的界面
 * @property route String 导航线路名称
 * @property title String 界面标题
 * @constructor
 */
sealed class Screens(val route:String,val title:String){
    
    
    object HomePage:Screens("home","机器人列表")
    object RobotPage:Screens("robot","机器人详细信息")
}

4. Implementar el cambio de navegación entre diferentes interfaces
. Para implementar la navegación, es necesario definir un mapa de navegación para indicar la dirección de navegación entre cada interfaz. Para este fin:

/**
 * Navigation graph screen
 * 定义导航图
 */
@Composable
fun NavigationGraphScreen(){
    
    
    //获取导航控制器
    val navController = rememberNavController()
    NavHost(navController, startDestination = Screens.HomePage.route){
    
    
        composable(Screens.HomePage.route){
    
    
            RobotListScreen()
        }
        composable(Screens.RobotPage.route){
    
    
            RobotScreen()
        }
    }
}

Hasta ahora, la navegación no se ha implementado. Esto se debe a que falta un objeto controlador de navegación en el control de navegación para manejar las acciones de navegación, y la definición de acción de clic en Imagen de RobotItemView está vacía, por lo que es imposible implementar la navegación; por lo tanto, lo siguiente Se realizan modificaciones
:
(1) Redefinir el mapa de navegación.

/**
 * Navigation graph screen
 * 定义导航图
 */
@Preview
@Composable
fun NavigationGraphScreen(){
    
    
    //获取导航控制器
    val navController = rememberNavController()
    NavHost(navController, startDestination = Screens.HomePage.route){
    
    
        composable(Screens.HomePage.route){
    
    
            RobotListScreen(navController)
        }
        composable(Screens.RobotPage.route){
    
    
            RobotScreen()
        }
    }
}

En el código anterior, NavHost es el mapa de navegación y el contenedor de NavController, que especifica la ruta inicial de navegación, es decir, Screens.HomePage.route.

(2) Modifique RobotListScreen y agregue un controlador de navegación para que tenga la capacidad de navegar por la interfaz

/**
 * Robot list screen
 * 定义显示机器人滚动列表的界面
 */
@Composable
fun RobotListScreen(navController:NavController){
    
    //增加导航控制器对象
    val robots = mutableListOf<Robot>()
    for(i in 1..20)
        robots.add(Robot(android.R.mipmap.sym_def_app_icon,"第${i}机器人","进入机器人世界"))
    var reverseLayout = false
    Box(modifier= Modifier
        .background(Color.Black)
        .fillMaxSize()){
    
    
        LazyColumn(state= rememberLazyListState(),
            verticalArrangement = if(!reverseLayout) Arrangement.Top else Arrangement.Bottom){
    
    
            items(robots){
    
    robot->
                RobotItemView(navController ,robot = robot) //将导航控制器对象传递给单项视图
            }
        }
    }
}

(3) Modifique RobotItemView, pásele el objeto controlador de navegación en el gráfico de navegación como parámetro y agregue procesamiento de navegación:

/**
 * 定义列表单项的视图
 * @param robot Robot
 */
@Composable
fun RobotItemView(navController:NavController,robot:Robot){
    
    
    Column{
    
    
        Row(modifier= Modifier
            .fillMaxWidth()
            .border(1.dp, Color.Black)
            .clip(RoundedCornerShape(10.dp))
            .background(colorResource(id = R.color.teal_200))
            .padding(5.dp)){
    
    
            Image(modifier = Modifier
                .width(80.dp)
                .height(80.dp)
                .clip(shape = CircleShape)
                .background(Color.Black)
                .clickable {
    
    
                    //增加导航处理
                    //根据导航路线robot到Screens.RobotPage对应的RobotScreen定义的界面
                    navController.navigate("robot")
                },
                painter = painterResource(id = robot.imageId),
                contentDescription = "机器人")
            Column{
    
    
                Text("${robot.name}",fontSize=16.sp,color=Color.Blue)
                Text("${robot.description}",fontSize=20.sp,color=Color.DarkGray)
            }
         }
    }
}

En la posición actual, el icono de un solo elemento se selecciona de la lista de desplazamiento del robot y salta a la página siguiente. Sin embargo, dado que cada salto es una interfaz con los mismos datos, no se ajusta a la situación real. Lo que realmente hay que hacer es seleccionar un icono de un solo elemento de la lista de desplazamiento y hacer clic en él para ingresar a la interfaz de información detallada de este "robot", por lo que se deben pasar los datos correspondientes.

5. Pasar datos en la navegación
1. Pasar tipos básicos de datos
Suponiendo que se pasa una cadena al saltar de la lista de desplazamiento a la interfaz de información detallada del robot, modifique la interfaz de vista de un solo elemento de la lista del robot y agregue el procesamiento de envío de datos para hacer clic acciones: (1
) Modificar la función RobotItemScreen y agregar procesamiento de envío de datos

@Composable
fun RobotItemView(navController:NavController,robot:Robot){
    
    
    Column{
    
    
        Row(modifier= Modifier
            .fillMaxWidth()
            .border(1.dp, Color.Black)
            .clip(RoundedCornerShape(10.dp))
            .background(colorResource(id = R.color.teal_200))
            .padding(5.dp)){
    
    
            Image(modifier = Modifier
                .width(80.dp)
                .height(80.dp)
                .clip(shape = CircleShape)
                .background(Color.Black)
                .clickable {
    
    
                    //增加导航处理,发送方在导航路线中发送字符串数据
                    navController.navigate("robot/${robot.toString()}")
                },
                painter = painterResource(id = robot.imageId),
                contentDescription = "机器人")
            Column{
    
    
                Text("${robot.name}",fontSize=16.sp,color=Color.Blue)
                Text("${robot.description}",fontSize=20.sp,color=Color.DarkGray)
            }
        }
    }
}

(2) Modificar el mapa de navegación
Modifique el mapa de navegación, especifique el nombre del parámetro y el tipo de parámetro para el receptor de los datos en el mapa de navegación y modifique la ruta de navegación a un formulario con parámetros:

/**
 * Navigation graph screen
 * 定义导航图
 */
@Preview
@Composable
fun NavigationGraphScreen(){
    
    
    //获取导航控制器
    val navController = rememberNavController()
    NavHost(navController, startDestination = Screens.HomePage.route){
    
    
        //数据的发送方
        composable(Screens.HomePage.route){
    
    
            RobotListScreen(navController)
        }
        //数据的接收方
        composable(route=Screens.RobotPage.route+"/{robot}",//修改导航路线,增加要传递的参数名称
        arguments=listOf(navArgument("robot"){
    
    type= NavType.StringType}))//指定接收的参数和参数类型
        {
    
    
            val robotStr=it.arguments?.getString("robot")?:"没有任何信息,接收参数失败"
            RobotScreen(robotStr)
        }
    }
}

(3) En la interfaz de información detallada del robot del receptor, especifique el procesamiento de los parámetros de recepción y los tipos de parámetros. El código es el siguiente:

/**
 * 定义机器人具体信息显示界面
 * @param robot Robot
 */
@Composable
fun RobotScreen(robot:String){
    
    
    Column(modifier = Modifier
        .background(Color.Black)
        .padding(20.dp)
        .fillMaxSize(),
        verticalArrangement = Arrangement.Center){
    
    
        Row(verticalAlignment = Alignment.CenterVertically){
    
    
            Image(modifier= Modifier
                .width(160.dp)
                .height(160.dp),
                painter= painterResource(id = android.R.mipmap.sym_def_app_icon),
                contentDescription = "${robot}")
        }
        Text("${robot}",fontSize=24.sp,color=Color.Yellow)
    }
}

Después de dicho procesamiento, navegar a la interfaz de detalles del robot se ve así:
Insertar descripción de la imagen aquí
Figura 3: Recibir los datos de cadena pasados

2. Pasar datos de tipo personalizado
. En situaciones reales, a menudo es necesario pasar datos de objetos de tipo personalizado. Por ejemplo, ¿qué debemos hacer cuando el objeto Robot mencionado anteriormente implementa la interfaz Parcelable? Si dicho objeto se pasa directamente a la siguiente interfaz, el formulario es el siguiente:
(1) Modificar el gráfico de navegación
Modifique el tipo de parámetro del receptor en el gráfico de navegación al tipo de Robot.

@Preview
@Composable
fun NavigationGraphScreen(){
    
    
    //获取导航控制器
    val navController = rememberNavController()
    NavHost(navController, startDestination = Screens.HomePage.route){
    
    
        //数据的发送方
        composable(Screens.HomePage.route){
    
    
            RobotListScreen(navController)
        }
        //数据的接收方
        composable(route=Screens.RobotPage.route+"/{robot}",//修改导航路线,增加要传递的参数名称
        arguments=listOf(navArgument("robot"){
    
    
             //指定接收的参数和参数类型 
            type= NavType.inferFromValueType(Robot(R.mipmap.ic_launcher,"",""))}))
        {
    
    
            val robot:Robot=it.arguments?.getParcelable("robot")?:Robot(android.R.mipmap.sym_def_app_icon,"测试","机器人信息获取失败")
            RobotScreen(robot)
        }
    }
}

(2) Modifique la interfaz de la parte receptora de datos.
La interfaz de la parte emisora ​​de datos aún mantiene el contenido anterior. No se requiere ninguna modificación. Solo necesita modificar el procesamiento de la interfaz de la parte receptora y cambiar el tipo de parámetro recibido por el La función RobotScreen de la parte receptora de cadena a tipo de robot, GUI. La interfaz puede manejarlo en consecuencia, como se muestra en el siguiente código:

@Composable
fun RobotScreen(robot:Robot){
    
    //修改参数类型为Robot
    Column(modifier = Modifier
        .background(Color.Black)
        .padding(20.dp)
        .fillMaxSize(),
        verticalArrangement = Arrangement.Center){
    
    
        Row(verticalAlignment = Alignment.CenterVertically){
    
    
            Image(modifier= Modifier
                .width(160.dp)
                .height(160.dp),
                painter= painterResource(id = robot.imageId),
                contentDescription = "${robot.description}")
            Text("${robot.name}",fontSize=36.sp,color=Color.White)
        }
        Text("${robot.description}",fontSize=24.sp,color=Color.Yellow)
    }
}

Lanzará java.lang.UnsupportedOperationException: Parcelables no admite valores predeterminados
porque los datos de tipo Parcelable no admiten valores predeterminados. Si se pasa directamente, se generará una excepción de operación no admitida. Por tanto, se necesitan otras formas de pasar objetos de tipos personalizados. 3. Una solución para usar Gson para transferir datos personalizados es convertir los datos del objeto de tipo personalizado en una cadena en formato JSON en el remitente y luego convertir la cadena recibida en un objeto de tipo personalizado en el lado del receptor
.
Esto se logra con la ayuda del marco Gson.
(1) Agregue la dependencia de Gson.
Debe agregar la dependencia del marco Gson en el build.gradle del módulo de la siguiente forma:

dependencies {
    
    
 	implementation 'com.google.code.gson:gson:2.10'
 	...
 }

(2) Definición de vista de un solo elemento de la lista de desplazamiento

/**
 * 定义列表单项的视图
 * @param robot Robot
 */
@Composable
fun RobotItemView(navController:NavController,robot:Robot){
    
    
    Column{
    
    
        Row(modifier= Modifier
            .fillMaxWidth()
            .border(1.dp, Color.Black)
            .clip(RoundedCornerShape(10.dp))
            .background(colorResource(id = R.color.teal_200))
            .padding(5.dp)){
    
    
            Image(modifier = Modifier
                .width(80.dp)
                .height(80.dp)
                .clip(shape = CircleShape)
                .background(Color.Black)
                .clickable {
    
    
                    val robotStr = Gson().toJson(robot)
                    //增加导航处理,发送方在导航路线中发送字符串数据
                    navController.navigate("robot/${robotStr}")
                },
                painter = painterResource(id = robot.imageId),
                contentDescription = "机器人")
            Column{
    
    
                Text("${robot.name}",fontSize=16.sp,color=Color.Blue)
                Text("${robot.description}",fontSize=20.sp,color=Color.DarkGray)
            }
        }
    }
}

(3) Modificar la definición de la interfaz de lista de desplazamiento

/**
 * Robot list screen
 * 定义显示机器人滚动列表的界面
 */
@Composable
fun RobotListScreen(navController:NavController){
    
    
    val robots = mutableListOf<Robot>()
    for(i in 1..20)
        robots.add(Robot(android.R.mipmap.sym_def_app_icon,"第${i}机器人","第${i}机器人进入机器人世界"))
    var reverseLayout = false
    Box(modifier= Modifier
        .background(Color.Black)
        .fillMaxSize()){
    
    
        LazyColumn(state= rememberLazyListState(),
            verticalArrangement = if(!reverseLayout) Arrangement.Top else Arrangement.Bottom){
    
    
            items(robots){
    
    robot->
                RobotItemView(navController,robot = robot)
            }
        }
    }
}

El código no ha cambiado
(4) Definición de la interfaz para recibir datos

@Composable
fun RobotScreen(robot:Robot){
    
    
    Column(modifier = Modifier
        .background(Color.Black)
        .padding(20.dp)
        .fillMaxSize(),
        verticalArrangement = Arrangement.Center){
    
    
        Row(verticalAlignment = Alignment.CenterVertically){
    
    
            Image(modifier= Modifier
                .width(160.dp)
                .height(160.dp),
                painter= painterResource(id = robot.imageId),
                contentDescription = "${robot.description}")
            Text("${robot.name}",fontSize=36.sp,color=Color.White)
        }
        Text("${robot.description}",fontSize=24.sp,color=Color.Yellow)
    }
}

(5) Modificar mapa de navegación

@Preview
@Composable
fun NavigationGraphScreen(){
    
    
    //获取导航控制器
    val navController = rememberNavController()
    NavHost(navController, startDestination = Screens.HomePage.route){
    
    
        //数据的发送方
        composable(Screens.HomePage.route){
    
    
            RobotListScreen(navController)
        }
        //数据的接收方
        composable(route=Screens.RobotPage.route+"/{robot}",//修改导航路线,增加要传递的参数名称
        arguments=listOf(navArgument("robot"){
    
    
            type= NavType.StringType}))//指定接收的参数和参数类型为字符串
        {
    
    
            val robotJsonStr=it.arguments?.getString("robot")?:"接收错误的参数"
            RobotScreen(Gson().fromJson(robotJsonStr,Robot::class.java))//将字符串转换成Robot对象
        }
    }
}

Llame a la interfaz del gráfico de navegación en la actividad principal, el código es el siguiente:

class MainActivity : ComponentActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContent {
    
    
            Ch04_ComposeTheme {
    
    
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
    
    
                    NavigationGraphScreen()
                }
            }
        }
    }
}

El resultado final de la ejecución es el siguiente:
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí

En este momento, haga clic en cualquier icono de elemento de la lista de desplazamiento para ingresar a la interfaz especificada.
Referencias
Uso de Compose para la navegación https://developer.android.google.cn/reference/androidx/navigation/NavHost?hl=zh-cn

Supongo que te gusta

Origin blog.csdn.net/userhu2012/article/details/127601476
Recomendado
Clasificación