Android 16 대응을 하면서 configuration에 대해 알아보다 "density" 값에 대해 좀 더 알아볼 필요를 느껴 정리하게 되었다.
해당 옵션 값은 우선 화면 밀도가 변할 때 적용되는 값으로, 폴드를 접은 상태에서 펼칠 때 등의 상황에 변경될 수 있다.
density
화면 밀도, 좀 더 정확히 말하자면 1dp를 몇 px로 그릴지를 정하는 배율 숫자이다.
dp는 160dpi(mdpi) 화면의 1px을 기준으로 한 가상 단위이며, 실제 px는 현재 density 배율을 곱해 계산된다.

- dpi(densityDpi): 화면의 밀도 값. 실제 물리 밀도가 아니라 시스템이 정한 논리 밀도다.
- density(DisplayMetrics.density): dp를 px로 바꾸는 배율. mdpi에서 1.0, hdpi에서 1.5, xhdpi에서 2.0이다.
공식 문서는 위 공식을 코드에 직접 쓰지 말고 TypedValue.applyDimension()을 쓰도록 안내한다. ViewConfiguration의 getScaled* 메서드처럼 이미 변환된 값을 주는 API도 있다.
// 공식 문서 예시: dp → px
val gestureThreshold = TypedValue.applyDimension(
COMPLEX_UNIT_DIP,
GESTURE_THRESHOLD_DP + 0.5f,
resources.displayMetrics
).toInt()
px
화면의 물리적인 점 하나,
"1080×2400 화면"이라고 하면 가로 1080개, 세로 2400개의 점이 있다는 뜻.
같은 크기의 화면이라도 점의 촘촘함이 기기마다 다르다.
100px 버튼은 성긴 화면에서는 크게, 촘촘한 화면에서는 작게 보일 수 있다.
dpi
1인치에 점이 몇 개 들어가는지를 나타낸다.
값이 클수록 화면이 촘촘하다.
dp
160dpi 화면(1인치 안에 160px이 존재)의 1px을 1dp로 정한 기준 단위. 실제 크기로는 약 1/160인치.
촘촘한 화면일수록 1dp에 더 많은 px를 써서(dp의 실제 인치에 맞춰서 px 소비), 어느 기기에서든 비슷한 실제 크기로 보이게 한다.
예: 48dp 버튼은 160 dpi 화면에서 48px, 480 dpi 화면에서 144px이다. 두 화면 모두 실제 크기는 약 0.3인치.
그렇기 때문에 레이아웃은 dp로 쓰고, px 변환은 시스템이 density 배율로 처리한다.
| 구분 | 무엇인가 | 예시 |
| px | 단위. 화면의 실제 점 1개 | 1px |
| 해상도 | 화면 전체를 px로 잰 값 | 가로 1080px × 세로 2400px |
| dp | 단위. 160dpi 기준의 가상 픽셀 1개 | 1dp |
| 화면 | 1인치 안의 px | 1dp = 몇 px | 1인치 = 몇 dp |
| 160dpi | 160px | 1px | 160dp |
| 320dpi | 320px | 2px | 160dp |
| 480dpi | 480px | 3px | 160dp |
밀도 한정자 (density qualifier)
drawable-xhdpi/처럼 리소스 폴더 이름 뒤에 붙여 어느 밀도 화면용 리소스인지 표시한다. 앱 실행 시 시스템이 기기의 densityDpi에 가장 맞는 폴더를 고른다.
| 한정자 | dpi(대상 밀도) | 배율 | 48dp 아이콘 | 의미 |
| ldpi | ~120dpi | 0.75x | 36px | 저밀도. 현재는 거의 쓰이지 않음 |
| mdpi | ~160dpi | 1.0x | 48px | 기준 밀도 (1dp = 1px) |
| tvdpi | ~213dpi | 1.33x | 약 64px | mdpi와 hdpi 사이. 주요 버킷은 아님 |
| hdpi | ~240dpi | 1.5x | 72px | 고밀도 |
| xhdpi | ~320dpi | 2.0x | 96px | 초고밀도 |
| xxhdpi | ~480dpi | 3.0x | 144px | 최근 폰에서 흔한 밀도 |
| xxxhdpi | ~640dpi | 4.0x | 192px | 런처 아이콘 등에 사용 |
| nodpi | 전체 | — | 원본 크기 | 밀도와 관계없이 스케일하지 않음 |
| anydpi | 전체 | — | 벡터로 렌더링 | 벡터처럼 모든 밀도에 쓰는 리소스. 다른 밀도 폴더보다 우선함 |
- 주요 6개 밀도는 3:4:6:8:12:16 비율을 따른다.
- 딜 맞는 폴더가 없으면 가까운 밀도의 리소스를 확대·축소해 쓴다. 예를 들어 420 dpi 기기는 보통 xxhdpi(480) 리소스를 축소해 쓴다.
- 낮은 밀도의 비트맵을 확대하면 흐려질 수 있어, 비트맵은 여러 밀도로 준비하거나 벡터 드로어블을 쓰는 것이 권장된다.
(Support different pixel densities)
💡 sp와 fontScale
폰트, 글자 크기의 단위 sp.
sp는 기본적으로 dp와 같은 크기지만 사용자의 글자 크기 설정(fontScale)을 따라 커지는 단위이며,
Android 14부터는 단순 곱셈이 아닌 비선형 곡선으로 계산된다.
(글꼴 크기 설정이 기본값(100%)이면 sp와 dp는 크기가 똑같다)
sp는 텍스트 크기에만 쓰고 레이아웃 크기에는 쓰지 않는다.
density와 fontScale은 별개의 Configuration 항목이다.
"화면 크기(Display size)" 설정은 density를, "글꼴 크기(Font size)" 설정은 fontScale을 바꾼다.
Android 14는 최대 200%까지 글자 확대를 지원하고, 큰 글자는 작은 글자보다 덜 커지도록 비선형으로 스케일 한다.
그래서 4sp + 20sp가 24sp와 같지 않을 수 있다. padding이나 높이를 sp로 계산하면 비율이 깨질 수 있다.
lineHeight는 textSize와 함께 sp로 지정해야 TextView가 비율을 보정해 준다.
sp ↔ px 변환
val metrics = resources.displayMetrics
val textPx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16f, metrics)
val textSp = TypedValue.deriveDimension(TypedValue.COMPLEX_UNIT_SP, textPx, metrics)
TypedValue.applyDimension()(sp → px)과 TypedValue.deriveDimension()(px → sp)을 쓰면 비선형 곡선이 자동 적용된다.
테스트는 설정 > 접근성 > 디스플레이 크기 및 텍스트에서 글꼴 크기를 최대(200%)로 올려 확인할 수 있다. (Android 14 features)
이제 sp는 비선형 곡형으로 계산되기 때문에 Configuration.fontScale이나 DisplayMetrics.scaledDensity로 직접 계산하지 말라고 안내한다.
scaledDensity는 더 이상 정확하지 않고, fontScale은 참고용으로만 써야 한다.
density가 런타임에 바뀌는 상황
공식 문서는 density 변경을 "사용자가 다른 디스플레이 배율을 지정하거나, 다른 디스플레이가 활성화된 경우"로 설명한다.
| 상황 | 함께 바뀔 수 있는 값 |
| 설정에서 "화면 크기(Display size)" 변경 | density, screenSize, smallestScreenSize, screenLayout |
| 폴더블 내부/외부 화면 전환 | density, screenSize, smallestScreenSize, screenLayout |
| 외부 디스플레이로 창 이동 (데스크톱 모드 등) | density, smallestScreenSize, screenLayout |
| 설정에서 "글꼴 크기" 변경 | fontScale (density는 바뀌지 않음) |
화면 크기 설정을 바꾸면 dp 기준 화면 너비도 함께 바뀌므로, density 단독으로만 바뀌는 경우는 드물다.
그래서 만약 configChanges를 사용하여 Activity 재생성을 방지하기 위해 density만 선언하면 함께 바뀐 다른 항목 때문에 결국 재생성될 수 있다. Configuration 변경은 선언하지 않은 항목이 하나라도 바뀌면 재생성되기 때문이다.
ADB 명령어로도 직접 변경하여 확인할 수 있다.
# 현재 값 확인
adb shell wm density
# Physical density: 420 ← 기기 기본값
# Override density: 480 ← 바꾼 값 (바꾼 적이 있을 때만 표시)
# density 변경
adb shell wm density 480
# 원래 값으로 복구
adb shell wm density reset
추가 학습 거리
- configChanges 옵션, 동작
- View(XML)와 Compose의 반영 방식
- Compose Dialog와 density
참고 자료
https://developer.android.com/training/multiscreen/screendensities
다양한 픽셀 밀도 지원 | Compatibility | Android Developers
Google은 AI 기술을 사용하여 콘텐츠를 사용자의 기본 언어로 번역합니다. AI 번역에는 오류가 있을 수 있습니다. 다양한 픽셀 밀도 지원 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠
developer.android.com
Support different pixel densities
다양한 픽셀 밀도 지원 | Compatibility | Android Developers
Google은 AI 기술을 사용하여 콘텐츠를 사용자의 기본 언어로 번역합니다. AI 번역에는 오류가 있을 수 있습니다. 다양한 픽셀 밀도 지원 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠
developer.android.com
<activity> — android:configChanges
<activity> | App architecture | Android Developers
애플리케이션의 시각적 사용자 인터페이스 일부를 구현하는 활동(Activity 서브클래스)을 선언합니다. 모든 활동은 매니페스트 파일의 {@code } 요소로 나타내야 합니다. 여기에 선언되지 않은 활동
developer.android.com
Android 14 features — Nonlinear font scaling
기능 및 API 개요 | Android Developers
Android 14의 개발자를 위한 기능과 API에 대해 알아봅니다.
developer.android.com
'Android' 카테고리의 다른 글
| [일상] 2025 안드로이드 탐구 영역 - 2025학년도 개발능력시험 문제지 안드로이드탐구 영역 (1) | 2024.12.08 |
|---|