一文搞懂在 Compose 中使用 Navigation 導航 | 開發者說·DTalk

語言: CN / TW / HK

本文原作者: 黃林晴, 原文 釋出於: Android技術圈

使用 Navigation 在 Compose 中導航

如果您之前不喜歡 Android 提倡的 "單 Activity" 應用,那麼在 Compose 中相信您會慢慢習慣的~

在此示例中,有兩個頁面 PageOne 和 PageTwo,首先來看 PageOne 的程式碼如下所示: 

@Composable
fun PageOne() {
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(
Color.White
)
) {
Text(text = "這是頁面1")
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
//點選跳轉到頁面2
}) {
Text(
text = "跳轉頁面2",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
}

PageOne 頁面中有個按鈕,點選按鈕跳轉到頁面 2,為了便於區分,新增一個 Text 用來顯示當前頁面內容。

接著來看 PageTwo 的程式碼,如下所示:

@Composablefun PageTwo() {
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(
Color.White
)
) {
Text(text = "這是頁面2")
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = { //點選返回頁面1
}) {
Text(
text = "返回頁面1",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
}

MainActivity 程式碼如下所示: 

setContent {
NavigationTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
PageOne()
}
}
}

執行程式,顯示出頁面 1,如圖所示。

接下來我們來看,如何使用 navigation 來進行頁面導航呢?

定義 NavHost

首先我們定義一個 NavHost 物件:

@Composable
fun NavHostDemo() {


NavHost(navController =, startDestination =) {


}


}

NavHost 物件需要兩個必傳引數,一個是 NavController,一個是起始路由地址,NavController 物件是 Navigation 元件的中心 API,我們可以通過 rememberNavController 建立,程式碼如下所示: 

val navController = rememberNavController()

為了便於管理路由地址,我們新建 RouteConfig 配置檔案,程式碼如下所示: 

object RouteConfig {


/**
* 頁面1路由
*/
const val ROUTE_PAGEONE = "pageOne"


/**
* 頁面1路由
*/
const val ROUTE_PAGETWO = "pageTwo"
}

在這裡,將頁面 1 路由設定為起始導航,並使用 composable 方法新增導航對應關係,修改後的 NavHostDemo 程式碼如下所示: 

@Composable
fun NavHostDemo() {


val navController = rememberNavController()
NavHost(navController = navController, startDestination = RouteConfig.ROUTE_PAGEONE) {


composable(RouteConfig.ROUTE_PAGEONE) {
OnePage()
}


composable(RouteConfig.ROUTE_PAGETWO) {
PageTwo()
}
}


}

如此一來,我們就建立了導航對應關係,RouteConfig.ROUTE_PAGEONE 對應 OnePage,RouteConfig.ROUTE_PAGETWO 對應 PageTwo,由於我們需要在各自的頁面中進行頁面跳轉,所以將 navController 傳遞到對應的頁面中去,程式碼如下所示:

composable(RouteConfig.ROUTE_PAGEONE) {
PageOne(navController)
}


composable(RouteConfig.ROUTE_PAGETWO) {
PageTwo(navController)
}

這樣我們就可以在頁面 1 中進行頁面跳轉了。

普通頁面跳轉

修改頁面 1 的程式碼如下所示: 

@Composable
fun PageOne(navController: NavController) {
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(
Color.White
)
) {
Text(text = "這是頁面1")
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
//點選跳轉到頁面2
navController.navigate(RouteConfig.ROUTE_PAGETWO)
}) {
Text(
text = "跳轉頁面2",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
}

我們在按鈕的監聽事件中呼叫 navController.navigate 方法,傳入頁面 2 的路由地址,這樣就可以跳轉到頁面 2 了。在頁面 2 中呼叫 popBackStack 方法將當前頁面出棧便又回到了頁面 1,這裡就不貼頁面 2 的程式碼了。當然我們要記得最後一步: 在入口處呼叫 NavHostDemo()。

setContent {
NavigationTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
NavHostDemo()
}
}
}

執行程式,點選跳轉頁面 2 按鈕,在頁面 2 點選返回頁面 1 按鈕,效果如下圖所示。

這樣一來,我們就實現了普通頁面跳轉,那麼如果我們在頁面跳轉的時候需要傳遞引數,該如何去做呢?

這裡以頁面 1 跳轉頁面 2 為例,假設頁面 1 跳轉到頁面 2 時需要傳遞一個 name 引數和 age 引數,該如何去做呢?

傳遞引數跳轉

必傳引數

首先,我們定義一個引數配置檔案,程式碼如下所示: 

object ParamsConfig {


/**
* 引數-name
*/
const val PARAMS_NAME = "name"


/**
* 引數-age
*/
const val PARAMS_AGE = "age"


}

修改 NavHost 的配置,程式碼如下所示: 

NavHost(navController = navController, startDestination = RouteConfig.ROUTE_PAGEONE) {


composable(RouteConfig.ROUTE_PAGEONE) {
PageOne(navController)
}


composable(
"${RouteConfig.ROUTE_PAGETWO}/{${ParamsConfig.PARAMS_NAME}}/{${ParamsConfig.PARAMS_AGE}}",
arguments = listOf(
navArgument("$ParamsConfig.PARAMS_NAME") {},
navArgument("$ParamsConfig.PARAMS_AGE") { type = NavType.IntType }
)
) {
PageTwo(navController)
}
}

