观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言

  此篇博客讲解Jetpack Compose的导航功能。官方文档:https://developer.android.google.cn/jetpack/compose/navigation?hl=zh-cn

  还在使用xml开发应用的时候,之前的Jetpack系列的开发工具中,Navigation的框架的使用结构通常是这样的:

  Actvity

    ->AFragment

    ->BFragment

  由一个根Activity管理多个Fragment通过导航管理器来控制跳转,返回。(当然有些情况下Activity也可以通过导航互相跳转)

  在新的Jetpack Compose Navigation下,其实已经没有了Fragment的概念了。或者说Jetpack Compose其实已经不需要Fragment的概念了。如果你已经了解过Compose应该有所察觉(Jetpack Compose如果极端一些整个应用可以只需要一个Activity)。所以Jetpack Compose Navigation主要就是从一个Composable方法导航到另一个Composable方法。

依赖

如果你不知道最新版本,可以通过官网文档查看

//jetpack compose导航
implementation("androidx.navigation:navigation-compose:2.5.3")

一个简单的跳转导航

效果图

代码


import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

class DemoActivity : AppCompatActivity() {
    private lateinit var mNavController: NavHostController

    companion object {
        fun jump(context: Context) {
            context.startActivity(Intent(context, DemoActivity::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DemoNavigation()
        }
    }


    @Composable
    fun DemoNavigation() {
        //创建导航控制器
        mNavController = rememberNavController()
        //创建NavHost导航组建者,传入了mNavController 导航控制器 与 首个显示的页面 startDestination = APage
        NavHost(navController = mNavController, startDestination = "APage") {
            //这里添加了页面A,APage为路由地址
            composable( route = "APage") { APage() }
            //这里添加了页面B
            composable( route ="BPage") { BPage() }
        }
    }

    /**
     * 页面A
     */
    @Composable
    fun APage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Button(onClick = { mNavController.navigate("BPage") }) {
                Text(text = "页面A: 点击跳转页面B", fontSize = 40.sp)
            }
        }
    }

    /**
     * 页面B
     */
    @Composable
    fun BPage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Button(onClick = { mNavController.navigateUp() }) {
                Text(text = "页面B: 点击返回页面A", fontSize = 40.sp)
            }
        }
    }
}

带参跳转

效果图

代码

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.sp
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument

class DemoActivity : AppCompatActivity() {
    private lateinit var mNavController: NavHostController

    companion object {
        fun jump(context: Context) {
            context.startActivity(Intent(context, DemoActivity::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DemoNavigation()
        }
    }


    @Composable
    fun DemoNavigation() {
        mNavController = rememberNavController()
        NavHost(navController = mNavController, startDestination = "APage") {
            composable(route = "APage") { APage() }
            composable(
                //在路径后面直接填入{key}传参,请注意在?后面的参数为非必填参数,这里的sex在传入的时候可填可不填
                route = "BPage/{id}/{name}?sex={sex}", arguments = listOf(navArgument("id") {
                    type = NavType.IntType //设置传值的类型
                    defaultValue = -1 //设置默认值
                }, navArgument("name"){
                    type = NavType.StringType
                    defaultValue = "null"
                }, navArgument("sex"){
                    type = NavType.BoolType
                    defaultValue = true
                })
            ) { it -> BPage(it) }
        }
    }

    /**
     * 页面A
     */
    @Composable
    fun APage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            //如果这里不填sex=false,sex则会默认为true
            Button(onClick = { mNavController.navigate("BPage/123/小明") }) {
                Text(text = "页面A: 点击跳转页面B", fontSize = 40.sp)
            }
        }
    }

    /**
     * 页面B
     */
    @Composable
    fun BPage(it: NavBackStackEntry) {
        val id = it.arguments?.getInt("id")
        val name = it.arguments?.getString("name")
        val sex = it.arguments?.getBoolean("sex")
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "传过来的Id = ${id} 名称 = ${name} 性别 = ${sex}", fontSize = 40.sp)
            Button(onClick = { mNavController.navigateUp() }) {
                Text(text = "页面B: 点击返回页面A", fontSize = 40.sp)
            }
        }
    }
}

