Android WindowSize로 반응형 / 적응형 UI 만들기

동키·2025년 4월 15일

안드로이드

목록 보기
10/14

오늘은 WindowSize 란 것을 이용하여 기기마다 기기의 화면 크기에 따라 다른 UI를 제공하는 방법을 알아보겠습니다.

WindowSizeClass

WindowSizeClass는 앱이 실행되는 창의 가용 공간을 너비와 높이 기준으로 세 가지 크기 범주로 분류합니다.

  • Compact: 좁은 화면 (예: 대부분의 스마트폰)
  • Medium: 중간 크기 화면 (예: 일부 태블릿, 폴더블 디바이스의 내부 화면)
  • Expanded: 넓은 화면 (예: 대부분의 태블릿, 데스크톱 환경)

분류 기준

너비

  • Compact: 너비 < 600dp
  • Medium: 600dp ≤ 너비 < 840dp
  • Expanded: 너비 ≥ 840dp

높이

  • Compact: 높이 < 480dp
  • Medium: 480dp ≤ 높이 < 900dp
  • Expanded: 높이 ≥ 900dp

왜 분류해야 하는데?

WindowSizeClass 를 사용하여 분류하면 앱이 다양한 화면 크기와 형태에 적용할 수 있는 반응형 UI를 구현할 수 있습니다.

이런 식으로 말이죠.

유저 입장에서는 훨씬 친화적이겠죠?

만약 테블릿에서도 스마트폰 UI를 사용하게 된다면 엄청 불편할 겁니다…

즉, WindowSizeClass 를 활용하여 사용자에게 일관되고 최적화된 경험을 제공할 수 있습니다.

그럼 바로 Compose에서 어떻게 사용할 수 있는지 보겠습니다.


활용

androidx-windowSize = {group = "androidx.compose.material3", name = "material3-window-size-class"}

우선 의존성을 추가해줘야 WindowSizeClass 를 사용할 수 있습니다.

그리고 위 사진처럼 기기의 가로 화면에 따라 다른 UI를 제공해보겠습니다.

우선 기기의 사이즈정보를 가져올 수 있는 WindowSizeClass 를 가져오겠습니다.

val windowSize = calculateWindowSizeClass(this)

@ExperimentalMaterial3WindowSizeClassApi
@Composable
fun calculateWindowSizeClass(activity: Activity): WindowSizeClass {
    LocalConfiguration.current
    val density = LocalDensity.current
    val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)
    val size = with(density) { metrics.bounds.toComposeRect().size.toDpSize() }
    return WindowSizeClass.calculateFromSize(size)
}

WindowSizeClass 에서 제공하는 calculateWindowSizeClass 를 사용하여 정보를 가져올 수 있습니다.

WindowSizeClass가 어떻게 작성되었을까요?

@Immutable
class WindowSizeClass private constructor(
    val widthSizeClass: WindowWidthSizeClass,
    val heightSizeClass: WindowHeightSizeClass
) {
	value class WindowWidthSizeClass private constructor(private val value: Int) :
    Comparable<WindowWidthSizeClass> {
	    companion object {
        val Compact = WindowWidthSizeClass(0)
        val Medium = WindowWidthSizeClass(1)
        val Expanded = WindowWidthSizeClass(2)
    }
    
    value class WindowHeightSizeClass private constructor(private val value: Int) :
    Comparable<WindowHeightSizeClass> {
	     companion object {
        val Compact = WindowHeightSizeClass(0)
        val Medium = WindowHeightSizeClass(1)
        val Expanded = WindowHeightSizeClass(2)
    }
}

WindowSizeClass 안에 value class로 Width와 Height 사이즈 클래스가 있고 그 안에 값에 따라 나뉘는 Compact , Medium , Expanded 로 나뉘고 있는 모습을 확인할 수 있습니다.

바로 이를 활용하여 기기 사이즈마다 다른 UI를 제공할 수 있습니다.

when (windowSize) {
        WindowWidthSizeClass.Compact -> {
            Scaffold(
                bottomBar = { BottomNavigationBar(navController = navController) }
            ) { innerPadding ->
                Box(modifier = Modifier.padding(innerPadding))
                content(innerPadding)
            }
        }
        WindowWidthSizeClass.Medium -> {
            Scaffold { innerPadding ->
                Row(modifier = Modifier.padding(innerPadding).fillMaxSize()) {
                    NavigationRailContent(navController = navController)
                    Box(modifier = Modifier.fillMaxSize().padding(start = 16.dp)) {
                        content(PaddingValues())
                    }
                }
            }
        }
        WindowWidthSizeClass.Expanded -> {
            PermanentNavigationDrawer(
                drawerContent = {
                    PermanentDrawerSheet(
                        modifier = Modifier.width(240.dp),
                        drawerContainerColor = MaterialTheme.colorScheme.inverseOnSurface
                    ) { // PermanentDrawerSheet Scope
                        NavigationDrawerContent(navController = navController)
                    }
                }
            ) {
                Box(modifier = Modifier.fillMaxSize()) {
                    content(PaddingValues())
                }
            }
        }
    }

이런식으로 저는 WindowWidthSizeClass 의 값에 따라 다른 UI를 제공했습니다.

  • Compact 가장 기본적인 스마트폰 형태이기 때문에 바텀 네비게이션 바를 설정했습니다.
  • Medium 일부 태블릿, 폴더블 휴대폰이기 때문에 NavigationRail 을 사용했습니다.
  • Expanded 태블릿, 윈도우기 때문에 PermanentNavigationDrawer 를 사용했습니다.

NavigationRailDrawer에 대한 자세한 내용은 링크에 들어가시면 자세한 내용을 확인하실 수 있습니다.


참고문헌

https://m3.material.io/components/navigation-rail/overview

https://developer.android.com/develop/ui/compose/layouts/adaptive/list-detail?hl=ko

https://developer.android.com/develop/ui/compose/layouts/adaptive/use-window-size-classes

profile
오늘 하루도 화이팅

0개의 댓글