Mostrar conteúdo de ponta a ponta nas visualizações

Experimente trabalhar com o Compose
O Jetpack Compose é o kit de ferramentas de IU recomendado para Android. Saiba como trabalhar com o recurso de ponta a ponta no Compose.

Depois de segmentar o SDK 35 ou mais recente em um dispositivo com o Android 15 ou mais recente, o app será mostrado de ponta a ponta. A janela abrange toda a largura e altura da tela, sendo renderizada atrás das barras de sistema. As barras de sistema incluem as barras de status, de legenda e de navegação.

Muitos apps têm uma barra de apps superior. A barra de apps de cima precisa se estender até a borda superior da tela e aparecer atrás da barra de status. Se quiser, a barra de apps de cima pode encolher até a altura da barra de status quando o conteúdo é rolado.

Muitos apps também têm uma barra de apps ou barra de navegação inferior. Essas barras também precisam se estender até a borda inferior da tela e aparecer atrás da barra de navegação. Caso contrário, os apps precisam mostrar o conteúdo de rolagem atrás da barra de navegação.

Figura 1. Barras de sistema em um layout de ponta a ponta.

Ao implementar um layout de ponta a ponta no app, lembre-se do seguinte:

  1. Ativar uma exibição de ponta a ponta
  2. Implemente layouts adaptáveis para otimizar a experiência do usuário em diferentes formatos.
  3. Lidar com sobreposições visuais
  4. Considere mostrar scrims atrás das barras do sistema
um exemplo de imagem atrás da barra de status
Figura 2. Exemplo de imagens atrás da barra de status.

Ativar a exibição de ponta a ponta

Se o app for destinado ao SDK 35 ou mais recente, a exibição de ponta a ponta será ativada automaticamente para dispositivos com o Android 15 ou mais recente.

Para ativar a exibição de ponta a ponta em versões anteriores do Android, chame manualmente enableEdgeToEdge em onCreate da sua Activity.

Kotlin

 override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         WindowCompat.enableEdgeToEdge(window)
        ...
      }

Java

 @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WindowCompat.enableEdgeToEdge(getWindow());
        ...
      }

Por padrão, enableEdgeToEdge() torna as barras de sistema transparentes, exceto no modo de navegação com três botões, em que a barra de navegação recebe uma tela translúcida. As cores dos ícones do sistema e da tela de fundo são ajustadas com base no tema claro ou escuro do sistema.

Para ativar a exibição de ponta a ponta no app sem usar a função enableEdgeToEdge(), consulte Configurar manualmente a exibição de ponta a ponta.

Processar sobreposições usando encartes

Algumas das visualizações do app podem ser renderizadas atrás das barras de sistema, como mostrado na figura 3.

Para resolver as sobreposições, reaja às margens, que especificam quais partes da tela se cruzam com a interface do sistema, como a barra de navegação ou a barra de status. A interseção pode significar mostrar mais conteúdo, mas também pode informar seu app sobre gestos do sistema.

Os tipos de encartes que se aplicam à exibição de ponta a ponta do app são:

  • Encartes de barras de sistema:são mais adequados para visualizações que podem ser tocadas e que não podem ser visualmente ocultadas pelas barras de sistema.

  • Inserções de corte da tela:para áreas em que pode haver um corte na tela devido ao formato do dispositivo.

  • Inserções de gestos do sistema:para áreas de navegação por gestos usadas pelo sistema que têm prioridade sobre o app.

Encartes de barras de sistema

Os recuos da barra de sistema são o tipo mais usado com frequência. Eles representam a área em que a interface do sistema é mostrada no eixo Z acima do app. Eles são mais usados para mover ou preencher visualizações no app que são tocáveis e que não podem ser visualmente ocultadas pelas barras de sistema.

Por exemplo, o botão de ação flutuante (FAB, na sigla em inglês) na figura 3 está parcialmente coberto pela barra de navegação:

um exemplo de implementação de ponta a ponta, mas a barra de navegação está cobrindo o FAB
Figura 3. Barra de navegação sobrepondo um FAB em um layout de ponta a ponta.

Para evitar esse tipo de sobreposição visual no modo de gestos ou de botões, aumente as margens da visualização usando getInsets(int) com WindowInsetsCompat.Type.systemBars().

O exemplo de código abaixo mostra como implementar as inserções da barra de sistema:

Kotlin

