Navigation コンポーネントと組み合わせて、JetPack Compose インターフェイス ナビゲーションを実装します。

Android JetPack Compose では、Navigation コンポーネントを使用してナビゲーションを実装できます
1. Navigation コンポーネントの構成
新しいプロジェクトを作成し、Empty Compose Activity を選択します。
次に、プロジェクト モジュールの build.gradle に次の内容を設定します。


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

2. アプリケーションの導入とエンティティ クラス
JetPack Compose コンポーネントのナビゲーション アプリケーションを説明するために、単純なアプリケーションを定義します。つまり、ロボットのスクロール リストを表示し、スクロール リスト内の 1 つの項目をクリックして、特定のロボットインターフェイス。
ロボット一覧
図 1: ロボット リスト
リストの行にあるロボット アイコンをクリックして、次のインターフェイスに入ります。
ここに画像の説明を挿入します

図2: 個々のロボット情報の表示

この目的のために、次のように定義されるロボット エンティティを表すクラスを作成します。

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. さまざまなインターフェイスを定義します。
このアプリケーションで 3 つのインターフェイスを定義します。
(1) 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) ロボットのスクロールリストを表示するインターフェース RobotListScreen を定義する

/**
 * 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」

/**
 * 定义机器人具体信息显示界面
 * @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)
    }
}

これらの異なるインターフェイスをより適切に識別して処理するために、シールされたクラス Screens が定義され、それぞれのインターフェイスのエンティティ オブジェクトが作成されます。

/**
 * 定义可显示的界面
 * @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. 異なるインターフェース間のナビゲーション切り替えを実装します
。ナビゲーションを実装するには、各インターフェース間のナビゲーション方向を示すナビゲーション マップを定義する必要があります。このためには、次の操作を行います。

/**
 * 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()
        }
    }
}

これまでナビゲーションは実装されていませんでしたが、これは、ナビゲーション コントロールにナビゲーション アクションを処理するためのナビゲーション コントローラー オブジェクトが不足しており、RobotItemView の Image クリック アクション定義が空であるため、ナビゲーションを実装することができないためです。変更が加えられます
:
(1) ナビゲーション マップを再定義します。

/**
 * 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()
        }
    }
}

上記のコードでは、NavHost はナビゲーション マップおよび NavController のコンテナーであり、ナビゲーションの開始ルート、つまり Screens.HomePage.route を指定します。

(2) RobotListScreen を変更し、インターフェイスをナビゲートできるようにナビゲーション コントローラーを追加します。

/**
 * 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) RobotItemView を変更し、ナビゲーション グラフ内のナビゲーション コントローラー オブジェクトをパラメーターとして渡し、ナビゲーション処理を追加します。

/**
 * 定义列表单项的视图
 * @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)
            }
         }
    }
}

現在の位置で、ロボットのスクロールリストから 1 つの項目のアイコンが選択され、次のページにジャンプします。ただし、各ジャンプは同一データのインターフェースであるため、実態に即したものではありません。実際に行う必要があるのは、スクロール リストから単一の項目アイコンを選択し、クリックしてこの「ロボット」の詳細情報インターフェイスに入ることで、対応するデータを渡す必要があります。

5. ナビゲーションでのデータの受け渡し
1. 基本的な種類のデータの受け渡し
スクロールリストからロボット詳細情報インターフェースへジャンプする際に文字列を渡すことを想定し、ロボットリスト単品表示インターフェースを修正し、クリック時のデータ送信処理を追加します。アクション: (1
) RobotItemScreen関数を修正し、データ送信処理を追加

@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) ナビゲーション マップを変更します。
ナビゲーション マップを変更し、ナビゲーション マップ内のデータの受信側のパラメーター名とパラメーター タイプを指定し、ナビゲーション ルートをパラメーターを含む形式に変更します。

/**
 * 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) 受信側ロボット詳細情報インタフェースでは、受信パラメータの処理とパラメータの種類を指定します。

/**
 * 定义机器人具体信息显示界面
 * @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)
    }
}

このような処理の後、ロボットの詳細インターフェイスに移動すると、次のようになります。
ここに画像の説明を挿入します
図 3: 渡された文字列データの受信

2. カスタムタイプのデータを渡す
実際の場面ではカスタムタイプのオブジェクトデータを渡す必要があることが多いですが、例えば上記のRobotオブジェクトがParcelableインターフェースを実装している場合はどうすればよいでしょうか?このようなオブジェクトを次のインターフェースに直接渡す場合は、
(1) ナビゲーショングラフの変更 ナビゲーション
グラフ内の受信機のパラメータの型をロボット型に変更します。

@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) データ受信側のインターフェースを変更する
データ送信側のインターフェースは上記の内容を維持していますので、変更の必要はありません、受信側のインターフェース処理の変更と、データ送信側のインターフェース処理の変更と、データ送信側のインターフェースが受け取るパラメータの型を変更するだけで済みます。受信側の RobotScreen 関数を文字列からロボット タイプ、GUI に変換し、次のコードに示すように、インターフェイスはそれに応じて処理できます。

@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)
    }
}

java.lang.UnsupportedOperationException : Parcelable はデフォルト値をサポートしていません。Parcelable
型データはデフォルト値をサポートしていないため、スローされます。直接渡した場合、サポートされていない操作の例外がスローされます。したがって、カスタム タイプのオブジェクトを渡すには他の方法が必要です。3. Gson を使用してカスタム データを転送する 1 つの解決策は、送信側でカスタム タイプ オブジェクトのデータを JSON 文字列に変換し、受信側で受信した文字列をカスタム タイプ オブジェクトに変換することです

これは、Gson フレームワークの助けを借りて実現されます。
(1) Gson の依存関係を追加する
Gson フレームワークの依存関係をモジュールの build.gradle に次の形式で追加する必要があります。

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

(2) スクロールリストの単品ビューの定義

/**
 * 定义列表单项的视图
 * @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) スクロールリストインターフェースの定義を変更する

/**
 * 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)
            }
        }
    }
}

コードは変更ありません。
(4) データを受信するインターフェースの定義

@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) ナビゲーションマップの修正

@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对象
        }
    }
}

メイン アクティビティでナビゲーション グラフ インターフェイスを呼び出します。コードは次のとおりです。

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

最終的な実行結果は次のとおりです。
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

この時点で、任意のスクロール リスト項目アイコンをクリックして、指定されたインターフェイスに入ります。
ナビゲーション
に Compose を使用する https://developer.android.google.cn/reference/androidx/navigation/NavHost?hl=zh-cn

おすすめ

転載: blog.csdn.net/userhu2012/article/details/127601476