휴대전화에서 앱 방향을 제한하고 대형 화면 기기에서는 제한하지 않기

앱이 세로 모드의 휴대전화에서 잘 작동하므로 앱을 세로 모드로만 사용하도록 제한했습니다. 하지만 대형 화면의 가로 모드 방향으로 더 많은 작업을 할 수 있는 경우가 있습니다.

작은 화면에서는 세로 모드 방향으로 앱을 제한하고 대형 화면에서는 가로 모드를 사용할 수 있도록 두 가지 방법을 모두 제공하려면 어떻게 해야 할까요?

이 가이드는 모든 기기 구성을 완벽하게 지원하도록 앱을 개선할 수 있을 때까지의 임시 조치입니다.

앱 방향 관리

대형 화면에서 가로 모드 방향을 사용 설정하려면 기본적으로 방향 변경을 처리하도록 앱 매니페스트를 설정합니다. 런타임 시 앱 창 크기를 결정합니다. 앱 창이 작은 경우 매니페스트 방향 설정을 재정의하여 앱 방향을 제한합니다.

1. 앱 매니페스트에 방향 설정 지정

앱 매니페스트의 screenOrientation 요소를 선언하지 않거나 (이 경우 방향 기본값이 unspecified가 됨) 화면 방향을 fullUser로 설정할 수 있습니다. 사용자가 센서 기반 회전을 잠그지 않은 경우 앱은 모든 기기 방향을 지원합니다.

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

unspecifiedfullUser의 차이점은 미묘하지만 중요합니다. screenOrientation 값을 선언하지 않으면 시스템이 방향을 선택하며, 시스템에서 방향을 정의하는 데 사용하는 정책은 기기마다 다를 수 있습니다. 반면에 fullUser를 지정하면 사용자가 기기에 정의한 동작에 더 근접합니다. 사용자가 센서 기반 회전을 잠근 경우 앱은 사용자 환경설정을 따르고, 그렇지 않은 경우 시스템은 가능한 네 가지 화면 방향 (세로, 가로, 세로 반전, 가로 반전) 중 하나를 허용합니다. screenOrientation을 참고하세요.

2. 화면 크기 확인

사용자가 허용하는 모든 방향을 지원하도록 매니페스트를 설정하면 화면 크기에 따라 프로그래매틱 방식으로 앱 방향을 지정할 수 있습니다.

모듈의 build.gradle 또는 build.gradle.kts 파일에 Jetpack WindowManager 라이브러리를 추가합니다.

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#COMPACTWindowHeightSizeClass#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() 메서드에 로직을 추가하면 활동이 디스플레이 간에 크기가 조절되거나 이동할 때마다(예: 기기 회전 후 또는 폴더블 기기가 접히거나 펼쳐질 때) 최대 창 측정항목을 가져오고 방향 설정을 재정의할 수 있습니다. 구성 변경이 발생하는 시점과 이러한 변경사항이 활동 재생성을 유발하는 시점에 관한 자세한 내용은 구성 변경 처리를 참고하세요.

핵심사항

  • screenOrientation: 앱이 기기 방향 변경에 응답하는 방식을 지정할 수 있는 앱 매니페스트 설정
  • Jetpack WindowManager: 앱 창의 크기와 가로세로 비율을 결정할 수 있는 라이브러리 모음으로 API 수준 14와 하위 호환됨
  • Activity#setRequestedOrientation(): 런타임 시 앱 방향을 변경할 수 있게 해주는 메서드

결과

이제 앱이 작은 화면에서 기기 회전과 관계없이 세로 모드 방향으로 유지될 것입니다. 대형 화면에서는 앱이 가로 모드 방향과 세로 모드 방향을 지원합니다.

이 가이드가 포함된 컬렉션

이 가이드는 광범위한 Android 개발 목표를 다루는 선별된 빠른 가이드 모음의 일부입니다.

앱이 태블릿, 폴더블, ChromeOS 기기에서 최적화된 사용자 환경을 지원하도록 설정합니다.

질문이나 의견이 있으신가요?

자주 묻는 질문(FAQ) 페이지로 이동하여 빠른 가이드를 알아보거나 문의하여 의견을 보내주세요.