Ograniczanie orientacji aplikacji na telefonach, ale nie na urządzeniach z dużym ekranem

Twoja aplikacja świetnie działa na telefonach w orientacji pionowej, więc ograniczasz ją tylko do tej orientacji. Widzisz jednak możliwość zrobienia więcej na dużych ekranach w orientacji poziomej.

Jak to zrobić, aby ograniczyć aplikację do orientacji pionowej na małych ekranach, ale włączyć orientację poziomą na dużych?

Ten przewodnik jest rozwiązaniem tymczasowym, dopóki nie ulepszysz aplikacji, aby w pełni obsługiwała wszystkie konfiguracje urządzeń.

Wyniki

Aplikacja pozostaje w orientacji pionowej na małych ekranach niezależnie od obrotu urządzenia. Na dużych ekranach aplikacja obsługuje orientację poziomą i pionową.

Zarządzanie orientacją aplikacji

Aby włączyć orientację poziomą na dużych ekranach, ustaw w pliku manifestu aplikacji domyślną obsługę zmian orientacji. Określ rozmiar okna aplikacji w czasie działania. Jeśli okno aplikacji jest małe, ogranicz orientację aplikacji, zastępując ustawienie orientacji w pliku manifestu.

1. Określanie ustawienia orientacji w manifeście aplikacji

Możesz uniknąć deklarowania elementu screenOrientation w pliku manifestu aplikacji (w takim przypadku orientacja domyślnie przyjmuje wartość unspecified) lub ustawić orientację ekranu na fullUser. Jeśli użytkownik nie zablokował obracania opartego na czujnikach, aplikacja będzie obsługiwać wszystkie orientacje urządzenia.

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

Różnica między unspecified a fullUser jest subtelna, ale ważna. Jeśli nie zadeklarujesz wartości screenOrientation, system wybierze orientację, a zasady, których używa do jej określania, mogą się różnić w zależności od urządzenia. Z drugiej strony określenie fullUser bardziej odpowiada zachowaniu zdefiniowanemu przez użytkownika na urządzeniu: jeśli użytkownik zablokował obracanie oparte na czujnikach, aplikacja postępuje zgodnie z jego preferencjami; w przeciwnym razie system zezwala na dowolną z 4 możliwych orientacji ekranu (pionową, poziomą, pionową odwróconą lub poziomą odwróconą). Zobacz screenOrientation.

2. Określanie rozmiaru ekranu

Jeśli plik manifestu obsługuje wszystkie orientacje dozwolone przez użytkownika, możesz programowo określić orientację aplikacji na podstawie rozmiaru ekranu.

Dodaj biblioteki Jetpack WindowManager do pliku build.gradle lub build.gradle.kts modułu:

Kotlin

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Groovy

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

Użyj metody Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics(), aby uzyskać rozmiar ekranu urządzenia jako obiekt WindowMetrics. Wartości wskaźników okna można porównać z klasami rozmiaru okna, aby określić, kiedy należy ograniczyć orientację.

Klasy rozmiarów okien określają punkty podziału między małymi a dużymi ekranami.

Użyj punktów przerwania WindowWidthSizeClass#COMPACTWindowHeightSizeClass#COMPACT, aby określić rozmiar ekranu:

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;
}
    Uwaga:
  • Przykłady są zaimplementowane jako metody działania, więc działanie jest dereferencjonowane jako this w argumencie computeMaximumWindowMetrics().
  • Metoda computeMaximumWindowMetrics() jest używana zamiast computeCurrentWindowMetrics(), ponieważ aplikację można uruchomić w trybie wielu okien, który ignoruje ustawienie orientacji ekranu. Określanie rozmiaru okna aplikacji i zastępowanie ustawienia orientacji nie ma sensu, chyba że okno aplikacji zajmuje cały ekran urządzenia.

Instrukcje deklarowania zależności, aby metoda computeMaximumWindowMetrics() była dostępna w aplikacji, znajdziesz w artykule WindowManager.

3. Zastępowanie ustawienia manifestu aplikacji

Gdy stwierdzisz, że urządzenie ma mały ekran, możesz wywołać Activity#setRequestedOrientation(), aby zastąpić ustawienie screenOrientation w pliku manifestu:

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);
            }
        }
    });
}

Dodając logikę do metod onCreate()View.onConfigurationChanged(), możesz uzyskać dane dotyczące maksymalnego okna i zastąpić ustawienie orientacji, gdy aktywność zostanie zmieniona lub przeniesiona między wyświetlaczami, np. po obróceniu urządzenia lub złożeniu lub rozłożeniu urządzenia składanego. Więcej informacji o tym, kiedy zmiany konfiguracji są wprowadzane i kiedy powodują ponowne tworzenie aktywności, znajdziesz w artykule Obsługa zmian konfiguracji.

Najważniejsze punkty

  • screenOrientation: ustawienie manifestu aplikacji, które umożliwia określenie, jak aplikacja reaguje na zmiany orientacji urządzenia.
  • Jetpack WindowManager: zestaw bibliotek, które umożliwiają określanie rozmiaru i proporcji okna aplikacji; zgodne wstecznie z interfejsem API na poziomie 14.
  • Activity#setRequestedOrientation(): metoda, za pomocą której możesz zmienić orientację aplikacji w czasie działania;

Kolekcje, które zawierają ten przewodnik

Ten przewodnik jest częścią wyselekcjonowanych kolekcji krótkich przewodników, które obejmują szersze cele związane z programowaniem na Androida:

Włącz w aplikacji obsługę zoptymalizowanego środowiska użytkownika na tabletach, urządzeniach składanych i urządzeniach z ChromeOS.

Masz pytania lub chcesz podzielić się opinią?

Odwiedź stronę z najczęstszymi pytaniami, aby zapoznać się z przewodnikami, lub skontaktuj się z nami i podziel się swoimi przemyśleniami.