ViewCompat.setOnApplyWindowInsetsListener(fab) { v, windowInsets ->
  val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
  // Apply the insets as a margin to the view. This solution sets
  // only the bottom, left, and right dimensions, but you can apply whichever
  // insets are appropriate to your layout. You can also update the view padding
  // if that's more appropriate.
  v.updateLayoutParams<MarginLayoutParams> {
      leftMargin = insets.left
      bottomMargin = insets.bottom
      rightMargin = insets.right
  }

  // Return CONSUMED if you don't want the window insets to keep passing
  // down to descendant views.
  WindowInsetsCompat.CONSUMED
}

Java

ViewCompat.setOnApplyWindowInsetsListener(fab, (v, windowInsets) -> {
  Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
  // Apply the insets as a margin to the view. This solution sets only the
  // bottom, left, and right dimensions, but you can apply whichever insets are
  // appropriate to your layout. You can also update the view padding if that's
  // more appropriate.
  MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
  mlp.leftMargin = insets.left;
  mlp.bottomMargin = insets.bottom;
  mlp.rightMargin = insets.right;
  v.setLayoutParams(mlp);

  // Return CONSUMED if you don't want the window insets to keep passing
  // down to descendant views.
    return WindowInsetsCompat.CONSUMED;
});

Se você aplicar essa solução ao exemplo mostrado na figura 3, não haverá sobreposição visual no modo de botão, como mostrado na figura 4:

uma barra de navegação translúcida que não cobre o FAB
Figura 4. Como resolver a sobreposição visual no modo de botão.

O mesmo se aplica ao modo de navegação por gestos, como mostrado na Figura 5:

de ponta a ponta com navegação por gestos
Figura 5. Como resolver a sobreposição visual no modo de navegação por gestos.

Inserções de corte da tela

Alguns dispositivos têm cortes da tela. Normalmente, o corte fica na parte de cima da tela e é incluído na barra de status. Quando a tela do dispositivo está no modo paisagem, o corte pode estar na borda vertical. Dependendo do conteúdo que o app mostra na tela, é necessário implementar o padding para evitar cortes da tela, já que, por padrão, os apps são renderizados no corte da tela.

Por exemplo, muitas telas de apps mostram uma lista de itens. Não oculte itens de lista com o corte da tela ou as barras de sistema.

Kotlin

ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, insets ->
  val bars = insets.getInsets(
    WindowInsetsCompat.Type.systemBars()
      or WindowInsetsCompat.Type.displayCutout()
  )
  v.updatePadding(
    left = bars.left,
    top = bars.top,
    right = bars.right,
    bottom = bars.bottom,
  )
  WindowInsetsCompat.CONSUMED
}

Java

ViewCompat.setOnApplyWindowInsetsListener(mBinding.recyclerView, (v, insets) -> {
  Insets bars = insets.getInsets(
    WindowInsetsCompat.Type.systemBars()
    | WindowInsetsCompat.Type.displayCutout()
  );
  v.setPadding(bars.left, bars.top, bars.right, bars.bottom);
  return WindowInsetsCompat.CONSUMED;
});

Determine o valor de WindowInsetsCompat usando o or lógico das barras de sistema e dos tipos de corte da tela.

Defina clipToPadding como RecyclerView para que o padding role com os itens da lista. Isso permite que os itens fiquem atrás das barras de sistema quando o usuário rola a tela, como mostrado no exemplo a seguir.

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

Encartes de gestos do sistema

As margens de gestos do sistema representam as áreas da janela em que os gestos do sistema têm prioridade sobre o app. Essas áreas são mostradas em laranja na Figura 6:

Exemplo de inserções de gestos do sistema
Figura 6. Encartes de gestos do sistema.

Assim como as inserções da barra de sistema, é possível evitar a sobreposição das inserções de gestos do sistema usando getInsets(int) com WindowInsetsCompat.Type.systemGestures().

Use esses encartes para mover ou preencher as visualizações deslizáveis para longe das bordas. Os casos de uso comuns incluem páginas inferiores, deslizar em jogos e carrosséis implementados usando ViewPager2.

No Android 10 ou versões mais recentes, os recuos de gestos do sistema contêm um recuo inferior para o gesto de início e um recuo esquerdo e direito para os gestos de voltar:

um exemplo de medições de inserção de gestos do sistema
Figura 7. Medidas de inserção de gestos do sistema.

O exemplo de código abaixo mostra como implementar as inserções de gestos do sistema:

