
developer.android.com
다양한 픽셀 밀도 지원 | Compatibility | Android Developers
width: 100px처럼 고정 크기를 적용하면 디바이스마다 크기가 다르게 보일 수 있다. 기기마다 디스플레이의 픽셀 밀도(DPI/PPI)가 다르기 때문이다.
해상도와 픽셀 밀도 (PPI vs DPI)
![]()
해상도(Resolution)는 디스플레이 화면을 구성하는 전체 픽셀 수다. 예를 들어 FHD 모니터에는 1920 × 1080만큼의 픽셀이 들어 있다. 하지만 픽셀 수만으로는 선명함이나 물리적 크기를 알 수 없다. 동일한 FHD라도 6인치 스마트폰 화면과 65인치 TV 화면에서의 픽셀 크기는 완전히 다르기 때문이다.
- PPI (Pixels Per Inch): 1인치(2.54cm) 공간 안에 들어간 물리적 픽셀의 개수
- DPI (Dots Per Inch): 1인치 안에 찍히는 인쇄 점의 수 (모바일 모니터 밀도를 언급할 때 PPI와 혼용)
![]()
이 값이 클수록 픽셀 하나의 크기가 작아지고 선명해진다. 따라서 절대적인 픽셀 수로 UI를 고정하면, 고밀도 화면일수록 UI가 작아 보이게 된다.
안드로이드 밀도 독립 픽셀 DP (Density-independent Pixel)
![]()
안드로이드는 수천 종류의 제조사 단말 해상도에 대응하기 위해 DP(Density-independent Pixel, dip)라는 가상 단위를 사용한다. 기준점은 160 DPI (mdpi) 화면이다. 160 DPI 기기에서는 1dp = 1px로 계산된다.
320 DPI 화면에서는 1dp를 2px로 2배 올려서 렌더링한다. 결과적으로 100dp로 지정한 버튼은 160 DPI 기기든 320 DPI 기기든 화면에서 비슷한 크기로 보인다.
DP와 PX 변환 공식
// Android Kotlin에서 dp를 실제 px로 변환하는 연산
val dp = 16
val px = dp * (resources.displayMetrics.densityDpi / 160f)
// Android가 제공하는 변환 API를 사용할 수도 있음
// 반대로 px를 dp로 바꿀 때는 AndroidX Core의 `TypedValueCompat.pxToDp()`를 사용
import android.util.TypedValue
val px = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
16f,
resources.displayMetrics
)
densityDpi / 160이 밀도 배율이다. 160 DPI에서는 1, 240 DPI에서는 1.5, 320 DPI에서는 2가 된다. 같은 16dp도 각각 16px, 24px, 32px로 그려진다.
| 밀도 버킷 | 기준 DPI | 배율 | 48dp를 그릴 때 |
|---|---|---|---|
ldpi | 120 | 0.75x | 36px |
mdpi | 160 | 1x | 48px |
hdpi | 240 | 1.5x | 72px |
xhdpi | 320 | 2x | 96px |
xxhdpi | 480 | 3x | 144px |
xxxhdpi | 640 | 4x | 192px |
이 버킷은 리소스를 준비하는 기준으로 삼으면 된다. 실제 densityDpi가 420, 440처럼 중간값일 수 있어서 코드에서 여섯 값 중 하나를 고를 필요는 없다. 현재 밀도에 맞는 배율을 계산하도록 하자.
!warning dp가 실제 인치를 보장하지는 않는다 dp는 물리적 길이 단위가 아니라 UI를 일정한 비율로 확대하는 논리 단위다.
DisplayMetrics.density는 실제 화면의 PPI를 그대로 쓰지 않고 전체 UI를 적당한 단계로 조절한 값이다. 제조사 설정이나 사용자가 바꾼 화면 크기 설정에 따라서도 달라질 수 있다.
실제 PPI에 가까운 값이 필요하면 xdpi, ydpi를 구분해서 봐야 한다. 일반적인 레이아웃에서는 실제 길이보다는 기기 사이의 일관성을 맞추는 것이 목적이다.
이미지 사용 시 밀도별 파일 필요
레이아웃을 48dp로 지정했다고 해서 48px 비트맵 하나가 모든 화면에서 선명하게 보이지는 않는다. xxhdpi 화면은 48dp 영역을 144px로 그리므로 48px 이미지를 사용하면 흐려진다.
res/
drawable-mdpi/icon.png # 48 × 48px
drawable-hdpi/icon.png # 72 × 72px
drawable-xhdpi/icon.png # 96 × 96px
drawable-xxhdpi/icon.png # 144 × 144px
drawable-xxxhdpi/icon.png # 192 × 192px
밀도 한정자가 붙은 폴더에 각각 이미지를 넣는다. Android는 기기 밀도에 가까운 리소스를 알아서 선택한다. 정확히 맞는 파일이 없으면 가까운 비트맵을 확대하거나 축소한다. 이 과정에서도 어쩔 수 없이 이미지가 흐려질 수 있다.
글자 크기는 sp로 지정
sp는 dp의 밀도 계산에 사용자가 설정한 글자 크기까지 반영한다. 레이아웃 크기와 간격에는 dp, 텍스트에는 sp를 사용해야 접근성 설정에서 글자를 키웠을 때 화면도 함께 반응한다.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textSize="16sp" />
padding은 화면 밀도만 따라가고 글자 크기는 화면 밀도와 글자 크기 설정을 함께 따른다. API 34부터 글꼴 확대가 비선형으로 적용될 수 있어서 scaledDensity를 직접 곱하는 방식은 정확하지 않다.
댓글 0개