返回堆栈最上级

下面的代码有3个页面A,B,C。 从A跳转到B,从B跳转到C,从C返回到A。 代码想实现的就是C到A的返回,并且清理掉堆栈中间的B页面。

效果图

代码


import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

class DemoActivity : AppCompatActivity() {
    private lateinit var mNavController: NavHostController
    private val A_PAGE = "APage"
    private val B_PAGE = "BPage"
    private val C_PAGE = "CPage"

    companion object {
        fun jump(context: Context) {
            context.startActivity(Intent(context, DemoActivity::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DemoNavigation()
        }
    }

    @Composable
    fun DemoNavigation() {
        mNavController = rememberNavController()
        NavHost(navController = mNavController, startDestination = A_PAGE) {
            composable(route = A_PAGE) { APage() }
            composable(route = B_PAGE) { BPage() }
            composable(route = C_PAGE) { CPage() }
        }
    }

    /**
     * 页面A
     */
    @Composable
    fun APage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            //如果这里不填sex=false,sex则会默认为true
            Text(text = "页面A", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            Button(onClick = { mNavController.navigate(B_PAGE) }) {
                Text(text = "点击跳转页面B", fontSize = 20.sp)
            }
        }
    }

    /**
     * 页面B
     */
    @Composable
    fun BPage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面B", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            Button(onClick = { mNavController.navigate(C_PAGE) }) {
                Text(text = "点击跳转页面C", fontSize = 20.sp)
            }
        }
    }

    /**
     * 页面C
     */
    @Composable
    fun CPage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面C", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            //这里的popBackStack,将会跳转会页面A,此外这里第二个参数是表示如果设置为true页面A也将一起退出
            Button(onClick = { mNavController.popBackStack(A_PAGE, false) }) {
                Text(text = "点击返回到页面A", fontSize = 20.sp)
            }
        }
    }
}

路由分组(多层嵌套导航)

在上面的例子中,我们的路由都是一层的。但是,有时候我们希望进行业务分组也将路由分组,但是又希望只有一个Activity。这个时候可以使用navigation的多层嵌套。

下面的代码中有A模块页面2个和B模块页面2个,现在他们都在一个navigation下。但是我们为了区分业务希望将他们分组导航。(实际开发中每一组可以创建不同的类文件保存,并且放在不同的文件夹中)

效果图

代码

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navigation

class DemoActivity : AppCompatActivity() {
    private lateinit var mNavController: NavHostController
    //A组
    private val A_GROUP = "AGroup"
    private val A1_PAGE = "A1Page"
    private val A2_PAGE = "A2Page"
    //B组
    private val B_GROUP = "BGroup"
    private val B1_PAGE = "B1Page"
    private val B2_PAGE = "B2Page"