Kotlin

ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets ->
    val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
    // Apply the insets as padding to the view. Here, set all the dimensions
    // as appropriate to your layout. You can also update the view's margin if
    // more appropriate.
    view.updatePadding(insets.left, insets.top, insets.right, insets.bottom)

    // Return CONSUMED if you don't want the window insets to keep passing down
    // to descendant views.
    WindowInsetsCompat.CONSUMED
}

Java

ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
    Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures());
    // Apply the insets as padding to the view. Here, set all the dimensions
    // as appropriate to your layout. You can also update the view's margin if
    // more appropriate.
    view.setPadding(insets.left, insets.top, insets.right, insets.bottom);

    // Return CONSUMED if you don't want the window insets to keep passing down
    // to descendant views.
    return WindowInsetsCompat.CONSUMED;
});

Componentes do Material Design

Muitos componentes do Material Design para Android (link em inglês) baseados em visualizações (com.google.android.material) processam encartes automaticamente, incluindo BottomAppBar, BottomNavigationView, NavigationRailView e NavigationView.

No entanto, o AppBarLayout não processa encartes automaticamente. Adicione android:fitsSystemWindows="true" para processar encartes superiores.

Leia como processar encartes com componentes do Material Design no Compose.

Envio de encartes compatível com versões anteriores

Para interromper o envio de encartes para visualizações filhas e evitar o excesso de padding, use a constante WindowInsetsCompat.CONSUMED para consumir encartes. No entanto, em dispositivos com o Android 10 (nível 29 da API e versões anteriores), as inserções não são enviadas para os irmãos depois de chamar WindowInsetsCompat.CONSUMED, o que pode causar uma sobreposição visual indesejada.

Exemplo de envio de inserção corrompido
Figura 8. Exemplo de envio de encarte corrompido. As inserções não são enviadas para visualizações irmãs depois que o ViewGroup 1 as consome no Android 10 (nível 29 da API) e versões anteriores, fazendo com que o TextView 2 se sobreponha à barra de navegação do sistema. No entanto, as inserções são enviadas para visualizações irmãs no Android 11 (nível da API 30) e versões mais recentes, conforme esperado.

Para confirmar que as inserções são enviadas para irmãos em todas as versões do Android com suporte, use ViewGroupCompat#installCompatInsetsDispatch antes de consumir inserções, disponíveis no AndroidX Core e Core-ktx 1.16.0-alpha01 e versões mais recentes.

Kotlin

// Use the i.d. assigned to your layout's root view, e.g. R.id.main
val rootView = findViewById(R.id.main)
// Call before consuming insets
ViewGroupCompat.installCompatInsetsDispatch(rootView)

Java

// Use the i.d. assigned to your layout's root view, e.g. R.id.main
LinearLayout rootView = findViewById(R.id.main);
// Call before consuming insets
ViewGroupCompat.installCompatInsetsDispatch(rootView);
Exemplo de envio de inserção fixa
Figura 9. Correção do envio de encartes após chamar ViewGroupCompat#installCompatInsetsDispatch.

Modo imersivo

Alguns conteúdos são mais bem aproveitados em tela cheia, proporcionando ao usuário uma experiência mais imersiva. É possível ocultar as barras de sistema para um modo imersivo usando as bibliotecas WindowInsetsController e WindowInsetsControllerCompat:

Kotlin

val windowInsetsController =
      WindowCompat.getInsetsController(window, window.decorView)

// Hide the system bars.
windowInsetsController.hide(Type.systemBars())

// Show the system bars.
windowInsetsController.show(Type.systemBars())

Java

Window window = getWindow();
WindowInsetsControllerCompat windowInsetsController =
      WindowCompat.getInsetsController(window, window.getDecorView());
if (windowInsetsController == null) {
    return;
  }
// Hide the system bars.
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());

// Show the system bars.
windowInsetsController.show(WindowInsetsCompat.Type.systemBars());

Consulte Ocultar as barras de sistema para o modo imersivo para mais informações sobre como implementar esse recurso.

Ícones da barra de sistema

Chamar enableEdgeToEdge garante que as cores dos ícones da barra de sistema sejam atualizadas quando o tema do dispositivo mudar.

Ao usar o modo de ponta a ponta, talvez seja necessário atualizar manualmente as cores dos ícones da barra de sistema para que eles contrastem com o plano de fundo do app. Por exemplo, para criar ícones de barra de status claros:

