Android는 별 5개 평점에 준하는 대형 화면용 앱에 필요한 모든 재료를 제공합니다. 이 안내서의 레시피에서는 선택한 재료를 선별하고 결합하여 특정 개발 문제를 해결하는 방법을 제시합니다. 각 레시피에는 권장사항, 양질의 코드 샘플, 단계별 안내가 포함되어 있어 대형 화면의 최고 실력자가 되는 데 활용할 수 있습니다.
별표 평점
레시피는 대형 화면 앱 품질 가이드라인을 얼마나 잘 준수하는지에 따라 별표로 평점을 표시합니다.
등급 1, 대형 화면 차별화 기준을 충족합니다. | |
등급 2, 대형 화면 최적화 기준을 충족합니다. | |
등급 3, 대형 화면 준비 완료 기준을 충족합니다. | |
대형 화면 기능을 일부 지원하지만, 대형 화면 앱 품질 가이드라인의 일부를 충족하지 않습니다. | |
특정 사용 사례의 요구사항을 충족하지만, 대형 화면을 제대로 지원하지 않습니다. |
Chromebook 카메라 지원
Google Play에서 Chromebook 사용자의 관심을 사로잡으세요.
카메라 앱이 기본 카메라 기능으로만 작동할 수 있는 경우에도, 고급형 휴대전화에서 사용되는 고급 카메라 기능을 실수로 지정하는 바람에 앱 스토어에서 Chromebook 사용자가 앱을 설치하지 못하게 되어서는 안 됩니다.
Chromebook에는 화상 회의, 스냅샷, 기타 애플리케이션에서 사용하기에 적합한 전면(안쪽) 카메라가 내장되어 있습니다. 하지만 모든 Chromebook에 후면(바깥쪽) 카메라가 있는 것은 아니며 Chromebook의 전면에 위치한 대부분의 카메라는 자동 초점이나 플래시 기능을 지원하지 않습니다.
권장사항
다목적으로 사용할 수 있는 카메라 앱은 카메라 구성에 관계없이 전면 카메라, 후면 카메라, USB로 연결된 외장 카메라 등 모든 기기를 지원합니다.
앱 스토어에서 최대한 많은 기기에 앱을 제공할 수 있도록 하려면 앱에서 사용하는 모든 카메라 기능을 항상 선언하고 특정 기능이 필요한지 여부를 명시해야 합니다.
재료
CAMERA
권한: 앱이 기기의 카메라에 액세스하도록 허용합니다.<uses-feature>
매니페스트 요소: 앱 스토어에 앱에서 어떤 기능을 사용하는지 알립니다.required
속성: 앱 스토어에 특정 기능이 없어도 앱 작동이 가능한지 여부를 알립니다.
단계
요약
CAMERA
권한을 선언합니다. 기본적인 카메라 지원을 제공하는 카메라 기능을 선언합니다. 각 기능이 필요한지 여부를 명시합니다.
1. CAMERA
권한 선언
앱 매니페스트에 다음 권한을 추가합니다.
<uses-permission android:name="android.permission.CAMERA" />
2. 기본 카메라 기능 선언
앱 매니페스트에 다음 기능을 추가합니다.
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. 각 기능이 필요한지 여부 지정
내장 카메라 또는 외장 카메라가 있는 기기나 카메라가 없는 기기에서 앱에 액세스할 수 있도록 android.hardware.camera.any
기능에 android:required="false"
를 설정합니다.
다른 기능의 경우 android:required="false"
를 설정하여 후면 카메라, 자동 초점, 플래시가 없는 Chromebook과 같은 기기에서도 앱 스토어의 앱에 액세스할 수 있도록 합니다.
결과
Chromebook 사용자가 Google Play 및 다른 앱 스토어에서 앱을 다운로드하고 설치할 수 있습니다. 휴대전화와 같이 모든 기능을 제공하는 카메라를 지원하는 기기는 카메라 기능에서 제한을 받지 않습니다.
앱에서 지원하는 카메라 기능을 명시적으로 설정하고 앱에 필요한 기능을 명시하면 최대한 많은 기기에 앱을 제공할 수 있습니다.
추가 리소스
자세한 내용은 <uses-feature>
문서의 카메라 하드웨어 기능을 참고하세요.
휴대전화에서는 앱 방향이 제한되지만 대형 화면 기기에서는 제한되지 않음
앱이 세로 모드의 휴대전화에서 잘 작동하므로 앱을 세로 모드로만 사용하도록 제한했습니다. 하지만 대형 화면의 가로 모드 방향으로 더 많은 작업을 할 수 있는 경우가 있습니다.
작은 화면에서는 세로 모드 방향으로 앱을 제한하고 대형 화면에서는 가로 모드를 사용할 수 있도록 두 가지 방법을 모두 제공하려면 어떻게 해야 할까요?
권장사항
우수한 앱은 기기 방향과 같은 사용자 선호도를 중요하게 생각합니다.
대형 화면 앱 품질 가이드라인에서는 앱이 세로 모드 방향, 가로 모드 방향, 멀티 윈도우 모드, 폴더블 기기의 접힌 상태와 펼친 상태를 비롯하여 모든 기기 설정을 지원할 것을 권장합니다. 앱은 다양한 구성에 맞게 레이아웃과 사용자 인터페이스를 최적화해야 하며, 구성을 변경하는 중에 상태를 저장하고 복원해야 합니다.
이 레시피는 대형 화면을 지원하기 위한 임시 조치입니다. 모든 기기 구성을 완벽하게 지원하도록 앱을 개선할 수 있게 되기까지 이 레시피를 사용하세요.
재료
screenOrientation
: 앱이 기기 방향 변경에 응답하는 방식을 지정할 수 있는 앱 매니페스트 설정- Jetpack WindowManager: 앱 창의 크기와 가로세로 비율을 결정할 수 있는 라이브러리 집합입니다. API 수준 14로의 하위 호환성
Activity#setRequestedOrientation()
: 런타임 시 앱 방향을 변경할 수 있게 해주는 메서드
단계
요약
기본적으로 앱이 방향 변경을 처리하도록 앱 매니페스트에 사용 설정합니다. 런타임 시 앱 창 크기를 결정합니다. 앱 창이 작은 경우 매니페스트 방향 설정을 재정의하여 앱 방향을 제한합니다.
1. 앱 매니페스트에 방향 설정 지정
앱 매니페스트의 screenOrientation
요소를 선언하지 않거나(이 경우 방향 기본값이 unspecified
가 됨) 화면 방향을 fullUser
로 설정할 수 있습니다. 사용자가 센서 기반 회전을 잠그지 않은 경우 앱은 모든 기기 방향을 지원합니다.
<activity
android:name=".MyActivity"
android:screenOrientation="fullUser">
unspecified
와 fullUser
의 차이점은 미묘하지만 중요합니다. screenOrientation
값을 선언하지 않으면 시스템이 방향을 선택하며, 시스템에서 방향을 정의하는 데 사용하는 정책은 기기마다 다를 수 있습니다. 반면에 fullUser
를 지정하면 사용자가 기기에 정의한 동작에 더 가까워집니다. 사용자가 센서 기반 회전을 잠근 경우에는 앱이 사용자 환경설정을 따르고, 그러지 않은 경우에는 시스템이 네 가지 화면 방향(세로, 가로, 세로 반전, 가로 반전)을 허용합니다. android:screenOrientation
을 참고하세요.
2. 화면 크기 확인
사용자가 허용하는 모든 방향을 지원하도록 매니페스트를 설정하면 화면 크기에 따라 프로그래매틱 방식으로 앱 방향을 지정할 수 있습니다.
Jetpack WindowManager 라이브러리를 모듈의 build.gradle
또는 build.gradle.kts
파일에 추가합니다.
Kotlin
implementation("androidx.window:window:version
") implementation("androidx.window:window-core:version
")
Groovy
implementation 'androidx.window:window:version
' implementation 'androidx.window:window-core:version
'
Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics()
메서드를 사용하여 기기 화면 크기를 WindowMetrics
객체로 가져옵니다. 창 측정항목을 창 크기 클래스와 비교하여 방향을 제한할 시점을 결정할 수 있습니다.
창 크기 클래스는 작은 화면과 큰 화면 사이에 중단점을 제공합니다.
WindowWidthSizeClass#COMPACT
및 WindowHeightSizeClass#COMPACT
중단점을 사용하여 화면 크기를 결정합니다.
Kotlin
/** Determines whether the device has a compact screen. **/ fun compactScreen() : Boolean { val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT }
Java
/** Determines whether the device has a compact screen. **/ private boolean compactScreen() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this); int width = metrics.getBounds().width(); int height = metrics.getBounds().height(); float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density); return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT || windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT; }
- 참고:
- 위 예는 활동의 메서드로 구현되었습니다. 따라서 활동은
computeMaximumWindowMetrics()
의 인수에서this
로 역참조됩니다. - 앱이 멀티 윈도우 모드에서 실행될 수 있으므로
computeMaximumWindowMetrics()
가computeCurrentWindowMetrics()
대신 사용됩니다. 멀티 윈도우 모드에서는 화면 방향 설정을 무시되기 때문입니다. 앱 창이 기기 전체 화면을 차지하지 않는다면 앱 창 크기를 결정하고 방향 설정을 재정의하는 것은 의미가 없습니다.
앱에서 computeMaximumWindowMetrics()
메서드를 사용할 수 있도록 종속 항목을 선언하는 방법은 WindowManager를 참고하세요.
3. 앱 매니페스트 설정 재정의
기기의 화면이 작다는 것을 확인했다면 Activity#setRequestedOrientation()
을 호출하여 매니페스트의 screenOrientation
설정을 재정의할 수 있습니다.
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. val container: ViewGroup = binding.container // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER } }) }
Java
@Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstanceState); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. ViewGroup container = binding.container; // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } } }); }
onCreate()
및 View.onConfigurationChanged()
메서드에 로직을 추가하면 활동이 디스플레이 간에 크기가 조절되거나 이동할 때마다(예: 기기 회전 후 또는 폴더블 기기가 접히거나 펼쳐질 때) 최대 창 측정항목을 가져오고 방향 설정을 재정의할 수 있습니다.
구성 변경이 발생하는 시점과 이러한 변경사항이 활동 재생성을 유발하는 시점에 관한 자세한 내용은 구성 변경사항 처리를 참고하세요.
결과
이제 앱이 작은 화면에서 기기 회전과 관계없이 세로 모드 방향으로 유지될 것입니다. 대형 화면에서는 앱이 가로 모드 방향과 세로 모드 방향을 지원합니다.
추가 리소스
항상 모든 기기 설정을 지원하도록 앱을 업그레이드하는 방법은 다음을 참고하세요.
외부 키보드 스페이스바를 사용하여 미디어 재생 일시중지 및 다시 시작
대형 화면 최적화에는 스페이스바를 누르는 동작에 반응하여 동영상 및 기타 미디어의 재생을 일시중지하거나 재개하는 등의 외부 키보드 입력을 처리하는 기능이 포함됩니다. 이는 주로 외부 키보드에 연결되는 태블릿과 일반적으로 외부 키보드와 함께 제공되지만 태블릿 모드로 사용할 수 있는 Chromebook에 유용합니다.
미디어가 창의 유일한 요소인 경우(예: 전체 화면 동영상 재생) 활동 수준 또는 Jetpack Compose에서는 화면 수준에서 키 누름 이벤트에 응답합니다.
권장사항
앱에서 미디어 파일을 재생할 때마다 사용자는 실제 키보드에서 스페이스바를 눌러 재생을 일시중지하거나 재개할 수 있어야 합니다.
재료
KEYCODE_SPACE
: 스페이스바의 키 코드 상수입니다.
Compose
onPreviewKeyEvent
: 구성요소(또는 그 하위 요소 중 하나)에 포커스가 있을 때 구성요소에서 하드웨어 키 이벤트를 가로채도록 하는Modifier
입니다.onKeyEvent
:onPreviewKeyEvent
와 마찬가지로 이Modifier
를 사용하면 구성요소(또는 그 하위 요소 중 하나)에 포커스가 있을 때 구성요소에서 하드웨어 키 이벤트를 가로챌 수 있습니다.
뷰
onKeyUp()
: 키가 해제되고 활동 내의 뷰에서 처리하지 않을 때 호출됩니다.
단계
요약
뷰 기반 앱과 Jetpack Compose 기반의 앱은 키보드 키 누름에 비슷한 방식으로 응답합니다. 앱은 키 누름 이벤트를 수신 대기하고, 이벤트를 필터링하고, 스페이스바 키 누름과 같은 선택된 키 누름에 응답해야 합니다.
1. 키보드 이벤트 수신 대기
뷰
앱의 활동에서 onKeyUp()
메서드를 다음과 같이 재정의합니다.
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { ... }
Java
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { ... }
이 메서드는 눌린 키가 해제될 때마다 호출되므로 키 입력마다 정확하게 한 번씩 실행됩니다.
Compose
Jetpack Compose를 사용하면 화면 내에서 키 입력을 관리하는 onPreviewKeyEvent
또는 onKeyEvent
수정자를 활용할 수 있습니다.
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
또는
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
2. 스페이스바 누르기 필터링
onKeyUp()
메서드 또는 Compose onPreviewKeyEvent
및 onKeyEvent
수정자 메서드 내에서 KeyEvent.KEYCODE_SPACE
를 필터링하여 올바른 이벤트를 미디어 구성요소에 전송합니다.
뷰
Kotlin
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback() return true } return false
Java
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback(); return true; } return false;
Compose
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
또는
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
결과
이제 앱이 스페이스바 키를 누를 때 이에 응답하여 동영상이나 다른 미디어를 일시중지하거나 재개할 수 있습니다.
추가 리소스
키보드 이벤트 및 관리 방법에 관한 자세한 내용은 키보드 입력 처리를 참고하세요.
스타일러스 손바닥 움직임 무시
스타일러스는 대형 화면에서 생산성과 창의성이 뛰어난 도구가 될 수 있습니다. 하지만 사용자는 스타일러스를 사용하여 앱에서 그리거나 쓰거나 상호작용할 때 손바닥으로 화면을 터치하기도 합니다. 시스템이 이러한 이벤트를 의도치 않은 손바닥 터치로 인식하여 무시하기 전에 터치 이벤트가 앱에 보고될 수 있습니다.
권장사항
앱은 관련 없는 터치 이벤트를 식별하여 무시해야 합니다. Android는 MotionEvent
객체를 전달하여 손바닥 터치를 취소합니다. ACTION_CANCEL
또는 ACTION_POINTER_UP
및 FLAG_CANCELED
객체를 확인하여 손바닥 터치로 인한 동작을 무시할지 결정합니다.
재료
MotionEvent
: 터치 및 이동 이벤트를 나타냅니다. 이벤트를 무시할지 결정하는 데 필요한 정보가 포함되어 있습니다.OnTouchListener#onTouch()
:MotionEvent
객체를 수신합니다.MotionEvent#getActionMasked()
: 모션 이벤트와 관련된 작업을 반환합니다.ACTION_CANCEL
: 동작이 실행취소되어야 함을 나타내는MotionEvent
상수입니다.ACTION_POINTER_UP
: 첫 번째 포인터 이외의 포인터가 소실되었음을, 즉 기기 화면과의 접촉을 포기했음을 나타내는MotionEvent
상수입니다.FLAG_CANCELED
: 포인터가 소실되어 의도치 않은 터치 이벤트가 발생했음을 나타내는MotionEvent
상수입니다. Android 13(API 수준 33) 및 이후 버전의ACTION_POINTER_UP
과ACTION_CANCEL
이벤트에 추가되었습니다.
단계
요약
앱에 전달된 MotionEvent
객체를 검사합니다. MotionEvent
API를 사용하여 이벤트 특성을 판단합니다.
- 단일 포인터 이벤트:
ACTION_CANCEL
을 확인합니다. Android 13 이상에서는FLAG_CANCELED
도 확인합니다. - 멀티 포인터 이벤트: Android 13 이상에서는
ACTION_POINTER_UP
및FLAG_CANCELED
를 확인합니다.
ACTION_CANCEL
및 ACTION_POINTER_UP
/FLAG_CANCELED
이벤트에 응답합니다.
1. 모션 이벤트 객체 획득
앱에 OnTouchListener
를 추가합니다.
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> // Process motion event. } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { // Process motion event. });
2. 이벤트 작업 및 플래그 확인
모든 API 수준에서 단일 포인터 이벤트를 나타내는 ACTION_CANCEL
을 확인합니다. Android 13 이상에서는 ACTION_POINTER_UP
에서 FLAG_CANCELED.
를 확인합니다.
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> when (event.actionMasked) { MotionEvent.ACTION_CANCEL -> { //Process canceled single-pointer motion event for all SDK versions. } MotionEvent.ACTION_POINTER_UP -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } } true } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { switch (event.getActionMasked()) { case MotionEvent.ACTION_CANCEL: // Process canceled single-pointer motion event for all SDK versions. case MotionEvent.ACTION_UP: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } return true; });
3. 동작 실행취소
손바닥 터치를 식별한 후에는 동작이 화면에 미치는 효과를 실행취소할 수 있습니다.
손바닥 터치와 같은 의도하지 않은 입력이 실행취소될 수 있도록 앱에서는 사용자 작업 기록을 유지해야 합니다. 예를 보려면 Android 앱에서 스타일러스 지원 개선 Codelab의 기본 그리기 앱 구현을 참고하세요.
결과
이제 앱은 Android 13 이상 API 수준의 멀티 포인터 이벤트와 모든 API 수준의 단일 포인터 이벤트에서 손바닥 터치를 식별하고 무시할 수 있습니다.
추가 리소스
자세한 내용은 다음을 참고하세요.
- Android 13 기능 및 API: 손바닥 움직임 무시 개선
- 개발자 가이드
- Codelab: Android 앱에서 스타일러스 지원 개선
WebView 상태 관리
WebView
는 일반적으로 사용되는 구성요소로, 상태 관리를 위한 고급 시스템을 제공합니다. WebView
는 구성 변경 시 상태와 스크롤 위치를 유지해야 합니다. WebView
는 사용자가 기기를 회전하거나 폴더블 휴대전화를 펼칠 때 스크롤하던 위치를 잃을 수 있습니다. 이런 경우 사용자가 WebView
의 상단에서 이전 스크롤 위치로 다시 스크롤해야 합니다.
권장사항
WebView
가 다시 생성되는 횟수를 최소화합니다. WebView
는 상태를 관리하는 데 능숙하며 가능한 한 많은 구성 변경을 관리하여야 이 기능의 장점을 활용할 수 있습니다. Activity
재생성(시스템에서 구성 변경을 처리하는 방식) 시 WebView
도 다시 생성하므로 WebView
에서 상태가 손실될 수 있기 때문에 앱이 구성 변경을 처리해야 합니다.
재료
android:configChanges
: 매니페스트<activity>
요소의 속성입니다. 활동이 처리하는 구성 변경을 나열합니다.View#invalidate()
: 뷰를 다시 그리는 메서드입니다.WebView
에서 상속됩니다.
단계
요약
WebView
상태를 저장하려면 Activity
재생성을 최대한 방지하고 그런 다음 상태를 유지하면서 크기를 조절할 수 있도록 WebView
를 무효화합니다.
1. 앱의 AndroidManifest.xml
파일에 구성 변경사항 추가
시스템이 아닌 앱에서 처리하는 구성 변경을 지정하여 활동 재생성을 방지합니다.
<activity
android:name=".MyActivity"
android:configChanges="screenLayout|orientation|screenSize
|keyboard|keyboardHidden|smallestScreenSize" />
2. 앱이 구성 변경을 수신할 때마다 WebView
를 무효화
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) webView.invalidate() }
Java
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); webview.invalidate(); }
이 단계는 Jetpack Compose가 Composable
요소의 크기를 올바르게 조절하기 위해 어떤 것을 무효화할 필요가 없으므로 뷰 시스템에만 적용됩니다. 그러나 WebView
를 올바르게 관리하지 않으면 Compose에서 다시 만들기도 합니다. Accompanist WebView 래퍼를 사용하여 Compose 앱에 WebView
상태를 저장하고 복원합니다.
결과
이제 앱의 WebView
구성요소가 크기 조절, 방향 변경, 접기 및 펼치기 등 여러 구성 변경사항에 대해 상태 및 스크롤 위치를 유지합니다.
추가 리소스
구성 변경사항 및 관리 방법에 대한 자세한 내용은 구성 변경 처리를 참고하세요.
RecyclerView 상태 관리
RecyclerView
는 최소한의 그래픽 리소스를 사용하여 대량의 데이터를 표시할 수 있습니다. RecyclerView
가 항목 목록을 스크롤하면 RecyclerView
는 화면에서 스크롤된 항목의 View
인스턴스를 재사용하여 화면을 스크롤할 때 새 항목을 만듭니다. 그러나 기기 회전과 같은 구성 변경으로 인해 RecyclerView
의 상태가 재설정되어 사용자가 RecyclerView
항목 목록에서 이전 위치로 다시 스크롤해야 할 수 있습니다.
권장사항
RecyclerView
는 모든 구성 변경사항 중에 상태(특히 스크롤 위치)와 목록 요소의 상태를 유지해야 합니다.
재료
RecyclerView.Adapter#setStateRestorationPolicy()
: 구성 변경 후RecyclerView.Adapter
가 상태를 복원하는 방법을 지정합니다.ViewModel
: 활동 또는 프래그먼트의 상태를 유지합니다.
단계
요약
RecyclerView.Adapter
의 상태 복원 정책을 설정하여 RecyclerView
스크롤 위치를 저장합니다. RecyclerView
목록 항목의 상태를 저장합니다. 목록 항목의 상태를 RecyclerView
어댑터에 추가하고 목록 항목이 ViewHolder
에 바인딩되었을 때 목록 항목의 상태를 복원합니다.
1. Adapter
상태 복원 정책 사용 설정
구성 변경 후에도 RecyclerView
의 스크롤 위치가 유지되도록 RecyclerView
어댑터의 상태 복원 정책을 사용 설정합니다. 어댑터 생성자에 정책 사양을 추가합니다.
Kotlin
class MyAdapter() : RecyclerView.Adapter() { init { stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY } ... }
Java
class MyAdapter extends RecyclerView.Adapter{ public Adapter() { setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY); } ... }
2. 스테이트풀(Stateful) 목록 항목 상태 저장
EditText
요소가 포함된 항목과 같이 복잡한 RecyclerView
목록 항목의 상태를 저장합니다. 예를 들어 EditText
의 상태를 저장하려면 onClick
핸들러와 유사한 콜백을 추가하여 텍스트 변경사항을 캡처합니다. 다음과 같이 콜백 내에서 저장할 데이터를 정의합니다.
Kotlin
input.addTextChangedListener( afterTextChanged = { text -> text?.let { // Save state here. } } )
Java
input.addTextChangedListener(new TextWatcher() { ... @Override public void afterTextChanged(Editable s) { // Save state here. } });
Activity
또는 Fragment
에서 콜백을 선언합니다. ViewModel
을 사용하여 상태를 저장합니다.
3. Adapter
에 목록 항목 상태 추가
RecyclerView.Adapter
에 목록 항목의 상태를 추가합니다. 호스트 Activity
또는 Fragment
가 생성되면 항목 상태를 어댑터 생성자에 전달합니다.
Kotlin
val adapter = MyAdapter(items, viewModel.retrieveState())
Java
MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());
4. 어댑터의 ViewHolder
에서 목록 항목 상태 복구
ViewHolder
를 항목에 바인딩하면 RecyclerView.Adapter
에서 항목의 상태를 복원합니다.
Kotlin
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { ... val item = items[position] val state = states.firstOrNull { it.item == item } if (state != null) { holder.restore(state) } }
Java
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ... Item item = items[position]; Arrays.stream(states).filter(state -> state.item == item) .findFirst() .ifPresent(state -> holder.restore(state)); }
결과
이제 RecyclerView
에서 스크롤 위치 및 RecyclerView
목록에 있는 모든 항목의 상태를 복원할 수 있습니다.
추가 리소스
분리형 키보드 관리
분리형 키보드가 지원되어
화면 기기 Android는 키보드가 조작될 때마다 구성 변경을
분리되어 UI 상태가 손실될 수 있습니다. 내
앱은 상태를 저장 및 복원할 수 있으며, 이를 통해 시스템은
활동 재생성을 사용하거나 키보드 구성 변경에 관해 활동 재생성을 제한합니다.
모든 경우에 키보드와 관련된 모든 데이터는
Configuration
객체 keyboard
및
구성 객체의 keyboardHidden
필드에는 유형 관련 정보가 포함됩니다.
키보드의 가용성에 관한 것입니다.
권장사항
대형 화면에 최적화된 앱은 스타일러스, 마우스, 트랙패드, 기타 주변기기에 소프트웨어 및 하드웨어 키보드 연결 기기에서 사용할 수 있습니다.
외부 키보드 지원에는 구성 변경이 수반됩니다. 이 변경은 두 가지 방법 중 하나로 관리할 수 있습니다.
- 시스템이 현재 실행 중인 활동을 재생성하도록 하면 개발자가 앱 상태를 관리합니다.
- 구성 변경을 직접 관리합니다 (활동이 다시 생성되지 않음).
<ph type="x-smartling-placeholder">
- </ph>
- 모든 키보드 관련 구성 값 선언
- 구성 변경 핸들러 만들기
텍스트 입력과 작성을 위해 UI를 세밀하게 제어해야 하는 생산성 앱 도움이 될 수 있으므로 이러한 질문에 대한 구성을 변경할 수 있습니다
특정 경우에 하드웨어가 실행될 때 앱 레이아웃을 변경해야 할 수도 있습니다. 도구나 소프트웨어를 설치할 공간을 더 많이 확보하기 위해 수정 창
구성 변경을 수신 대기하는 신뢰할 수 있는 유일한 방법은
뷰의 onConfigurationChanged()
메서드를 통해 새 뷰를 추가할 수 있습니다.
인스턴스를 추가하고 뷰의 onConfigurationChanged()
에서 응답합니다.
연결된 키보드 또는 키보드로 인한 구성 변경에 대한 핸들러
분리됩니다.
재료
android:configChanges
: 앱 매니페스트의<activity>
요소의 속성입니다. 앱이 관리하는 구성 변경을 시스템에 알립니다.View#onConfigurationChanged()
: 새 앱 구성이 포함됩니다
단계
요약
configChanges
속성을 선언하고 키보드 관련 값을 추가합니다. 여기에
View
를 활동의 뷰 계층 구조에 연결하고 구성 변경을 수신 대기합니다.
1. configChanges
속성 선언
이미 관리되는 구성 변경사항 목록에 keyboard|keyboardHidden
값을 추가하여 앱 매니페스트에서 <activity>
요소를 업데이트합니다.
<activity
…
android:configChanges="...|keyboard|keyboardHidden">
2. 뷰 계층 구조에 빈 뷰 추가
새 뷰를 선언하고 뷰의 onConfigurationChanged()
메서드 내에 핸들러 코드를 추가합니다.
Kotlin
val v = object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) // Handler code here. } }
자바
View v = new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Handler code here. } };
결과
이제 앱이 현재 실행 중인 활동을 다시 생성하지 않고 연결되거나 분리된 외부 키보드에 응답합니다.
추가 리소스
키보드 연결 또는 분리와 같은 구성 변경 중에 앱의 UI 상태를 저장하는 방법을 알아보려면 UI 상태 저장을 참고하세요.
추천 서비스
- 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- 구성 변경 처리