클론 코딩 과정에서, 우연히 폴드 에뮬레이터에 앱을 실행해 보았다.
폴드의 펼쳐진 화면으로 보니 너무 못생겼다.
나름 어떤 크기의 폼팩터에도 맞는 UI를 개발하기 위해 반응형을 염두해 개발하긴 했지만...
역시 직접 맞아보기 전까지는 모른다.
내가 가진 테스트 폰이 Galaxy Flip 4와 Galaxy S10 5G이라, 폴드화면은 생각하지 못했나보다.
아무튼... 이 과정을 클론코딩에 적용해보았다.
기존 figma에 있는 Calendar.io라는 UI를 구현한 화면이다.
디자인과 폼팩터 크기가 비교적 비슷하기에 UI가 제법 유사하게 나올 수 있었다.
이 상태에서 Fold의 펼친 화면으로 바꿔보았다.
뭔가가 잘못되었다.
캘린더와 바텀 네비게이션바 사이에 있어야 할 일정 목록이 안보인다.
뭔가 이상해서 레이아웃 범위를 표시해보니, 각 일자별 그리드 크기가 엄청 크게 잡혀있었다.
캘린더의 각 그리드 크기를 6:5로 설정하여 화면 비율에 맞도록 사이즈를 잡아놨는데, 폼팩터의 너비가 크다보니 세로 크기도 늘어난 모양이다.
// 각 그리드 별 크기를 화면에 맞도록 설정, 너비 6 : 높이 5
Column(
modifier = Modifier
.weight(1f)
.aspectRatio(6f / 5f)
.then(
if (item.isCurrDay) {
Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
onSelectedDayChanged(item.day)
}
} else {
Modifier
}
),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
)
이에 대한 대응으로, 캘린더의 maxWidth를 설정하여 캘린더의 최대 가로폭을 400dp 정도로 강제해보았다.
하지만, 양쪽에 빈 공간이 너무 거슬린다. 바텀 네비게이션바는 큰데, 내부 컨텐츠는 너무 부실해보인달까?
게다가, 아래 일정 목록 부분이 보이긴 하지만 잘 안보이는 듯하다.
이렇게 넓은 폴드의 펼친 화면에는 캘린더와 일정 목록을 양 옆으로 펼치면 사용하기 좋을 것 같다.
그럼, 폴드가 펼쳐져 있는 상태라는 것을 어떻게 알 수 있을까?
androidx의 window 라이브러리에서는 WindowInfoTracker를 통해서 디스플레이의 Bound와 현재 폴드 화면의 상태를 담고 있는 foldingFeatrues를 가져올 수 있다.
1) 라이브러리 가져오기
/** version catalog */
// version: 1.3.0
window = {group = "androidx.window", name = "window", version.ref = "window"}
/** Gradle Module */
implementation(libs.window)
2) 액티비티에서 foldingFeatures 가져오기
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
windowInfoTracker.windowLayoutInfo(this@CalendarActivity)
.collect { windowLayoutInfo ->
Log.d("WindowInfo", "Window Layout Info: $windowLayoutInfo")
val foldingFeatures = windowLayoutInfo.displayFeatures.filterIsInstance<FoldingFeature>()
if (foldingFeatures.isNotEmpty()) {
for (feature in foldingFeatures) {
Log.d("FoldingFeature", "Folding feature state: ${feature.state}")
when (feature.state) {
FoldingFeature.State.HALF_OPENED -> {
Log.d("FoldingFeature", "반 접힘")
}
FoldingFeature.State.FLAT -> {
Log.d("FoldingFeature", "완전히 펼침")
}
}
}
} else {
Log.d("FoldingFeature", "폴더 관련 정보 X")
}
}
}
}
}
출력 결과
이런 식으로, 펼쳐진 상태를 잘 트리거한 것을 볼 수 있다.
이제 트리거한 상태를 기준으로 폴드 화면 UI를 적절히 배치하면 되겠다.
UI 배치
viewModel에서 FoldingFeature를 최신화 시키고, FoldingFeature의 상태에 따라서 UI 형태를 달리 하였다.
if (state.value.foldingFeatureList.isNotEmpty() && state.value.foldingFeatureList[0].state == FoldingFeature.State.FLAT) {
CalendarBodyRow(
viewModel = calendarViewModel,
modifier = Modifier.fillMaxSize()
)
} else {
CalendarBody(
viewModel = calendarViewModel,
modifier = Modifier
.widthIn(max = 400.dp)
.fillMaxSize()
)
}
결과 화면
초기 펼쳐진 폴드 화면들 보다는 적절하게 잘 배치한 것처럼 보인다.
초기 폴더블 기기가 나왔을 때만 해도 대응이 되어 있는 앱은 찾기 어려웠는데, 폴더블 기기가 종류가 다양해지면서 많은 앱들이 폴더블 UI를 신경쓰고 있는 듯하다.
폴더블 기기에 대한 화면 대응을 어느정도 알아 갈 수 있어서 좋은 경험이었다고 생각한다.
추가적으로, 안드로이드는 화면 분할 기능을 지원하므로, 화면 비율에 따라 UI를 적절히 대응한다면 더욱 완성도 높은 서비스를 제공할 수 있겠다고 생각이 들었다.
물론, UI의 형태나 기능에 따라 이러한 부분에 제약이 있을 수는 있겠지만 말이다.