Suporte a tamanhos de tela diferentes

O suporte a diferentes tamanhos de tela permite que o app alcance o maior número de usuários e a maior variedade de dispositivos possível.

Para oferecer suporte ao máximo possível de tamanhos de tela, crie layouts de app responsivos e adaptáveis. Os layouts responsivos/adaptáveis proporcionam uma experiência do usuário otimizada, independente do tamanho da tela. Isso permite que o app acomode smartphones, tablets, dispositivos dobráveis e Chrome OS, orientações de retrato e paisagem e configurações redimensionáveis como o modo de várias janelas.

Classes de tamanho de janela

Classes de tamanho de janela são um conjunto de pontos de interrupção específicos de janela de visualização que ajudam você a projetar, desenvolver e testar layouts de aplicativos responsivos e adaptáveis. Os pontos de interrupção foram escolhidos especificamente a fim de equilibrar a simplicidade do layout com a flexibilidade de otimizar o app para casos únicos.

As classes de tamanho da janela categorizam a área de exibição disponível ao app como compacta, média ou expandida. A largura e a altura disponíveis são classificadas separadamente. A todo momento, o app tem duas classes de tamanho de janela: uma para largura e outra para altura. A largura disponível geralmente é mais importante do que a altura devido à ubiquidade da rolagem vertical. Sendo assim, a classe de tamanho da largura da janela provavelmente vai ser mais relevante para a IU do app.

Figura 1. Representações de classes de tamanho de janela baseadas em largura.
Figura 2. Representações de classes de tamanho de janela baseadas em altura.

Conforme mostrado acima, esses pontos de interrupção permitem que você continue pensando nos layouts em termos de dispositivos e configurações. Cada ponto de interrupção de classe de tamanho representa um caso majoritário para cenários de dispositivos típicos e podem ser uma referência útil enquanto você pensa sobre o design dos seus layouts baseados em pontos de interrupção.

Classe de tamanho Ponto de interrupção Representação do dispositivo
Largura compacta < 600 dp 99,96% dos smartphones no modo retrato
Largura média 600 dp+ 93,73% dos tablets no modo retrato

Telas internas grandes desdobradas no modo retrato

Largura expandida 840 dp+ 97,22% dos tablets no modo paisagem

Telas internas grandes desdobradas no modo paisagem

Altura compacta < 480 dp 99,78% dos smartphones no modo paisagem
Altura média 480 dp+ 96,56% dos tablets no modo paisagem

97,59% de smartphones no modo retrato

Altura expandida 900 dp+ 94,25% de tablets no modo retrato

Embora possa ser útil visualizar classes de tamanho como dispositivos físicos, as classes de tamanho de janela não são explicitamente determinadas pelo tamanho da tela do dispositivo. As classes de tamanho de janela não se destinam à lógica isTablet-type. Em vez disso, as classes de tamanho de janela são determinadas pelo tamanho da janela disponível para o aplicativo, independente do tipo de dispositivo em que ele está sendo executado. Isso tem duas consequências importantes:

  • Os dispositivos físicos não garantem uma classe de tamanho de janela específica. O espaço na tela disponível para o app pode ser diferente do tamanho da tela do dispositivo por vários motivos. Em dispositivos móveis, o modo de tela dividida pode dividir a tela entre dois aplicativos. No Chrome OS, os apps Android podem ser apresentados em janelas de formato livre e arbitrariamente redimensionáveis. Os dobráveis podem ter duas telas de tamanho diferente, acessadas individualmente ao dobrar ou desdobrar o dispositivo.

  • A classe de tamanho de janela pode mudar durante todo o ciclo de vida do app. Enquanto ele está em execução, a orientação, multitarefas e dobrar/desdobrar o dispositivo podem mudar a quantidade de espaço disponível na tela. Como resultado, a classe de tamanho da janela é dinâmica, e a IU do app precisa se adaptar.

As classes de tamanho de janela mapeiam para os pontos de interrupção do layout na grade de layout responsivo (link em inglês) do Material Design. Use as classes de tamanho de janela para tomar grandes decisões sobre o layout do aplicativo, como decidir se vai usar um layout canônico específico para aproveitar o espaço extra na tela.

Os apps baseados em visualização precisam calcular a classe de tamanho de janela de acordo com as métricas atuais da janela fornecidas pela biblioteca Jetpack WindowManager. O código de exemplo abaixo em Visualizações (Kotlin) e Visualizações (Java) mostra um exemplo de como calcular a classe de tamanho de janela com base nos pontos de interrupção e receber atualizações sempre que o tamanho mudar.