    companion object {
        fun jump(context: Context) {
            context.startActivity(Intent(context, DemoActivity::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DemoNavigation()
        }
    }

    @Composable
    fun DemoNavigation() {
        mNavController = rememberNavController()
        NavHost(navController = mNavController, startDestination = A_GROUP) {
            navigation(startDestination = A1_PAGE, route = A_GROUP) {
                composable(route = A1_PAGE) { A1Page() }
                composable(route = A2_PAGE) { A2Page() }
            }
            navigation(startDestination = B1_PAGE, route = B_GROUP) {
                composable(route = B1_PAGE) { B1Page() }
                composable(route = B2_PAGE) { B2Page() }
            }
        }
    }

    /**
     * 页面A1
     */
    @Composable
    fun A1Page() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            //如果这里不填sex=false,sex则会默认为true
            Text(text = "页面A_1", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            Button(onClick = { mNavController.navigate(A2_PAGE) }) {
                Text(text = "点击跳转A_2页面", fontSize = 20.sp)
            }
        }
    }

    /**
     * 页面A2
     */
    @Composable
    fun A2Page() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面A_2", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            Button(onClick = { mNavController.navigate(B_GROUP) }) {
                Text(text = "点击跳转B组的B_1页面", fontSize = 20.sp)
            }
        }
    }

    /**
     * 页面B1
     */
    @Composable
    fun B1Page() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            //如果这里不填sex=false,sex则会默认为true
            Text(text = "页面B_1", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            Button(onClick = { mNavController.navigate(B2_PAGE) }) {
                Text(text = "点击跳转B_2页面", fontSize = 20.sp)
            }
        }
    }

    /**
     * 页面B2
     */
    @Composable
    fun B2Page() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面B_2", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            Button(onClick = { mNavController.navigate(A_GROUP) }) {
                Text(text = "点击跳转A组的A_1页面", fontSize = 20.sp)
            }
        }
    }
}

使用高级函数进行导航

在上面的例子中,我将NavController设置全局变量,这样可以在各个Composable方法里直接调用导航,这实际没什么问题,但是也不太符合规范。因为Composable的设计初衷就有强烈的作用域限制想法(各种remember就是最好的证明),他们并不希望你在Composable里调用外部的数据与状态。这里我们使用kotlin的高阶函数实现导航,这种方式也是官网文档推荐的,我这里稍稍改变让多目的地路由也可以实现。

效果图

代码


class DemoActivity : AppCompatActivity() {
    private val A_PAGE = "APage"
    private val B_PAGE = "BPage"
    private val C_PAGE = "CPage"

    companion object {
        fun jump(context: Context) {
            context.startActivity(Intent(context, DemoActivity::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DemoNavigation()
        }
    }

    @Composable
    fun DemoNavigation() {
        val navController = rememberNavController()
        NavHost(navController = navController, startDestination = A_PAGE) {
            composable(route = A_PAGE) { APage() { navController.navigate(it) } }
            composable(route = B_PAGE) { BPage() { navController.navigateUp() } }
            composable(route = C_PAGE) { CPage() { navController.navigateUp() } }
        }
    }

    /**
     * 页面A
     */
    @Composable
    fun APage(navigationJump: (String) -> Unit) {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面A", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            //这里用高阶函数回调到外部
            Button(onClick = { navigationJump.invoke(B_PAGE) }) {
                Text(text = "点击跳转页面B", fontSize = 20.sp)
            }
            Button(onClick = { navigationJump.invoke(C_PAGE) }) {
                Text(text = "点击跳转页面C", fontSize = 20.sp)
            }
        }
    }

    /**
     * 页面B
     */
    @Composable
    fun BPage(navigationBack: () -> Unit) {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面B", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            Button(onClick = { navigationBack.invoke() }) {
                Text(text = "点击返回", fontSize = 20.sp)
            }
        }
    }

    /**
     * 页面C
     */
    @Composable
    fun CPage(navigationBack: () -> Unit) {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面C", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            Button(onClick = { navigationBack.invoke() }) {
                Text(text = "点击返回", fontSize = 20.sp)
            }
        }
    }
}

深层链接

这个功能很重要,但是也很容易被人忽视。其实就是提供从外部跳转到App某个指定页面的功能。比如从通知栏点击后跳转到信息列表页面这种业务。下面的代码就是通知栏点击跳转到指定页面为例子:

效果图

代码

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.app.NotificationCompat
import androidx.core.net.toUri
import androidx.navigation.NavBackStackEntry
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navDeepLink
import com.lwlx.eye.R

class DemoActivity : AppCompatActivity() {
    private val A_PAGE = "APage"
    private val B_PAGE = "BPage"

