개발자는 폴더블용 애플리케이션을 만들 때, 특히 가로 모드 형식 (rotation_0 = 가로 모드)으로 열리는 Samsung Trifold 또는 원래 Pixel Fold와 같은 기기에서 고유한 어려움을 겪는 경우가 많습니다. 개발자 실수에는 다음이 포함됩니다.
- 기기 방향에 관한 잘못된 가정
- 간과된 사용 사례
- 구성 변경 시 값을 다시 계산하거나 캐시하지 못함
특정 기기 관련 문제는 다음과 같습니다.
- 커버 디스플레이와 내부 디스플레이 간의 기기 자연스러운 방향 불일치 (rotation_0 = 세로 모드에 기반한 가정)로 인해 접기 및 펼치기 여정에서 앱이 실패함
- 다양한 화면 밀도 및 잘못된 density 구성 변경 처리
- 자연스러운 방향에 대한 카메라 센서 종속성으로 인해 발생하는 카메라 미리보기 문제
폴더블 기기에서 고품질 사용자 환경을 제공하려면 다음의 중요한 영역에 집중하세요.
- 기기의 물리적 방향이 아닌 앱이 차지하는 실제 화면 영역을 기반으로 앱의 방향을 결정합니다.
- 카메라 미리보기를 업데이트하여 기기 방향과 가로세로 비율을 올바르게 관리하고, 옆으로 표시되는 미리보기를 방지하며, 늘어나거나 잘린 이미지를 방지합니다.
ViewModel또는 유사한 접근 방식으로 상태를 유지하거나 화면 밀도 변경 및 방향 변경을 수동으로 처리하여 기기 접기 또는 펼치기 중에 앱 연속성을 유지합니다. 이렇게 하면 앱이 다시 시작되거나 상태가 손실되지 않습니다.- 모션 센서를 활용하는 앱의 경우 좌표계를 조정하여 화면의 현재 방향에 맞추고 rotation_0 = 세로 모드에 기반한 가정을 방지하여 정확한 사용자 상호작용을 보장합니다.
적응형 빌드
앱이 이미 적응형이고 적응형 앱 품질 가이드라인에 설명된 최적화된 수준 (2단계) 을 준수하는 경우 폴더블 기기에서 앱이 제대로 작동해야 합니다. 그렇지 않은 경우 Trifold 및 가로 모드 폴더블의 세부정보를 다시 확인하기 전에 다음의 기본적인 Android 적응형 개발 개념을 검토하세요.
적응형 레이아웃
UI는 다양한 화면 크기뿐만 아니라 펼치기, 멀티 윈도우 또는 데스크톱 창 모드 진입과 같은 가로세로 비율의 실시간 변경사항을 처리해야 합니다. 다음 방법을 자세히 알아보려면 적응형 레이아웃 정보를 참고하세요.
- 적응형 레이아웃 설계 및 구현
- 창 크기에 따라 앱의 기본 탐색 조정
- 창 크기 클래스를 사용하여 앱의 UI 조정
- Jetpack API를 사용하여 목록-세부정보와 같은 표준 레이아웃 구현 간소화
창 크기 클래스
가로 모드 폴더블 및 Trifold를 비롯한 폴더블 기기는 컴팩트, 보통, 펼침 창 크기 클래스 간에 즉시 전환할 수 있습니다. 이러한 클래스를 이해하고 구현하면 앱이 현재 기기 상태에 맞는 올바른 탐색 구성요소와 콘텐츠 밀도를 표시할 수 있습니다.
다음 예에서는 Material 3 적응형 라이브러리를 사용하여 먼저
currentWindowAdaptiveInfo() 함수를 호출한 다음 세 가지 창 크기 클래스에 해당하는
레이아웃을 사용하여 앱에서 사용할 수 있는 공간을 결정합니다.
val adaptiveInfo = currentWindowAdaptiveInfo()
val windowSizeClass = adaptiveInfo.windowSizeClass
when {
windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_EXPANDED_LOWER_BOUND) -> // Expanded
windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND) -> // Medium
else -> // Compact
}
자세한 내용은 창 크기 클래스 사용을 참고하세요.
적응형 앱 품질
적응형 앱 품질 가이드라인의 2단계 (적응형 최적화) 또는 1단계 (적응형 차별화)를 준수하면 앱이 Trifold 기기, 가로 모드 폴더블, 기타 대형 화면 기기에서 매력적인 사용자 환경을 제공할 수 있습니다. 이 가이드라인은 적응형 준비 상태에서 차별화된 환경으로 전환하기 위해 여러 단계 수준에서 중요한 검사를 다룹니다.
Android 16 이상
Android 16 (API 수준 36) 이상을 타겟팅하는 앱의 경우 시스템은 최소 너비가 600dp 이상인 디스플레이에서 방향, 크기 조절 가능 여부, 가로세로 비율 제한을 무시합니다. 앱은 가로세로 비율 또는 사용자가 선호하는 방향과 관계없이 전체 디스플레이 창을 채우며 레터박스 처리 호환성 모드는 더 이상 사용되지 않습니다.
특별 고려사항
Trifold 및 가로 모드 폴더블은 특히 센서, 카메라 미리보기, 구성 연속성 (접기, 펼치기 또는 크기 조절 시 상태 유지)과 관련하여 특별한 처리가 필요한 고유한 하드웨어 동작을 도입합니다.
카메라 미리보기
가로 모드 폴더블 또는 가로세로 비율 계산 (멀티 윈도우, 데스크톱 창 모드 또는 연결된 디스플레이와 같은 시나리오)에서 흔히 발생하는 문제는 카메라 미리보기가 늘어나거나, 옆으로 표시되거나, 잘리거나, 회전하는 경우입니다.
일치하지 않는 가정
이 문제는 앱이 가로세로 비율 및 센서 방향과 같은 카메라 기능과 기기 방향 및 자연스러운 방향과 같은 기기 기능 간의 고정된 관계를 가정할 수 있으므로 대형 화면 및 폴더블 기기에서 자주 발생합니다.
새로운 폼 팩터는 이러한 가정을 어렵게 만듭니다. 폴더블 기기는 기기 회전이 변경되지 않고도 디스플레이 크기와 가로세로 비율을 변경할 수 있습니다. 예를 들어 기기를 펼치면 가로세로 비율이 변경되지만 사용자가 기기를 회전하지 않으면 회전이 동일하게 유지됩니다. 앱이 가로세로 비율이 기기 회전과 상관관계가 있다고 가정하면 카메라 미리보기를 잘못 회전하거나 조정할 수 있습니다. 앱이 카메라 센서 방향이 세로 모드 기기 방향과 일치한다고 가정하는 경우에도 동일한 문제가 발생할 수 있습니다. 이는 가로 모드 폴더블에서는 항상 사실이 아닙니다.
솔루션 1: Jetpack CameraX (최고)
가장 간단하고 강력한 해결 방법은 Jetpack CameraX 라이브러리를 사용하는 것입니다. 이 라이브러리의
PreviewView UI 요소는 모든 미리보기 복잡성을
자동으로 처리하도록 설계되었습니다.
PreviewView는 센서 방향, 기기 회전, 조정에 맞게 올바르게 조정됩니다.- 일반적으로 가운데 맞춤 및 자르기 (FILL_CENTER)를 통해 카메라 이미지의 가로세로 비율을 유지합니다.
- 필요한 경우 확장 유형을
FIT_CENTER로 설정하여 미리보기를 레터박스 처리할 수 있습니다.
자세한 내용은 CameraX 문서에서 미리보기 구현을 참고하세요.
솔루션 2: CameraViewfinder
기존 Camera2 코드베이스를 사용하는 경우 CameraViewfinder 라이브러리(API 수준 21과 이전 버전 호환)가 또 다른 최신 솔루션입니다. TextureView 또는 SurfaceView를 사용하고 필요한 모든 변환 (가로세로 비율, 조정, 회전)을 적용하여 카메라 피드 표시를 간소화합니다.
자세한 내용은 카메라 뷰파인더 소개 블로그 게시물 및 카메라 미리보기 개발자 가이드를 참고하세요.
솔루션 3: 수동 Camera2 구현
CameraX 또는 CameraViewfinder를 사용할 수 없는 경우 방향과 가로세로 비율을 수동으로 계산하고 각 구성 변경 시 계산이 업데이트되도록 해야 합니다.
CameraCharacteristics에서 카메라 센서 방향 (예: 0, 90, 180, 270도)을 가져옵니다.- 기기의 현재 디스플레이 회전 (예: 0, 90, 180, 270도)을 가져옵니다.
- 이 두 값을 사용하여
SurfaceView또는TextureView에 필요한 변환을 결정합니다. - 왜곡을 방지하려면 출력
Surface의 가로세로 비율이 카메라 미리보기의 가로세로 비율과 일치하는지 확인합니다. - 카메라 앱은 멀티 윈도우 또는 데스크톱 창 모드 또는 연결된 디스플레이에서 화면의 일부에서 실행될 수 있습니다. 따라서 화면 크기를 사용하여 카메라 뷰파인더의 크기를 결정해서는 안 되며 대신 창 측정항목을 사용해야 합니다.
자세한 내용은 카메라 미리보기 개발자 가이드 및 다양한 폼 팩터의 카메라 앱 동영상을 참고하세요.
솔루션 4: 인텐트를 사용하여 기본 카메라 작업 실행
카메라 기능이 많이 필요하지 않은 경우 간단한 해결 방법은 기기의 기본 카메라 애플리케이션을 사용하여 사진이나 동영상 촬영과 같은 기본 카메라 작업을 실행하는 것입니다. 카메라 라이브러리와 통합할 필요가 없으며 대신 인텐트를 사용합니다.
자세한 내용은 카메라 인텐트를 참고하세요.
구성 및 연속성
폴더블 기기는 UI 다용도성을 향상하지만 폴더블이 아닌 기기보다 더 많은 구성 변경을 시작할 수 있습니다. 앱은 앱 상태를 유지하거나 복원하면서 멀티 윈도우 또는 데스크톱 모드에서 기기 회전, 접기/펼치기, 창 크기 조절과 같은 이러한 구성 변경 및 조합을 관리해야 합니다. 예를 들어 앱은 다음과 같은 연속성을 유지해야 합니다.
- 비정상 종료되거나 사용자에게 방해가 되는 변경사항 (예: 화면 전환 또는 앱을 백그라운드로 전송할 때)이 없는 앱 상태
- 스크롤 가능한 필드의 스크롤 위치
- 텍스트 필드에 입력한 텍스트 및 키보드 상태
- 구성 변경이 시작되었을 때 중단된 지점부터 재생이 다시 시작되도록 미디어 재생 위치
자주 트리거되는 구성 변경에는 screenSize, smallestScreenSize, screenLayout, orientation, density, fontScale, touchscreen, keyboard가 포함됩니다.
android:configChanges 및 구성 변경 처리를 참고하세요. 앱 상태 관리에 관한 자세한 내용은 UI 상태 저장을 참고하세요.
밀도 구성 변경
Trifold 및 가로 모드 폴더블 기기의 외부 및 내부 화면은 픽셀 밀도가 다를 수 있습니다. 따라서 density의 구성 변경을 관리하려면 추가 주의가 필요합니다. Android는 일반적으로 디스플레이 밀도가 변경될 때 활동을 다시 시작하므로 데이터가 손실될 수 있습니다. 시스템이 활동을 다시 시작하지 못하도록 하려면 매니페스트에서 밀도 처리를 선언하고 앱에서 구성 변경을 프로그래매틱 방식으로 관리하세요.
AndroidManifest.xml 구성
density: 앱이 화면 밀도 변경을 처리한다고 선언합니다.- 기타 구성 변경:
screenSize,orientation,keyboardHidden,fontScale등 자주 발생하는 기타 구성 변경을 선언하는 것도 좋습니다.
밀도 (및 기타 구성 변경)를 선언하면 시스템이 활동을 다시 시작하지 않고 대신 onConfigurationChanged()를 호출합니다.
onConfigurationChanged() 구현
밀도 변경이 발생하면 콜백에서 리소스 (예: 비트맵 다시 로드 또는 레이아웃 크기 다시 계산)를 업데이트해야 합니다.
- DPI가
newConfig.densityDpi로 변경되었는지 확인합니다. - 맞춤 뷰, 맞춤 드로어블 등을 새 밀도로 재설정합니다.
처리할 리소스 항목
- 이미지 리소스: 비트맵과 드로어블을 밀도별 리소스로 바꾸거나 직접 조정을 조정합니다.
- 레이아웃 단위 (dp-px 변환): 뷰 크기, 여백, 패딩을 다시 계산합니다.
- 글꼴 및 텍스트 크기: sp 단위 텍스트 크기를 다시 적용합니다.
- 맞춤
View/Canvas그리기: 그리는 데 사용되는 픽셀 기반 값을 업데이트합니다.Canvas
앱 방향 결정
적응형을 빌드할 때는 물리적 기기 회전에 의존하지 마세요. 기기 대형 화면 기기에서 무시되고 멀티 윈도우 모드의 앱은 기기와 다른 방향을 가질 수 있기 때문입니다. 대신 Configuration.orientation 또는 WindowMetrics를 사용하여 창 크기에 따라 앱이 현재 가로 모드인지 세로 모드인지 확인합니다.
솔루션 1: Configuration.orientation 사용
이 속성은 앱이 현재 표시되는 방향을 식별합니다.
솔루션 2: WindowMetrics#getBounds() 사용
앱의 현재 디스플레이 경계를 가져오고 너비와 높이를 확인하여 방향을 결정할 수 있습니다.
휴대전화 (또는 폴더블의 외부 화면)에서 앱 방향을 제한해야 하지만 대형 화면 기기에서는 제한하지 않으려면 휴대전화에서 앱 방향 제한을 참고하세요.
자세 및 디스플레이 모드
테이블탑 및 HALF_OPENED와 같은 폴더블 자세 및 상태는
세로 모드 폴더블과 가로 모드 폴더블 모두에서 지원됩니다. 하지만 Trifold는 테이블탑 자세를 지원하지 않으며 HALF_OPENED를 사용할 수 없습니다. 대신 Trifold는 완전히 펼쳐졌을 때 고유한 사용자 환경을 위한 더 큰 화면을 제공합니다.
HALF_OPENED를 지원하는 폴더블에서 앱을 차별화하려면 Jetpack
WindowManager API(예: FoldingFeature)를 사용하세요.
다음 개발자 가이드에서 폴더블 자세, 상태, 카메라 미리보기 지원에 관해 자세히 알아보세요.
폴더블은 독특한 시청 환경을 제공합니다. 후면 디스플레이 모드와 듀얼 화면 모드를 사용하면 후면 카메라 셀카 미리보기, 내부 및 외부 화면의 다르지만 동시에 보이는 디스플레이 등 폴더블 기기의 특별한 디스플레이 기능을 빌드할 수 있습니다. 자세한 내용은 다음을 참고하세요.
자연스러운 센서 방향으로 방향 잠그기
매우 구체적인 사용 사례, 특히 기기의 접힌 상태와 관련이 없는 전체 화면을 차지해야 하는 앱의 경우 nosensor 플래그를 사용하면 앱을 기기의 자연스러운 방향으로 잠글 수 있습니다. 예를 들어 Pixel Fold에서 접힌 기기의 자연스러운 방향은 세로 모드이고 펼쳐진 기기의 자연스러운 방향은 가로 모드입니다. nosensor 플래그를 추가하면 외부 디스플레이에서 실행될 때 앱이 세로 모드로 잠기고 내부 디스플레이에서 실행될 때 가로 모드로 잠깁니다.
<activity
android:name=".MainActivity"
android:screenOrientation="nosensor">
게임 및 XR 센서 재매핑
게임 및 XR 앱의 경우 원시 센서 데이터 (예: 자이로스코프 또는 가속도계)는 기기 고정 좌표계에서 제공됩니다. 사용자가 기기를 회전하여 가로 모드로 게임을 플레이하면 센서 축이 화면과 함께 회전하지 않아 게임 컨트롤이 잘못됩니다.
이 문제를 해결하려면 현재 Display.getRotation()을 확인하고 그에 따라 축을 재매핑하세요.
- 회전 0: x=x, y=y
- 회전 90: x=-y, y=x
- 회전 180: x=-x, y=-y
- 회전 270: x=y, y=-x
나침반 또는 XR 앱에 사용되는 회전 벡터의 경우 SensorManager.remapCoordinateSystem()을 사용하여 현재 회전을 기반으로 카메라 렌즈 방향 또는 화면 상단을 새 축에 매핑합니다.
앱 호환성
애플리케이션은 모든 폼 팩터와 연결된 디스플레이에서 호환성을 보장하기 위해 앱 품질 가이드라인을 따라야 합니다. 애플리케이션이 가이드라인을 준수할 수 없는 경우 기기 제조업체는 호환성 처리를 구현할 수 있지만 사용자 환경이 저하될 수 있습니다.
자세한 내용은 플랫폼에서 제공하는 호환성 해결 방법의 포괄적인 목록, 특히 앱 동작을 변경할 수 있는 카메라 미리보기, 재정의, Android 16 API 변경과 관련된 해결 방법을 검토하세요.
적응형 앱 빌드에 관해 자세히 알아보려면 적응형 앱 품질 가이드라인을 참고하세요.