Apps baseados no Compose precisam usar a biblioteca material3-window-size-class para calcular uma WindowSizeClass com base nas métricas atuais da janela com calculateWindowSizeClass().

Visualizações

enum class WindowSizeClass { COMPACT, MEDIUM, EXPANDED }

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // ...

        // Replace with a known container that you can safely add a
        // view to where it 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. We also 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)
                computeWindowSizeClasses()
            }
        })

        computeWindowSizeClasses()
    }

    private fun computeWindowSizeClasses() {
        val metrics = WindowMetricsCalculator.getOrCreate()
            .computeCurrentWindowMetrics(this)

        val widthDp = metrics.bounds.width() /
            resources.displayMetrics.density
        val widthWindowSizeClass = when {
            widthDp < 600f -> WindowSizeClass.COMPACT
            widthDp < 840f -> WindowSizeClass.MEDIUM
            else -> WindowSizeClass.EXPANDED
        }

        val heightDp = metrics.bounds.height() /
            resources.displayMetrics.density
        val heightWindowSizeClass = when {
            heightDp < 480f -> WindowSizeClass.COMPACT
            heightDp < 900f -> WindowSizeClass.MEDIUM
            else -> WindowSizeClass.EXPANDED
        }

        // Use widthWindowSizeClass and heightWindowSizeClass.
    }
}

Visualizações

public enum WindowSizeClass { COMPACT, MEDIUM, EXPANDED }

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        // Replace with a known container that you can safely add a
        // view to where it 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. We also 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);
                computeWindowSizeClasses();
            }
        });

        computeWindowSizeClasses();
    }

    private void computeWindowSizeClasses() {
        WindowMetrics metrics = WindowMetricsCalculator.getOrCreate()
                .computeCurrentWindowMetrics(this);

        float widthDp = metrics.getBounds().width() /
                getResources().getDisplayMetrics().density;
        WindowSizeClass widthWindowSizeClass;

        if (widthDp < 600f) {
            widthWindowSizeClass = WindowSizeClass.COMPACT;
        } else if (widthDp < 840f) {
            widthWindowSizeClass = WindowSizeClass.MEDIUM;
        } else {
            widthWindowSizeClass = WindowSizeClass.EXPANDED;
        }

        float heightDp = metrics.getBounds().height() /
                getResources().getDisplayMetrics().density;
        WindowSizeClass heightWindowSizeClass;

        if (heightDp < 480f) {
            heightWindowSizeClass = WindowSizeClass.COMPACT;
        } else if (heightDp < 900f) {
            heightWindowSizeClass = WindowSizeClass.MEDIUM;
        } else {
            heightWindowSizeClass = WindowSizeClass.EXPANDED;
        }

        // Use widthWindowSizeClass and heightWindowSizeClass.
    }
}

Compose

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            val windowSizeClass = calculateWindowSizeClass(this)
            MyApp(windowSizeClass)
        }
    }
}

Quando estiver observando as classes de tamanho de janela no app, você vai estar pronto para começar a mudar o layout com base na classe de tamanho de janela atual.

Veja os recursos abaixo e aprenda a usar classes de tamanho de janela para tornar os layouts responsivos:

Lista de verificação para suporte a diferentes classes de tamanho de janela

Ao fazer mudanças no layout, teste o comportamento dele em todos os tamanhos de janela, especialmente nas larguras compactas, médias e expandidas dos pontos de interrupção.

Se você já tiver um layout de telas menores, primeiro ele deve ser otimizado para a classe de tamanho de largura expandida, porque isso fornece um espaço maior a mais conteúdos ou mudanças de layout. Em seguida, decida qual layout faz sentido para a classe de tamanho de largura média. Considere adicionar um layout especializado para o tamanho médio de tela.

Ofereça uma experiência melhorada ao usuário adicionando funcionalidades que se apliquem especificamente ao seu app, como suporte a posturas dobráveis ou otimizações de suporte a entradas de teclado, mouse e stylus.

Para saber mais sobre o que faz um app funcionar bem em todos os dispositivos e tamanhos de tela, acesse Qualidade de apps para telas grandes.

Design responsivo

A primeira etapa para oferecer suporte a vários formatos de dispositivo é criar um layout que responda a variações no tamanho da tela.

ConstraintLayout

A melhor maneira de criar um layout responsivo é usar ConstraintLayout como o layout básico na IU. O ConstraintLayout permite especificar a posição e o tamanho de cada visualização de acordo com as relações espaciais com outras visualizações no layout. Todas as visualizações podem se mover e ser redimensionadas ao mesmo tempo, conforme o tamanho da tela muda.