Kotlin

WindowCompat.getInsetsController(window, window.decorView)
    .isAppearanceLightStatusBars = false

Java

WindowCompat.getInsetsController(window, window.getDecorView())
    .setAppearanceLightStatusBars(false);

Proteção da barra de sistema

Quando o app for direcionado ao SDK 35 ou versões mais recentes, a exibição de ponta a ponta será aplicada. As barras de status do sistema e de navegação por gestos são transparentes, mas a barra de navegação com três botões é translúcida. Chame enableEdgeToEdge para que isso seja compatível com versões anteriores.

No entanto, os padrões do sistema podem não funcionar para todos os casos de uso. Consulte as orientações de design das barras de sistema do Android e o design de ponta a ponta para determinar se você deve usar barras de sistema transparentes ou translúcidas.

Criar barras de sistema transparentes

Crie uma barra de status transparente direcionando o app ao Android 15 (SDK 35) ou versões mais recentes ou chamando enableEdgeToEdge() com argumentos padrão para versões anteriores.

Crie uma barra de navegação por gestos transparente segmentando o Android 15 ou versões mais recentes ou chamando enableEdgeToEdge() com argumentos padrão para versões anteriores. Para a barra de navegação com três botões, defina Window.setNavigationBarContrastEnforced como false. Caso contrário, uma tela translúcida será aplicada.

Criar barras de sistema translúcidas

Para criar uma barra de status translúcida, faça o seguinte:

  1. Atualize a dependência androidx-core para 1.16.0-beta01 ou uma versão mais recente.
  2. Envolva o layout XML em androidx.core.view.insets.ProtectionLayout e atribua um ID.
  3. Acesse o ProtectionLayout de forma programática para definir proteções, especificando o lado e um GradientProtection para a barra de status.

<androidx.core.view.insets.ProtectionLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list_protection"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:id="@+id/item_list"
        android:clipToPadding="false"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--items-->

    </ScrollView>

</androidx.core.view.insets.ProtectionLayout>

findViewById<ProtectionLayout>(R.id.list_protection)
    .setProtections(
        listOf(
            GradientProtection(
                WindowInsetsCompat.Side.TOP,
                // Ideally, this is the pane's background color
                paneBackgroundColor
            )
        )
    )

Verifique se o ColorInt transmitido para GradientProtection corresponde ao plano de fundo do conteúdo. Por exemplo, um layout de detalhes e listas exibido em um dobrável pode ter diferentes GradientProtections de cores diferentes para o painel de lista e o painel de detalhes.

Figura 1. Proteção de gradiente de cores diferentes.

Não crie uma barra de navegação por gestos translúcida. Para criar uma barra de navegação translúcida com três botões, siga um destes procedimentos:

  • Se o layout já estiver encapsulado em um ProtectionView, transmita um ColorProtection ou GradientProtection extra para o método setProtections. Antes de fazer isso, verifique se window.isNavigationBarContrastEnforced = false.
  • Caso contrário, defina window.isNavigationBarContrastEnforced = true.

Outras dicas

Outras dicas para lidar com inserções.

Criar conteúdo de rolagem de ponta a ponta

Verifique se o último item da lista não está oculto pelas barras de sistema no RecyclerView ou NestedScrollView, processando as inserções e definindo clipToPadding como false.

O vídeo a seguir mostra uma RecyclerView com a exibição de ponta a ponta desativada (à esquerda) e ativada (à direita):

Consulte os snippets de código na seção Criar listas dinâmicas com a RecyclerView para ver um exemplo de código.

Abrir caixas de diálogo em tela cheia de ponta a ponta

Para abrir caixas de diálogo em tela cheia de ponta a ponta, chame enableEdgeToEdge na caixa de diálogo.

Kotlin

class MyAlertDialogFragment : DialogFragment() {
    override fun onStart(){
        super.onStart()
        dialog?.window?.let { WindowCompat.enableEdgeToEdge(it) }
    }
    ...
}

Java

public class MyAlertDialogFragment extends DialogFragment {
    @Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();
        if (dialog != null) {
            Window window = dialog.getWindow();
            if (window != null) {
                WindowCompat.enableEdgeToEdge(window);
            }
        }
    }
    ...
}

Outros recursos

Consulte as referências a seguir para mais informações sobre como usar o recurso de ponta a ponta.

Blogs

Design

Outras documentações

Vídeos