Restringir a orientação do app em smartphones, mas não em dispositivos de tela grande

Como o app funciona muito bem em smartphones na orientação retrato, você o restringiu apenas a esse modo. Mas você percebe uma oportunidade de melhorar o suporte do app em telas grandes na orientação paisagem.

Como você pode aproveitar o melhor de dois mundos, restringindo o app à orientação retrato em telas pequenas, mas ativando o modo paisagem em telas grandes?

Este guia é uma medida temporária até que você melhore o app para oferecer suporte total a todas as configurações de dispositivos.

Gerenciar a orientação do app

Para ativar a orientação paisagem em telas grandes, defina o manifesto do app para processar mudanças de orientação por padrão. Durante a execução, determine o tamanho da janela do app. Se a janela do app for pequena, restrinja a orientação do app substituindo a configuração de orientação do manifesto.

1. Especificar a configuração de orientação no manifesto do app

Você pode evitar declarar o elemento screenOrientation do manifesto do app, o que define a orientação padrão como unspecified, ou pode definir a orientação da tela como fullUser. Se o usuário não tiver bloqueado a rotação com base no sensor, o app vai oferecer suporte a todas as orientações do dispositivo.

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

A diferença entre unspecified e fullUser é sutil, mas importante. Se você não declarar um valor screenOrientation, o sistema vai escolher a orientação, e a política usada para defini-la poderá variar de acordo com o dispositivo. Por outro lado, especificar fullUser corresponde mais de perto ao comportamento definido pelo usuário para o dispositivo: se o usuário bloqueou a rotação com base em sensor, o app segue a preferência do usuário. Caso contrário, o sistema permite qualquer uma das quatro orientações de tela possíveis (retrato, paisagem, retrato invertido ou paisagem invertida). Consulte screenOrientation.

2. Determinar o tamanho da tela

Com o manifesto definido para oferecer suporte a todas as orientações permitidas pelo usuário, você pode especificar a orientação do app de forma programática com base no tamanho da tela.

Adicione as bibliotecas Jetpack WindowManager ao arquivo build.gradle ou build.gradle.kts do módulo:

Kotlin

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

Groovy

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

Use o método WindowMetricsCalculator#computeMaximumWindowMetrics() da Jetpack WindowManager para conferir o tamanho da tela do dispositivo como um objeto WindowMetrics. As métricas da janela podem ser comparadas a classes de tamanho de janela para decidir quando restringir a orientação.

As classes de tamanho de janelas oferecem os pontos de interrupção entre telas pequenas e grandes.

Use os pontos de interrupção WindowWidthSizeClass#COMPACT e WindowHeightSizeClass#COMPACT para determinar o tamanho da tela:

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;
}
    Observação:
  • Os exemplos são implementados como métodos de uma atividade. A atividade é desreferenciada como this no argumento de computeMaximumWindowMetrics().
  • O método computeMaximumWindowMetrics() é usado em vez de computeCurrentWindowMetrics() porque o app pode ser iniciado no modo de várias janelas, que ignora a configuração de orientação da tela. Não há sentido em determinar o tamanho da janela do app e substituir a configuração de orientação, a menos que a janela do app seja a tela inteira do dispositivo.

Consulte WindowManager para instruções sobre como declarar dependências e disponibilizar o método computeMaximumWindowMetrics() no app.

3. Substituir a configuração do manifesto do app

Se você determinar que o dispositivo tem um tamanho de tela compacto, chame Activity#setRequestedOrientation() para substituir a configuração screenOrientation do manifesto:

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

Ao adicionar a lógica aos métodos onCreate() e View.onConfigurationChanged(), você poderá acessar as métricas máximas da janela e substituir a configuração de orientação sempre que a atividade for redimensionada ou movida entre as telas, por exemplo, após uma rotação do dispositivo ou quando um dispositivo dobrável for dobrado ou desdobrado. Para mais informações sobre quando as mudanças de configuração ocorrem e quando elas causam a recriação de atividades, consulte Gerenciar mudanças de configuração.

Pontos principais

  • screenOrientation: configuração do manifesto do app que permite especificar como o app responde às mudanças de orientação do dispositivo.
  • Jetpack WindowManager: conjunto de bibliotecas que permitem determinar o tamanho e a proporção da janela do app. Ele é compatível com o nível 14 da API e versões mais recentes.
  • Activity#setRequestedOrientation(): método com que é possível mudar a orientação do app no momento da execução.

Resultados

Agora, o app vai permanecer na orientação retrato em telas pequenas, independente da rotação do dispositivo. Em telas grandes, o app precisa oferecer suporte às orientações de paisagem e retrato.

Coleções que contêm este guia

Este guia faz parte destas coleções selecionadas de guias rápidos que abrangem objetivos mais amplos de desenvolvimento para Android:

Permita que seu app ofereça suporte a uma experiência do usuário otimizada em tablets, dispositivos dobráveis e ChromeOS.

Tem dúvidas ou feedback?

Acesse a página de perguntas frequentes e saiba mais sobre os guias rápidos ou entre em contato e conte o que você pensa.