一文搞懂在 Compose 中使用 Navigation 導航 | 開發者說·DTalk
本文原作者: 黃林晴, 原文 釋出於: 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"
- 共碼未來 | 持續賦能開發者和初創生態
- 要近萬元買iPhone 14 Pro才能玩靈動島?Android 開發者:別急,我給你自制了一個 App
- 揭祕 Jetpack Compose 快照系統 | 開發者說·DTalk
- 輕鬆學習,考取證書 | 商品詳情繫列內容第十講
- 是時候讓所有人能一起聊個痛快了!
- 在 Jetpack Compose 中安全地使用資料流
- 共碼未來 | 助力打造現代、高效、流暢的開發體驗
- 共碼未來丨2022 Google 谷歌開發者大會主旨演講亮點回顧
- 京東金融客戶端使用者觸達方式的探索與實踐
- 微信安卓測試版 8.0.28 開發者更新內容公佈:統一檔案相關介面錯誤碼,升級地圖 SDK 等
- 讓您的遊戲在 PC 閃耀 | Google Play Games Beta 版現已開放更多市場
- 打造卓越的 Android 遊戲體驗
- Android 老手翻車了,竟拿不到 Application Context?| 開發者說·DTalk
- 2022 OPPO 開發者大會拉開帷幕,這些重點你不容錯過!
- Firebase Crashlytics 近期更新 | 在 Android Studio 中更好地除錯應用
- 谷歌為 Pixel 6 系列推出 Android 13 降級 12 方案:繞過防回滾機制,只面向開發者
- 提升效率,減少錯誤 | Twitter 採用 Jetpack Compose 進行功能開發
- Android Jetpack: 利用 Palette 進行圖片取色 | 開發者說·DTalk
- Android 官方現代 App 架構指南 | 開發者說·DTalk
- Smule 藉助 Oboe 音訊庫提升使用者體驗,助力音樂創作 | Android 開發者故事