A maneira mais fácil de criar um layout com o ConstraintLayout é usar o Layout Editor no Android Studio. Esse recurso permite que você arraste novas visualizações para o layout, aplique restrições relativas a visualizações mãe e irmãs e defina propriedades de visualização, tudo isso sem editar qualquer XML manualmente.

Figura 3. O Layout Editor no Android Studio mostrando um arquivo ConstraintLayout.

Para ver mais informações, consulte Criar uma IU responsiva com o ConstraintLayout.

Largura e altura responsivas

Para garantir que o layout seja responsivo a diferentes tamanhos de tela, use wrap_content, match_parent ou 0dp (match constraint) para a largura e a altura da maioria dos componentes de visualização em vez de valores codificados:

  • wrap_content: permite que a visualização defina o próprio tamanho conforme necessário para ajustar o conteúdo dela.
  • match_parent: permite que a visualização se expanda o máximo possível dentro da visualização mãe.
  • 0dp (match constraint): em um ConstraintLayout, é semelhante a match_parent. Permite que a visualização ocupe todo o espaço disponível dentro das restrições dela.

Exemplo:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/lorem_ipsum" />

A Figura 4 mostra como a largura e a altura da TextView se ajustam conforme a largura da tela muda pela orientação do dispositivo.

Figura 4. Uma TextView responsiva.

A TextView define a largura para preencher todo o espaço disponível (match_parent) e define a altura exatamente como o espaço necessário para a altura do texto (wrap_content). Isso permite que a visualização se adapte a diferentes tamanhos de tela e de texto.

Se você usar um LinearLayout, também pode expandir as visualizações filhas com base no peso do layout para que elas sejam preenchidas proporcionalmente no espaço disponível. No entanto, para usar pesos em um LinearLayout aninhado, é necessário que o sistema realize várias transmissões de layout para determinar o tamanho de cada visualização, diminuindo o desempenho da IU.

O ConstraintLayout pode criar quase todos os mesmos layouts possíveis com LinearLayout sem impacto no desempenho. Por isso, tente converter seu LinearLayout aninhado em ConstraintLayout. Depois, você pode definir layouts com pesos usando cadeias de restrição.

Design adaptável

O layout do app precisa ser sempre responsivo a diferentes tamanhos de tela. No entanto, até mesmo um layout responsivo pode não oferecer a melhor experiência do usuário em todos os dispositivos. Por exemplo, a IU projetada para um smartphone provavelmente não oferece uma experiência ideal em um tablet. O design adaptável fornece layouts alternativos otimizados para diferentes dimensões de tela.

SlidingPaneLayout para IUs de detalhes de lista

Uma IU de detalhes de lista normalmente oferece uma experiência do usuário diferente em telas de tamanhos variados. Em telas grandes, os painéis de lista e de detalhes costumam estar lado a lado. Quando um item da lista é selecionado, as informações do item são exibidas no painel de detalhes sem alterar a IU. Os dois painéis permanecem lado a lado. No entanto, em telas pequenas, os dois painéis são mostrados separadamente, cada um ocupando toda a área de exibição. Quando um item no painel da lista é selecionado, o painel de detalhes (que contém as informações do item selecionado) substitui o painel da lista. A navegação de retorno substitui o painel de detalhes pela lista.

O SlidingPaneLayout gerencia a lógica para determinar qual das duas experiências do usuário é adequada ao tamanho atual da janela.

<?xml version="1.0" encoding="utf-8"?>
<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/item_navigation" />

</androidx.slidingpanelayout.widget.SlidingPaneLayout>

Os atributos layout_width e layout_weight das duas visualizações contidas em SlidingPaneLayout determinam o comportamento SlidingPaneLayout. No exemplo, se a janela for grande o suficiente (pelo menos 580 dp de largura) para exibir as duas visualizações, os painéis vão ser exibidos lado a lado. No entanto, se a largura da janela for menor que 580 dp, os painéis vão ser exibidos individualmente em relação aos outros para ocupar toda a janela do app.

Se a largura da janela for maior que a largura mínima especificada (580 dp), os valores layout_weight poderão ser usados para dimensionar os dois painéis proporcionalmente. No exemplo, o painel de lista sempre tem 280 dp de largura porque não tem um peso. No entanto, o painel de detalhes sempre preenche qualquer espaço horizontal além de 580 dp devido ao layout_weight da visualização.

