-
Notifications
You must be signed in to change notification settings - Fork 0
컴포즈‐Navigation 활용해보기
jimbo edited this page Jun 2, 2024
·
4 revisions
- xml Fragment Navigation 과 동일한 compose Navigation 을 사용하여 간단한 페이지 흐름에 대해서 익힙니다.
- 여러 화면들을 간단히 구성하고 화면마다 데이터를 전달합니다.
- Compose 전용 Glide Image Loader를 만들어 봅니다.
- 화면 이동시 유지보수측면에서 나은 방식이 있는지 고민합니다.
- NavGraphBuilder 형태를 분석하면 url 방식으로 데이터를 전달합니다. 즉, 흔한 방식으로 사용되는
Parcelable
형태로 객체를 역/직렬화 하는 방식이 아닌 문자열로 이루어진 url 로 데이터를 전달하는 방향으로 설계 되어 있습니다. - url 방식이다보니 path/{arguments1}/{arguments2} 방식으로 처리하는 경우 무조건 필수 인자로 처리해야 합니다. 만약 인자값이 nullable인 경우에는 해당 방식으로 처리할때 이슈사항이 있습니다.
- Android 도큐먼트에 나와있는것처럼 필수인자값인경우에는
path/{requiredArgument}
필수가아닌 경우path/optionArgument={optionArgument}
방식으로 하라고 나와 있습니다. - 또한, A->B 화면으로 이동할때는 상기 방식으로 데이터를 전달하고, 이전 화면에 데이터를 전달하려면 NavController previousBackStackEntry 를 사용하여 간단히 해결할수 있습니다.
- 확장 함수로 만들어두면 좋을거 같아서 아래와 같이 만들어봤습니다.
inline fun NavController.prevPutBundle(
predicate: SavedStateHandle.() -> Unit
) {
val savedStateHandle = previousBackStackEntry?.savedStateHandle ?: return
predicate.invoke(savedStateHandle)
}
- 현재 Glide에서 지원하는 이미지 라이브러리 추가합니다.
implementation('com.github.bumptech.glide:compose:1.0.0-beta01')
- core 모듈에 공통으로 두면 좋을거 같아서 아래와 같이 코드를 추가 했습니다.
@OptIn(ExperimentalGlideComposeApi::class)
@SuppressLint("ModifierParameter")
@Composable
fun ImageLoader(
imageUrl: String,
contentScale: ContentScale = ContentScale.Crop,
modifier: Modifier = Modifier,
) {
GlideImage(
model = imageUrl,
contentDescription = null,
modifier = modifier,
loading = placeholder(ColorPainter(TilTheme.color.gray3Light)),
failure = placeholder(R.drawable.ic_error),
contentScale = contentScale
) { requestBuilder ->
requestBuilder.diskCacheStrategy(DiskCacheStrategy.NONE)
}
}
- compose 버전이 5.0.0-rc 버전이라 기존에 있던 버전 같이 올리는게 좋을거 같아서 같이 5.0.0 으로 올렸는데 이때 문제점이 발생했습니다.
- 기존에 AppGlideModule 안에 있는 함수들이 호출이 안되는 이슈가 발생했습니다. 확인해본 결과 Glide 4.16.x 이후 부터 ksp 이슈로 인하여 reflect 를 잘못 하고 있는 이슈가 있었습니다..(일해라 구글..bumbitch)
- 물론 해결방법이야 어떤 사람이 PR 을 날렸고 merge 까지한 상태입니다. 하지만 무슨 이유인지 릴리즈를 안해서 해결된 버전을 사용할수 없었습니다. 결국 4.13.2 에서 멈추고 compose 만 버전 올리는 방식으로 처리했습니다.
- 참고 링크
- NavGraphBuilder 분석하여 공통으로 사용되는 것들과 개발에 필요한 부분들을 분리하여 공통 함수등을 설계했습니다.
-
path{requiredArgument}
로 Route URL 구성하면 여러가지 제약사항이 있어서 Optional 하게 Argument 구성해봤습니다.
enum class Screens(
val destination: String,
val arguments: List<NamedNavArgument> = listOf() // type == StringType 만 가능
) {
LOGIN(
destination = "login",
arguments = listOf(
navArgument("user_id") {
type = NavType.StringType
nullable = true
},
navArgument("user_pw") {
type = NavType.StringType
nullable = true
}
)
),
MEMO(
destination = "memo",
arguments = listOf(
navArgument("user_id") {
type = NavType.StringType
}
)
);
fun getNavGraph(
builder: NavGraphBuilder,
content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit
) {
val route = StringBuilder(destination)
if (arguments.isNotEmpty()) {
route.append("?")
route.append(arguments.joinToString("&") { "${it.name}={${it.name}}" })
}
return builder.composable(
route = route.toString(),
arguments = arguments,
content = content
)
}
/**
* 화면에 정의된 Argument 스펙 기준으로 파라미터 셋팅해서 URL 형식으로 전달하는 함수
* @param argumentsMap 다음 화면에 전달할 파라미터 데이터
*/
fun getNavigation(
argumentsMap: Map<String, Any?> = mapOf()
): String {
val route = StringBuilder(destination)
if (arguments.isNotEmpty()) {
route.append("?")
route.append(arguments.mapNotNull {
val value = argumentsMap[it.name]
if (value != null) {
"${it.name}=$value"
} else {
null
}
}.joinToString("&"))
}
return route.toString()
}
}
- 상기 방식으로 화면들을 정의했을때 화면들의 경로 방식은 아래처럼 구성이 됩니다.
- /login?user_id={user_id}&user_pw={user_pw}
- 화면 전환을 할때 데이터가 필요한 경우 아래와 같이 간단히 처리할수 있습니다.
Screens.LOGIN.getNavigation(
mapOf(
"user_id" to id.value,
"user_pw" to pw.value
)
)
- 구글에서 지향하는 Navigation 방식이 뭔지 잘 알거 같았습니다. 이제 더이상 공식적으로 Object -> Bytes, Bytes -> Object 방식으로 하는 역/직렬화는 사용하지 말라는건가 싶기도 합니다.. 물론 꼼수로 인자값에 json 형식으로 변환해서 Object -> String, String -> Object 로 할수는 있습니다만, 뭔가 지금까지 자기들이 만든 Parcelable, Parcelize 를 배제한다는게 참..아이러니하다는 생각이 들었습니다...
- UI 를 그리는 클라이언트 입장에서 xml CoordinatorLayout in CollapseToolbarlayout 를 잘 다루어야 합니다. compose 에도 이와 같은 기능을 하는게 있지만, 아직은 학습이 좀 부족해서 애매하게 스크롤을 하는 경우 뚝뚝 끊기는? 이슈가 있었습니다. 이후 한번더 공부해서 잘 다룰수 있도록 할 생각입니다.