這才是我的完全體:當Jetpack Compose遇到Navigation

語言: CN / TW / HK

在這裡插入圖片描述 很多Android專案使用Jetpack Navigation進行頁面切換。Navigation在設計上高度抽象,只負責導航邏輯不關心頁面的具體實現,無論是Activity、Fragment甚至是一個已定義View都可以基於Navigation實現導航。當然,Composable也是可以的。

Jetpack Compose作為一個宣告式UI框架經常拿來與React 、Flutter等作對比,但是遺憾的是Compose一直缺少其他框架的導航機制,現在使用Jetpack自家的Navigation,可以將Composable抽象為Destination從而進行導航,補齊了Compose的短板。

Installation

使用navigation-compose,只需要在build.gradle中新增依賴:

implementation "androidx.navigation:navigation-compose:1.0.0-alpha02"
複製程式碼

NavController

Navigation中我們通過findNavController擴充套件方法獲取NavController,然後進行跳轉。 NavController中管理NavGraph等配置資訊,所以是stateful的,在Compose的純函式中需要通過以下方式獲取一個有狀態的例項

val navController = rememberNavController()
複製程式碼

NavHost

NavHost是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 Sample

Compose中一個完整的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_screen composable()的引數作為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的回退。

Fin

以前如果想使用Compose實現多頁面的APP,只能在Fragment或者Activity內部寫Compose程式碼。現在有了Navigation,可以徹底擺脫Fragment或者Activity了,這得益於Navigation高度抽象的設計,有興趣的同學可以閱讀NavController原始碼瞭解其中細節。