Recursos de layouts alternativos

Para adaptar o design da IU a vários tamanhos de tela, use layouts alternativos identificados por qualificadores de recursos.

Figura 5. O mesmo app usa diferentes layouts para diferentes tamanhos de tela.

Você pode criar outros diretórios res/layout/ no código-fonte do app a fim de fornecer layouts adaptáveis específicos para a tela. Crie um diretório para cada configuração de tela que exige um layout diferente. Em seguida, anexe um qualificador de configuração de tela ao nome do diretório layout. Por exemplo, layout-w600dp para telas com 600 dp de largura disponível.

Os qualificadores de configuração representam o espaço de tela visível disponível para a IU do app. O sistema considera todas as decorações, como a barra de navegação, e mudanças na configuração de janelas, como o modo de várias janelas, ao selecionar o layout do app.

Para criar um layout alternativo no Android Studio (versão 3.0 ou mais recente), faça o seguinte:

  1. Abra o layout padrão e clique em Orientation for Preview na barra de ferramentas.
  2. Na lista suspensa, clique para criar uma variante sugerida, por exemplo, Create Landscape Variation ou clique em Create Other.
  3. Se você tiver selecionado Create Other, a guia Select Resource Directory vai aparecer. Selecione um qualificador de tela à esquerda e o adicione à lista de Chosen qualifiers. Quando terminar de adicionar qualificadores, clique em OK. Consulte as seções abaixo para ver informações sobre os qualificadores de tamanho de tela.

Uma cópia do arquivo de layout padrão é criada no novo diretório de layout para que você possa começar a personalizar o layout da variante da tela.

Qualificador de menor largura

O qualificador de tamanho de tela de menor largura permite que você forneça layouts alternativos para telas que tenham uma largura mínima medida em pixels de densidade independente (dp ou dip).

Ao descrever o tamanho da tela como uma medida de dp, o Android permite que você crie layouts voltados a dimensões de tela específicas sem se preocupar com diferentes densidades de pixel.

Por exemplo, é possível criar um layout com o nome main_activity, otimizado para celulares e tablets, criando versões diferentes do arquivo em diferentes diretórios:

res/layout/main_activity.xml           # For phones (smaller than 600dp smallest width)
res/layout-sw600dp/main_activity.xml   # For 7” tablets (600dp wide or wider)

O qualificador de menor largura especifica o menor dos dois lados da tela, independente da orientação atual do dispositivo. Essa é uma forma simples de especificar o tamanho de tela disponível para o layout.

Veja como outros valores de menor largura correspondem a tamanhos de tela típicos:

  • 320 dp: tela típica de smartphone (240 x 320 ldpi, 320 x 480 mdpi, 480 x 800 hdpi etc)
  • 480 dp: tela grande de smartphone (aprox. 5 pol.) (480 x 800 mdpi)
  • 600 dp: tablet de 7 pol. (600 x 1024 mdpi)
  • 720 dp: tablet de 10 pol. (720 x 1280 mdpi, 800 x 1280 mdpi, etc.)

A Figura 6 oferece uma visão mais detalhada de como diferentes larguras de tela em dp correspondem a diferentes tamanhos e orientações de tela.

Figura 6. Pontos de interrupção de largura recomendados para oferecer suporte a tamanhos diferentes de tela.

Os valores do qualificador de menor largura são medidos em dp, porque o importante é a quantidade de espaço disponível na tela depois que o sistema considera a densidade de pixels, não a resolução bruta deles.

Os tamanhos especificados usando qualificadores de recursos, como menor largura, não são tamanhos reais de telas. Na verdade, eles especificam a largura ou a altura em unidades de dp que estão disponíveis para a janela do app. O sistema Android pode usar parte da tela para a IU do sistema (por exemplo, a barra de sistema na parte de baixo da tela ou a barra de status na parte de cima). Sendo assim, a tela pode não estar totalmente disponível para o layout. Se o app for usado no modo de várias janelas, ele só vai ter acesso ao tamanho da janela que o contém. Quando a janela é redimensionada, ela aciona uma mudança de configuração com o novo tamanho da janela, permitindo que o sistema selecione um arquivo de layout adequado. Os tamanhos dos qualificadores de recurso declarados precisam especificar somente o espaço necessário para o app. O sistema considera todo o espaço usado pela IU do sistema ao fornecer espaço para o layout.

Qualificador de largura disponível

