์๋๋ก์ด๋์ Compose๋ก ๊ตฌํํ๋ค๋ฉด
ํ๋ฉด ์ด๋ํ ๋๋ Compose Navigation์ ์จ์ค์ผ ํ์ง ์๊ฒ ์ต๋๊น?
์๋์ ๊ฐ์ ์ํฉ๋ค์์ ์ ์ฉํ ์ ์๋ ์์๋ฅผ ์ ์ด๋ณด๊ณ ์ ํฉ๋๋ค.
1. ํ๋ฉด ์ด๋ํ๊ธฐ
2. ์ธ์ ์ ๋ฌํ๊ธฐ
2-1. String ํ์ ์ด๋
2-2. ์ปค์คํ ๋ฐ์ดํฐ ๋ชจ๋ธ (Parcelable ํ์ ) ์ด๋
์ฐ์ build.gradle(app)์ ์๋ dependency ์ถ๊ฐ๊ฐ ํ์ํฉ๋๋ค.
dependencies {
def nav_version = "2.6.0"
implementation "androidx.navigation:navigation-compose:$nav_version"
}
์ต์ ๋ฒ์ ์ ๊ณต์๋ฌธ์์์ ํ์ธํ ์ ์์ต๋๋ค.
https://developer.android.com/jetpack/compose/navigation
Compose๋ฅผ ํตํด ์ด๋ | Jetpack Compose | Android Developers
Compose๋ฅผ ํตํด ์ด๋ ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ์. Navigation ๊ตฌ์ฑ์์๋ Jetpack Compose ์ ํ๋ฆฌ์ผ์ด์ ์ ์ง์ํฉ๋๋ค. Navigation ๊ตฌ์ฑ์์์ ์ธํ๋ผ
developer.android.com
1. ํ๋ฉด ์ด๋ํ๊ธฐ
Intro ํ๋ฉด์์ List ํ๋ฉด์ผ๋ก ์ด๋ํ๋ค๋ ์์๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
ํ๋ฉด๋ค์ ๊ฒฝ๋ก๋ฅผ ํ๋์ ๋ณผ ์ ์๋๋ก Screens ํด๋์ค๋ฅผ ์์ฑํด์ ์ ๋ฆฌํด์ค๋๋ค.
sealed class Screens(
val route: String
) {
object IntroScreen: Screens("intro_screen")
object ListScreen: Screens("list_screen")
}
๊ดํธ ์์ ์ฐ์ธ "intro_screen"๊ฐ์ ๊ฒฝ๋ก๋ ์์๋ก ์ง์ ํ๊ฑฐ๋, string ํ์ผ์ ์ง์ ํด์ ๊ฐ์ ธ์์ค๋ ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ AppNavHost ํจ์๋ฅผ ์์ฑํด์ค๋๋ค.
ํ๋ฉด ์ ํ ์ฒ๋ฆฌ ๋๊ตฌ์ธ navController๋ฅผ ๊ด๋ฆฌํ๊ณ ์ฌ๋ฌ ํ๋ฉด์ผ๋ก์ ์ ํ์ ์ฒ๋ฆฌํ๋ ํจ์๋ก,
๋ฐ๋ก ํ์ผ์ ๋ง๋ค์ด๋ ๋๊ณ , Compose Screen๋ค์ด ๋ ์๋ Activity(ํน์ Fragment)์์ ์์ฑํด์ฃผ์ ๋ ๋ฉ๋๋ค.
@Composable
fun AppNavHost(navController: NavHostController) {
NavHost(navController = navController, startDestination = Screens.IntroScreen.route) {
composable(Screens.IntroScreen.route) {
IntroScreen(navController)
}
composable(Screens.ListScreen.route) {
ListScreen(navController)
}
}
}
๊ทธ๋ฆฌ๊ณ Activity์์ ํด๋น ํจ์๋ฅผ ํธ์ถํด์ฃผ๋ฉด Navigation์ ์ฐ๊ธฐ ์ํ ์ค๋น๊ณผ์ ์ด ๋๋ฉ๋๋ค.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyApp() {
val navController = rememberNavController()
Scaffold(
modifier = Modifier.fillMaxSize()
) { _innerPadding ->
Box(
modifier = Modifier.padding(_innerPadding)
) {
AppNavHost(navController = navController)
}
}
}
์ฌ๊ธฐ์ ์ด์ฉํ๋ navController๋ state hoisting๋ฅผ ์ํด ํ ๊ตฐ๋ฐ์๋ง ์์ฑํด ๊ด๋ฆฌํด์ฃผ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ฌ๊ธฐ์ ์ด์ Intro ํ๋ฉด์์ ๋ฒํผ์ ๋๋ฌ List ํ๋ฉด์ผ๋ก ๊ฐ๊ณ ์ ํ๋ค๋ฉด,
BottomButton(
onClick = {
navController.navigate(
Screens.ListScreen.route
)
},
buttonTitle = "๋ค์"
)
์ ์ฝ๋์ ๊ฐ์ด,
navController.navigate(Screens.ListScreen.route) ๋ก ์ด๋์ด ๊ฐ๋ฅํฉ๋๋ค.
navController๋ @Composable IntroScreen์ ๋ณด์ฌ์ค ๋ ํ๋ผ๋ฏธํฐ๋ก navController๋ฅผ ๋ฃ์ด์ฃผ๋ ๊ฑธ ์ด์ฉํ๋ ๊ฒ๋๋ค.
2. ์ธ์ ์ ๋ฌํ๊ธฐ
List Screen์์ Detail Screen์ผ๋ก ์ด๋ํ๋ ์์๋ฅผ ๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
๋จผ์ , String ํ์ ๊ณผ ๊ฐ์ ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์ ํ๋๋ง ์ ๋ฌํด๋ณด๊ฒ ์ต๋๋ค.
์ ๊ฐ ๋ณธ ๋ฐฉ๋ฒ์ด 2๊ฐ ์๋๋ฐ์
์ฒซ๋ฒ์งธ๋ Screens ํ์ผ์์ ๊ฒฝ๋ก์ ํฌํจ์ํค๋ ๋ฐฉ๋ฒ์ ๋๋ค.
const val TITLE_ARGS = "TITLE"
sealed class Screens(val route: String) {
object DetailScreen : Screens("detail_screen/{$TITLE_ARGS}") {
fun withTitle(title: String): String {
return this.route.replace(TITLE_ARGS, title)
}
}
}
๊ฒฝ๋ก์ ์ ๋ฌํ ์ธ์๋ฅผ ์ง์ ํด์ฃผ๊ณ
ํจ์๋ฅผ ํ๋ ์์ฑํด ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์ธ์๋ฅผ ๊ฒฝ๋ก์ ํฌํจ์์ผ์ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
Activity์ AppNavHostํจ์์์๋
composable(
route = Screens.DetailScreen.route,
arguments = listOf(
navArgument(TITLE_ARGS) {
type = NavType.StringType
}
)
) { _backStackEntry ->
val title = _backStackEntry.arguments!!.getString(TITLE_ARGS)!!
DetailScreen(navController, title)
}
์์ ๊ฐ์ด backStackEntry argument์์ ๋ฃ์ด์ฃผ๊ณ ๋ฐ์์ Compose Screen์ ๋ณด์ฌ์ค ๋
ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ฃผ๋ฉด ๋ฉ๋๋ค.
๋ง์ฝ์ ์ด๋ฏธ์ง url์ ์ ๋ฌํ๊ณ ์ถ์๋ฐ ์์ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฐ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋๋ฐ์
java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation
url ๋งํฌ๊ฐ ์ถฉ๋๋์ ์๊ธฐ๋ ๋ฌธ์ ์ ๋๋ค.
๊ทธ๋ด ๋๋ ๋ณด๋ด์ค๋ url์ encodeํ์ฌ ๋ณด๋ด์ฃผ๋ฉด ๋ฉ๋๋ค.
val encodedUrl = URLEncoder.encode(_imageUrl , StandardCharsets.UTF_8.toString())
๋ฐ์ ๊ณณ์์๋ ๋ณ๋์ ์ฒ๋ฆฌ ๊ณผ์ ์ด ํ์ํ์ง ์์ต๋๋ค.
๋๋ฒ์งธ ๋ฐฉ๋ฒ์ navController์ BackStackEntry ์ saveStateHandle์ ๋ฃ๊ณ ๊บผ๋ด์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ์ ๋๋ค.
์ด ๋ฐฉ๋ฒ์ Screens ํด๋์ค์์๋ ๋ณ๋์ ์ง์ ์ด ํ์ํ์ง ์๊ณ
AppNavHost ํจ์์์ ๊บผ๋ด ์ ๋ฌํ๋ ๊ณผ์ ์ด ํ์ํฉ๋๋ค.
composable(Screens.DetailScreen.route) {
val data = remember {
navController.previousBackStackEntry?.savedStateHandle?.get<String>("title")
}
if (data != null) {
DetailScreen(navController, data)
}
}
Detail์ ๋ณด์ฌ์ค ๋๋ navController.previousBackStackEntry์ savedStateHandle์ getํด์ ๋ถ๋ฌ์ค๊ณ
List์์ Detail๋ก ์ด๋์ํฌ ๋๋ navController.currentBackStackEntry์ savedStateHandle์ set ํด์ค๋๋ค.
navController.currentBackStackEntry?.savedStateHandle?.set(key = "title", value = titleText)
navController.navigate(Screens.DetailScreen.route)
์ด ๋ฐฉ๋ฒ์ ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์ ์ด ์๋ ์ง์ ์์ฑํ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์ด๋์์ผ์ผํ ๋ ์ ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ธ๋ฐ์
@Parcelize
data class Menu(
val name: String,
val price: Int,
) : Parcelable
์์ ๊ฐ์ ๋ฐ์ดํฐ ํ์ ์ Parcelable ํ์ ์ผ๋ก ์์ฑํด์ฃผ๊ณ ,
set, getํด์ฃผ๋ ํ์ ์ Menu๋ก๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ๋ฉ๋๋ค.
val data = remember {
navController.previousBackStackEntry?.savedStateHandle?.get<Menu>("menu")
}
if (data != null) {
DetailScreen(navController, data)
}
์ฐธ๊ณ ํ๋ ๊ธ์ ๋๋ค.. ์ ๋ง ๋ง์ ๋์์ด ๋์.. ๐ฅฒ
Compose Navigation Argument ๋ก url ์ ๋ฌํ๊ธฐ
์ด์ ์ ๋ง๋ค์๋ ๊ณผ์ ์ฐ์ต์ฉ ๋ ํฌ๋ฅผ ์ปดํฌ์ฆ๋ก ๋ณํํ๋ ๊ณผ์ ์ค, ๊ฐ์ฅ ํด๊ฒฐํ๋๋ฐ ์ค๋ ๊ฑธ๋ ธ๋ ์ด์์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๊ณต์ ํ๊ณ ์ ํ๋ค.๊ตฌํ ์๊ตฌ ์ฌํญ์ Paging3 API ๋ฅผ ํตํด ๊ฒ์์ด์ ๋ง๋ ๊ฒฐ๊ณผ๋ฅผ
velog.io
'Android > Jetpack Compose' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android Compose] TextField์ placeholder ๋ฃ๊ธฐ (0) | 2022.07.15 |
---|---|
[Android Compose] TextField Focus ์ฃผ๊ธฐ (0) | 2022.07.06 |