当Jetpack Compose 遇到 Navigation

很多Android项目使用 Jetpack Navigation进行页面切换。Navigation在设计上高度抽象,只负责导航逻辑不关心页面的具体实现,无论是Activity、Fragment或是已定义View都可以通过Navigation实现导航。
Jetpack Compose作为一个声明式UI框架经常拿来与React 、Flutter等作对比,但是遗憾的是一直缺少同类框架的导航机制,如今通过Jetpack自家的Navigation 也终于可以补齐Compose的短板了。Installation使用navigation-compose,只需要在build.gradle中添加依赖:

implementation "androidx.navigation:navigation-compose:1.0.0-alpha02"

NavControllerNavigation中我们通过findNavController扩展方法获取NavController,然后进行跳转。NavController中管理NavGraph等配置信息,所以是stateful的,在Compose的纯函数中需要通过以下方式获取一个有状态的实例

val navController = rememberNavController()

NavHostNavHost是NavController的持有者,NavHostFragment是Fragment对于NavHost的实现。Compose基于composable函数渲染UI,没有Fragment这样的具体实例做载体,所以Compose的NavHost更加抽象,你可以将其理解为一个容器,内部通过NavController在“页面切换”时,渲染当前UI

val navController = rememberNavController()
NavHost(
        navController = navController,
        startDestination = "first_screen"
) {
    composable("first_screen") {
        // first screen
    }
    composable("second_screen") {
        // second screen
    }
}

如上,NavHost接受两个参数,navController和startDestination,这是Navigation的标准用法,不再赘述。其DSL内部的composable用来声明各个页面A Navigation SampleCompose中一个完整的Navigation定义如下:

@Composable
fun ComposeNavigation() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "first_screen"
    ) {
        composable("first_screen") {
            FirstScreen(navController = navController)
        }
        composable("second_screen") {
            SecondScreen(navController = navController)
        }
        composable("third_screen") {
            ThirdScreen(navController = navController)
        }
    }
}

配置了三个页面,初始页面是first_screencomposable()的参数作为Destination的id,用于后续跳转

@Composable
fun FirstScreen(navController: NavController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "First Screen\n" +
                    "Click me to go to Second Screen",
            color = Color.Green,
            style = TextStyle(textAlign = TextAlign.Center),
            modifier = Modifier.padding(24.dp).clickable(onClick = {
                // this will navigate to second screen
                navController.navigate("second_screen")
            })
        )
    }
}

如上,FirstScreen中,通过navController.navigate("second_screen")跳转到SecondScreen。同样的, 其他的页面定义如下:

@Composable
fun SecondScreen(navController: NavController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Second Screen\n" +
                    "Click me to go to Third Screen",
            color = Color.Yellow,
            style = TextStyle(textAlign = TextAlign.Center),
            modifier = Modifier.clickable(onClick = {
                // this will navigate to third screen
                navController.navigate("third_screen")
            })
        )
    }
}

@Composable
fun ThirdScreen(navController: NavController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Third Screen\n" +
                    "Click me to go to First Screen",
            color = Color.Red,
            style = TextStyle(textAlign = TextAlign.Center),
            modifier = Modifier.clickable(onClick = {
                // this will navigate to first screen
                navController.navigate("first_screen")
            })
        )
    }
}

最后,需要在setContent中调用ComposeNavigation:

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

之后,就可以在Compose项目中进行页面切换了,而且还支持BackStack的回退。最后以前如果想使用Compose实现多页面的APP,只能在Fragment或者Activity内部写Compose代码。现在有了Navigation,可以彻底摆脱Fragment或者Activity了,这得益于Navigation高度抽象的设计,有兴趣的同学可以阅读NavController源码了解其中细节。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容