Em vez de mudar o layout com base na menor largura da tela, é possível mudá-lo com base na largura ou altura disponível no momento. Por exemplo, você pode usar um layout de dois painéis sempre que a tela tiver pelo menos 600 dp de largura, o que pode mudar dependendo da orientação do dispositivo (paisagem ou retrato). Nesse caso, use o qualificador de largura disponível desta maneira:

res/layout/main_activity.xml         # For phones (smaller than 600dp available width)
res/layout-w600dp/main_activity.xml  # For 7” tablets or any screen with 600dp available width
                                     # (possibly landscape phones)

Se a altura disponível for uma preocupação para o app, você pode usar o qualificador de altura disponível. Por exemplo, layout-h600dp para telas com pelo menos 600 dp de altura.

Qualificadores de orientação

Embora seja possível oferecer suporte a todas as variações de tamanho usando apenas combinações dos qualificadores de menor largura e de largura disponível, talvez você também queira mudar a experiência do usuário quando o app trocar entre as orientações de retrato e paisagem.

Para isso, adicione o qualificador port ou land aos nomes do diretório de layout. Confira se os qualificadores de orientação vêm depois dos qualificadores de tamanho. Exemplo:

res/layout/main_activity.xml                # For phones
res/layout-land/main_activity.xml           # For phones in landscape
res/layout-sw600dp/main_activity.xml        # For 7” tablets
res/layout-sw600dp-land/main_activity.xml   # For 7” tablets in landscape

Para ver mais informações sobre todos os qualificadores de configuração de tela, consulte Visão geral dos recursos de app.

Componentes de IU modularizados usando fragmentos

Ao criar o app para vários tamanhos de tela, use fragmentos para extrair a lógica da IU em componentes diferentes e garantir que não há duplicação desnecessária do comportamento da IU nas atividades. Depois, você pode combinar fragmentos para criar layouts de vários painéis em telas grandes ou os colocar em atividades separadas em telas pequenas.

Por exemplo, o padrão de detalhes de lista (consulte SlidingPaneLayout acima) pode ser implementado com um fragmento que contém a lista e outro com os detalhes do item. Em telas grandes, os fragmentos podem ser exibidos lado a lado ou podem ser mostrados individualmente em telas pequenas.

Para saber mais, consulte a visão geral de fragmentos.

Incorporação de atividades

Se o app for composto de várias atividades, a incorporação de atividades vai permitir que você crie uma IU adaptável com facilidade.

Esse recurso permite mostrar várias atividades ou instâncias da mesma atividade simultaneamente na janela de tarefas de um app. Em telas grandes, as atividades podem ser exibidas lado a lado. Em telas pequenas, podem estar uma empilhada uma sobre a outra.

Para determinar como o app exibe as atividades, crie um arquivo de configuração XML que o sistema vai usar para determinar a apresentação apropriada com base no tamanho da tela. Como alternativa, você pode fazer chamadas da API Jetpack WindowManager.

A incorporação de atividades oferece suporte a mudanças de orientação do dispositivo e dispositivos dobráveis, empilhando e desempilhando atividades conforme o dispositivo é girado ou dobrado e desdobrado.

Para mais informações, consulte Incorporação de atividades.

Tamanhos e proporções de tela

Teste o app em vários tamanhos e proporções de tela para garantir que a IU seja dimensionada corretamente.

O Android 10 (nível 29 da API) e versões mais recentes oferecem suporte a várias proporções. Os formatos dobráveis podem variar de telas altas e estreitas, como 21:9 quando dobrado, até uma proporção quadrada de 1:1 quando desdobrado.

Para garantir a compatibilidade com o maior número possível de dispositivos, teste seus apps com o maior número possível de proporções de tela.

Figura 7. Proporções de tela.

Se o app não oferecer suporte a algumas proporções, use maxAspectRatio e minAspectRatio para indicar as maiores e menores proporções com suporte do app. Nos casos em que as telas excedem esses limites, o app pode ser colocado no modo de compatibilidade.

Se você não tiver acesso a dispositivos para todos os diferentes tamanhos de tela que quiser testar, pode usar o Android Emulator e emular quase todos os tamanhos de tela.

Se você preferir testar em um dispositivo físico, mas não tiver um, use o Firebase Test Lab para acessar dispositivos em um Data Center do Google.

Suporte ao tamanho específico da tela

Se você não quiser que o app seja executado em alguns tamanhos de tela, pode definir limites de até quanto a tela pode ser redimensionada ou até mesmo limitar quais dispositivos podem instalar o app com base na configuração de tela. Para saber mais, consulte Declarar suporte restrito à tela.

Outros recursos (em inglês)