창 크기 클래스는 반응형/적응형 레이아웃을 디자인, 개말, 테스트하는데 도움을 주도록 권장되는 뷰포트 중단점(viewport breakpoint)의 집합임, 이 중단점은 레이아웃의 단순성과 특정 경우에 앱을 최적화하는 유연성의 균형을 맞춤
창 크기 클래스는 앱을 위해 사용 가능한 디스플레이 영역을 compact, medium, expanded, large, extra large 로 분류함, 사용 가능한 너비와 높이는 별도로 분류되므로 앱은 앱이 실행되는 동안 너비와 높이로 나뉘는 두 창 크기 클래스를 가지고 있음, 수직 스크롤이 보편적으로 사용되기 때문에, 사용 가능한 너비가 일반적으로 사용 가능한 높이보다 중요하며, 따라서 너비 창 크기 클래스가 앱의 UI에 더 관련되어 있음

그림 1. 너비 기반 창 크기 클래스의 구분 예시

그림 1. 높이 기반 창 크기 클래스의 구분 예시
| 크기 클래스 | 중단점 | 기기 대표 예시 |
|---|---|---|
| Compact width | width < 600dp | 세로 모드 휴대폰 99.96% |
| Medium width | 600dp ≤ width < 840dp | 세로 모드 태블릿 93.73%, 대부분의 큰 폴더블 기기의 펼쳐진 내부 디스플레이의 세로 방향 |
| Expanded width | 840dp ≤ width < 1200dp | 가로 모드 태블릿 97.22%, 대부분의 큰 폴더블 기기의 펼쳐진 내부 디스플레이의 가로 방향의 너비는 이 너비임 |
| Large width | 1200dp ≤ width < 1600dp | 대형 태블릿 디스플레이 |
| Extra-large width | width ≥ 1600dp | 데스크톱 디스플레이 |
| Compact height | height < 480dp | 가로 모드 휴대폰 99.78% |
| Medium height | 480dp ≤ height < 900dp | 가로 모드 태블릿 96.56%, 세로 모드 휴대폰 97.59% |
| Expanded height | height ≥ 900dp | 세로 모드 태블릿 94.25% |
노트: 대부분의 앱은 오직 너비 창 크기 클래스만 고려해도 적응형 UI를 만들 수 있습니다. 하지만, 가로 방향의 휴대폰이나 펼쳐진 플립형 기기와 같은 상황에서는 높이 창 크기 클래스도 고려해야 합니다. 이러한 경우 창의 너비는 일반적으로
medium이지만 창의 높이가compact이므로 두 패널 레이아웃은 실용적이지 않습니다.
크기 클래스를 물리적 기기로 시각화하는 것은 유용할 수 있지만, 창 크기 클래스는 명시적으로 기기 화면 크기에 따라 결정되지 않음, 창 크기 클래스는 태블릿을 구분하는 로직을 위해 설계된 것이 아님, 오히려 창 크기 클래스는 기기 종류에 상관 없이 앱이 사용 가능한 창 크기에 따라 결정되고 이는 두 가지 중요한 의미가 있음
창 크기 클래스는 머티리얼 디자인 레이아웃 가이드 의 compact, medium, expanded 중단점에 매핑됨, 창 크기 클래스를 추가 화면 공간을 활용하기 위한 특정 표준 레이아웃을 사용하는 것을 결정하는 것과 같이 상위 수준 앱 레이아웃을 결정하는데 사용할 수 있음
현재 WindowSizeClass 를 Jetpack WindowManager 라이브러리에서 제공되는 WindowSizeClass#compute() 함수를 사용해 측정할 수 있음, 다음 예시는 창 크기 클래스를 측정하는 방법과 창 크기 클래스가 변경될 때 변경점을 전달받는 방법을 보여줌
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
// 안전하게 뷰를 추가할 수 있고 레이아웃에
// 영향을 주지 않고 다른 뷰로 대체되지 않는
// 알려진 컨테이너로 교체하세요.
val container: ViewGroup = binding.container
// View.onConfigurationChanged()에 연결하기 위해
// 유틸리티 뷰를 컨테이너에 추가합니다. 이는 구성 변경을
// 직접 처리하지 않는 액티비티를 포함한 모든 액티비티에서
// 필요합니다. Activity.onConfigurationChanged()는
// 구성 변경시 호출되지 않는 경우가 있으므로
// 사용할 수 없습니다. 그런 상황에서는
// View.onConfigurationChanged()가 호출됩니다.
container.addView(object : View(this) {
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
computeWindowSizeClasses()
}
})
computeWindowSizeClasses()
}
private fun computeWindowSizeClasses() {
val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this)
val width = metrics.bounds.width()
val height = metrics.bounds.height()
val density = resources.displayMetrics.density
val windowSizeClass = WindowSizeClass.compute(width/density, height/density)
// COMPACT, MEDIUM, 또는 EXPANDED
val widthWindowSizeClass = windowSizeClass.windowWidthSizeClass
// COMPACT, MEDIUM, 또는 EXPANDED
val heightWindowSizeClass = windowSizeClass.windowHeightSizeClass
// widthWindowSizeClass 와 heightWindowSizeClass 사용하기.
}
}
레이아웃을 변경할 때 모든 창 크기에서 레이아웃 동작을 테스트 해야 함, 특히 compact, medium, expanded 중단점 너비에서 확인해야 함
compact 화면을 위한 레이아웃이 있다면 먼저 expanded 너비 크기 클래스를 위한 레이아웃을 최적화 하는게 좋음, 이 크기 클래스가 추가 내용과 UI 변화를 위한 가장 많은 공간을 제공하기 때문임, 그 이후에 medium 너비 크기 클래스를 위한 레이아웃이 필요한지 결정하고 특수 레이아웃을 추가하는 것을 고려하면 됨
원문: https://developer.android.com/develop/ui/views/layout/use-window-size-classes