    companion object {
        fun jump(context: Context) {
            context.startActivity(Intent(context, DemoActivity::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DemoNavigation()
        }
    }

    @Composable
    fun DemoNavigation() {
        val uri = "https://www.example.com"
        val navController = rememberNavController()
        NavHost(navController = navController, startDestination = A_PAGE) {
            composable(route = A_PAGE) { APage() }
            //这里是关键代码!这里将设置提供给外部的uri地址
            composable(route = B_PAGE+"?id={id}", deepLinks = listOf(navDeepLink { uriPattern = "$uri/{id}" })) { it-> BPage(it) }
        }
    }

    /**
     * 页面A
     */
    @Composable
    fun APage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面A", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
            //点击创建通知
            Button(onClick = { createNotification() }) {
                Text(text = "创建通知", fontSize = 20.sp)
            }
        }
    }

    /**
     * 页面B,待深层链接的页面
     */
    @Composable
    fun BPage(navBackStackEntry: NavBackStackEntry) {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "页面B ID = ${navBackStackEntry.arguments?.getString("id")}", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
        }
    }

    /**
     * 这是关键代码!创建深层链接的PendingIntent
     */
    fun deepLinkPendingIntent():PendingIntent{
        //请注意!这个exampleId就是传入的值
        val id = "exampleId"
        val deepLinkIntent = Intent(Intent.ACTION_VIEW, "https://www.example.com/$id".toUri(), this@DemoActivity, DemoActivity::class.java)
        val deepLinkPendingIntent: PendingIntent = TaskStackBuilder.create(this@DemoActivity).run {
            addNextIntentWithParentStack(deepLinkIntent)
            getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
        }
        return deepLinkPendingIntent
    }

    /**
     * 创建通知
     */
    fun createNotification() {
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel =
                NotificationChannel("nomal", "Nomal", NotificationManager.IMPORTANCE_DEFAULT)
            manager.createNotificationChannel(channel)
        }
        val notification = NotificationCompat.Builder(this, "nomal")
            .setContentTitle("点击跳转页面B")
            .setContentText("this is content")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentIntent(deepLinkPendingIntent()) //这里将传入 deepLinkPendingIntent
            .build()
        manager.notify(1, notification)
    }
}

导航目的地变化监听

mNavController = rememberNavController()
mNavController.addOnDestinationChangedListener(object :NavController.OnDestinationChangedListener{
    override fun onDestinationChanged(controller: NavController, destination: NavDestination, arguments: Bundle?) {
        Log.e("zh", "目的地 = ${destination} 携带参数 = ${arguments}" )
    }
})

与底部导航栏集成

效果图

所需依赖

implementation("androidx.compose.material:material:1.4.3")

代码

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.lwlx.eye.R

class DemoActivity : AppCompatActivity() {
    private val A_PAGE = "APage"
    private val B_PAGE = "BPage"
    private val C_PAGE = "CPage"