這裡,直接將傳遞的引數使用 "/" 拼寫在路由地址後面新增佔位符即可,預設情況下,所有的引數都會被解析成字串,所以我們可以使用 arguments 來為引數指定 type 型別。當然,因為這裡我們只需要將年齡欄位指定為整形,所以 navArgument("$ParamsConfig.PARAMS_NAME") {} 也可以不寫,這裡知道就行啦~

那麼 PageTwo 頁面該如何接收引數呢?

可以通過 composable 函式中提供的 NavBackStackEntry 來獲取,並將獲取的結果傳遞給 PageTwo 頁面即可,修改後的程式碼如下所示:

 NavHost(navController = navController, startDestination = RouteConfig.ROUTE_PAGEONE) {


composable(RouteConfig.ROUTE_PAGEONE) {
PageOne(navController)
}


composable(
"${RouteConfig.ROUTE_PAGETWO}/{${ParamsConfig.PARAMS_NAME}}/{${ParamsConfig.PARAMS_AGE}}",
arguments = listOf(
navArgument("$ParamsConfig.PARAMS_AGE") { type = NavType.IntType }
)
) {
val argument = requireNotNull(it.arguments)
val name = argument.getString(ParamsConfig.PARAMS_NAME)
val age = argument.getInt(ParamsConfig.PARAMS_AGE)
PageTwo(name,age,navController)
}
}

在 PageTwo 頁面接受傳遞的引數,並新增一個 Text 用於顯示,修改後的 PageTwo 的主要程式碼如下所示: 

{
Text(text = "這是頁面2")
Spacer(modifier = Modifier.height(20.dp))
Text(text = "我是$name,我今年$age 歲了")
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
//點選返回頁面1
navController.popBackStack()
}) {
Text(
text = "返回頁面1",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}

最後,在頁面 1 的監聽事件中使用佔位符傳參即可,程式碼如下所示: 

{
Text(text = "這是頁面1")
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
//點選跳轉到頁面2
navController.navigate("${RouteConfig.ROUTE_PAGETWO}/黃林晴/26")
}) {
Text(
text = "跳轉頁面2",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}

執行程式,點選跳轉頁面 1 按鈕,效果如下圖所示: 

如此一來就實現了從頁面 1 到頁面 2 的傳參,如果我們在頁面 1 的點選事件中少傳一個引數,會怎麼樣呢?

navController.navigate("${RouteConfig.ROUTE_PAGETWO}/黃林晴")

不用想了,會崩潰!!所以佔位符的方式相當於必傳引數,如果不傳的話則會丟擲異常。

如果我們想將引數設定為可選引數應該怎麼樣做呢?

可選引數

可選引數類似於 get 請求的新增方式 ?name = name,現在我們將年齡修改為一個可選引數,來看看如何修改。

首先,我們修改 NavHost 程式碼如下所示:

composable(RouteConfig.ROUTE_PAGEONE) {
PageOne(navController)
}


composable(
"${RouteConfig.ROUTE_PAGETWO}/{${ParamsConfig.PARAMS_NAME}}" +
"?${ParamsConfig.PARAMS_AGE}={${ParamsConfig.PARAMS_AGE}}",
arguments = listOf(
navArgument(ParamsConfig.PARAMS_NAME) {},
navArgument(ParamsConfig.PARAMS_AGE) {
defaultValue = 30
type = NavType.IntType
}
)
) {
val argument = requireNotNull(it.arguments)
val name = argument.getString(ParamsConfig.PARAMS_NAME)
val age = argument.getInt(ParamsConfig.PARAMS_AGE)
PageTwo(name, age, navController)
}

當前的路由地址就是 "pageTwo/{name}?age={age}",由於可選引數必須要設定一個預設值,這裡設定年齡的預設值為 30,現在在頁面 1 的點選事件中不再傳遞年齡引數

navController.navigate("${RouteConfig.ROUTE_PAGETWO}/黃林晴")

再次執行程式,點選跳轉頁面 2 按鈕,執行結果如下圖所示。

由圖可知,我們已經成功的將年齡設定為可選引數。

總結

除此之外,Navigation 在 Compose 中還支援深層連結等~ 

長按右側二維碼

檢視更多開發者精彩分享

"開發者說·DTalk" 面向 中國開發者們徵集 Google 移動應用 (apps & games) 相關的產品/技術內容。歡迎大家前來分享您對移動應用的行業洞察或見解、移動開發過程中的心得或新發現、以及應用出海的實戰經驗總結和相關產品的使用反饋等。我們由衷地希望可以給這些出眾的中國開發者們提供更好展現自己、充分發揮自己特長的平臺。我們將通過大家的技術內容著重選出優秀案例進行 谷歌開發技術專家 (GDE) 的推薦。

  點選屏末  |  閱讀原文  | 即刻報名參與  " 開發者說 · DTalk"