    companion object {
        fun jump(context: Context) {
            context.startActivity(Intent(context, DemoActivity::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DemoNavigation()
        }
    }

    @Composable
    fun DemoNavigation() {
        val iconList = remember { listOf(R.mipmap.ic_fruit_apple, R.mipmap.ic_fruit_banana, R.mipmap.ic_fruit_coconut) }
        val nameList = remember { listOf("苹果", "香蕉", "椰子") }
        val routeList = remember { listOf(A_PAGE, B_PAGE, C_PAGE) }
        val navController = rememberNavController()
        //这里也可以是topBar,让导航栏显示到上方
        Scaffold(bottomBar = {
            //这里的backgroundColor 可以导航拦的背景色, 此外高度也是在这里调节
            BottomNavigation(backgroundColor = Color.Gray, modifier = Modifier.height(80.dp)) {
                val navBackStackEntry by navController.currentBackStackEntryAsState()
                val currentDestination = navBackStackEntry?.destination
                for (index in routeList.indices) {
                    BottomNavigationItem(
                        icon = { Image(painter = painterResource(id = iconList[index]), contentDescription = null,) }, //图片
                        label = { Text(text = nameList[index]) },   //文字
                        selected = currentDestination?.hierarchy?.any { it -> it.route == routeList[index] } == true, //选中状态
                        onClick = {  //点击处理
                            navController.navigate(routeList[index]) {
                                //以下防止在切换Item的时候,在已经有的Item页里创建新的Item页。
                                //弹出到图的开始目的地,以避免在用户选择项目时在后堆栈上构建大量目的地堆栈
                                popUpTo(navController.graph.findStartDestination().id) {
                                    saveState = true
                                }
                                //重新选择同一项时,避免同一目标的多个副本
                                launchSingleTop = true
                                //重新选择先前选择的项时恢复状态
                                restoreState = true
                            }
                        })
                }
            }
        }) { innerPadding -> //innerPadding为内部边距
            NavHost(
                navController = navController,
                startDestination = A_PAGE,
                modifier = Modifier.padding(innerPadding)
            ) {
                composable(A_PAGE) { APage() }
                composable(B_PAGE) { BPage() }
                composable(C_PAGE) { CPage() }
            }
        }
    }

    @Composable
    fun APage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "苹果", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
        }
    }
    
    @Composable
    fun BPage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "香蕉", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
        }
    }
    
    @Composable
    fun CPage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "椰子", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
        }
    }
}

发散思维实现一个侧边栏导航

根据上面的方式,其实可以发现关键是NavHost写在哪里,即可在指定位置实现导航功能。jetpack compose未提供可以直接使用的固定侧边栏(侧滑侧边栏倒是有,Scaffold可以实现侧滑侧边栏),这边我们自行实现一个固定侧边栏。

效果图

代码

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

class DemoActivity : AppCompatActivity() {
    private val A_PAGE = "APage"
    private val B_PAGE = "BPage"
    private val C_PAGE = "CPage"

    companion object {
        fun jump(context: Context) {
            context.startActivity(Intent(context, DemoActivity::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DemoNavigation()
        }
    }

    @Composable
    fun DemoNavigation() {
        val nameList = remember { listOf("苹果", "香蕉", "椰子") }
        val routeList = remember { listOf(A_PAGE, B_PAGE, C_PAGE) }
        val navController = rememberNavController()
        Row() {
            Column(modifier = Modifier
                .width(100.dp)
                .fillMaxHeight()
                .background(Color.Gray)) {
                for (index in routeList.indices){
                    Text(text = nameList[index], fontSize = 18.sp, modifier = Modifier
                        .padding(30.dp)
                        .clickable {
                        navController.navigate(routeList[index]) {
                            //以下防止在切换Item的时候,在已经有的Item页里创建新的Item页。
                            //弹出到图的开始目的地,以避免在用户选择项目时在后堆栈上构建大量目的地堆栈
                            popUpTo(navController.graph.findStartDestination().id) {
                                saveState = true
                            }
                            //重新选择同一项时,避免同一目标的多个副本
                            launchSingleTop = true
                            //重新选择先前选择的项时恢复状态
                            restoreState = true
                        }
                    })
                }
            }
            Column(modifier = Modifier.fillMaxWidth()) {
                NavHost(
                    navController = navController,
                    startDestination = A_PAGE,
                ) {
                    composable(A_PAGE) { APage() }
                    composable(B_PAGE) { BPage() }
                    composable(C_PAGE) { CPage() }
                }
            }
        }
    }

    @Composable
    fun APage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "苹果", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
        }
    }

    @Composable
    fun BPage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "香蕉", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
        }
    }

    @Composable
    fun CPage() {
        Column(
            Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(text = "椰子", fontSize = 40.sp, modifier = Modifier.padding(bottom = 50.dp))
        }
    }
}

 

end

posted on 2023-07-27 16:17  观心静  阅读(1555)  评论(0编辑  收藏  举报