Desenvolvedores que trabalham com Xamarin.Forms foram recentemente surpreendidos por uma mudança significativa no Android 15: os aplicativos começaram a renderizar em tela cheia (edge-to-edge) por padrão, sobrepondo as barras de status e de navegação. Este comportamento inesperado quebrou a interface de muitos aplicativos que antes funcionavam perfeitamente.
Este post oferece uma documentação completa e uma solução robusta para corrigir esse problema, garantindo que seu aplicativo mantenha uma aparência profissional e funcional em todas as versões do Android, incluindo a mais recente. A solução foi implementada e testada em um projeto real, o AppCelmi.
O Problema: UI Quebrada no Android 15
Com a chegada do Android 15, o Google alterou a forma como os aplicativos lidam com o espaço da tela. O modo edge-to-edge tornou-se o padrão, forçando o conteúdo do aplicativo a se estender sob as áreas do sistema.
Sintomas
- Sobreposição da UI: O aplicativo renderiza sobre o relógio, ícones de notificação e a barra de navegação.
- Elementos Inacessíveis: Botões e outros componentes de UI podem ficar escondidos atrás das barras do sistema.
- Experiência do Usuário (UX) Comprometida: A interface se torna confusa, pouco profissional e difícil de usar.
- Impacto Generalizado: Afeta principalmente aplicativos legados ou que não foram explicitamente preparados para o edge-to-edge.
Antes da Solução (Problema) | Depois da Solução (Corrigido) |
✗ Conteúdo sobre a status bar | ✅ Status bar com cor sólida e respeitada |
✗ App “vaza” para a navigation bar | ✅ Conteúdo ajustado acima da navegação |
✗ Interface confusa | ✅ Layout limpo e profissional |
Export to Sheets
A Solução: WindowInsets
e ViewCompat
A solução correta e recomendada pelo Google envolve o uso das APIs ViewCompat
e WindowInsetsCompat
para gerenciar dinamicamente o preenchimento (padding) da sua aplicação, respeitando as áreas do sistema (insets).
Por que esta abordagem?
- Oficial e Moderna: Segue as diretrizes oficiais do Google para o desenvolvimento Android.
- Compatibilidade Retroativa: Funciona perfeitamente desde o Android 5.0 (API 21) até o Android 15 (API 35).
- Robusta e Duradoura: É uma solução “future-proof”, que se adapta a diferentes tamanhos de tela, notches e barras de navegação.
Implementação do Código
A correção é aplicada inteiramente no projeto Android (.Droid
) da sua solução Xamarin.Forms, sem a necessidade de alterar o código compartilhado.
1. Arquivo: MainActivity.cs
No método OnCreate
, orquestramos a configuração da janela para desabilitar o comportamento padrão e, em seguida, aplicamos nosso listener de insets personalizado.
C#
using Android.App;
using Android.Content.PM;
using Android.OS;
using Android.Views;
using AndroidX.Core.View;
using Xamarin.Forms.Platform.Android;
namespace BalancasCelmi.Droid // Substitua pelo seu namespace
{
[Activity(Label = "AppCelmi", Icon = "@mipmap/icon", Theme = "@style/MainTheme", /* ... outros atributos ... */)]
public partial class MainActivity : FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// 1. Configura a janela para renderização controlada
ConfigureWindowInsets();
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
// 2. Aplica o listener que ajustará o padding
ApplyWindowInsets();
}
private void ConfigureWindowInsets()
{
// Habilita o controle sobre as áreas do sistema
Window.AddFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);
Window.ClearFlags(WindowManagerFlags.TranslucentStatus | WindowManagerFlags.TranslucentNavigation);
Window.SetStatusBarColor(Android.Graphics.Color.Rgb(246, 114, 16)); // Cor laranja do tema
// Para Android 11+ (API 30+), informa que o app cuidará dos insets
if (Build.VERSION.SdkInt >= BuildVersionCodes.R)
{
Window.SetDecorFitsSystemWindows(false);
}
// Para versões mais antigas, usa flags de visibilidade
else if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
{
Window.DecorView.SystemUiVisibility = (StatusBarVisibility)(
SystemUiFlags.LayoutStable |
SystemUiFlags.LayoutHideNavigation |
SystemUiFlags.LayoutFullscreen
);
}
}
private void ApplyWindowInsets()
{
var contentView = FindViewById(Android.Resource.Id.Content);
if (contentView != null)
{
// Define o listener para a view principal
ViewCompat.SetOnApplyWindowInsetsListener(contentView, new WindowInsetsListener());
ViewCompat.RequestApplyInsets(contentView);
}
}
}
// Listener que recebe os insets do sistema e aplica o padding
public class WindowInsetsListener : Java.Lang.Object, IOnApplyWindowInsetsListener
{
public WindowInsetsCompat OnApplyWindowInsets(View v, WindowInsetsCompat insets)
{
// Obtém os insets das barras do sistema (status bar, navigation bar) e recortes (notch)
var systemBars = insets.GetInsets(
WindowInsetsCompat.Type.SystemBars() |
WindowInsetsCompat.Type.DisplayCutout()
);
// Aplica o padding na view principal da aplicação
v.SetPadding(
0, // Left
systemBars.Top, // Top
0, // Right
systemBars.Bottom // Bottom
);
// Informa ao sistema que os insets foram consumidos
return WindowInsetsCompat.Consumed;
}
}
}
2. Arquivo: Resources/values/styles.xml
É crucial garantir que seu tema não force o modo de tela cheia ou translúcido, permitindo que o código C# gerencie o comportamento.
XML
<pre class="wp-block-syntaxhighlighter-code"> <item name="colorPrimary">#F6720F</item>
<item name="colorPrimaryDark">#F6720F</item>
<item name="colorAccent">#F5D2BE</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:fitsSystemWindows">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item> </pre>
Como a Solução Funciona
-
Desativar o Automático: Primeiro, instruímos o sistema Android (via
styles.xml
e o métodoConfigureWindowInsets
) a não aplicar o modo edge-to-edge automaticamente. Nós assumimos o controle. -
Ouvir o Sistema: Em seguida, registramos um
WindowInsetsListener
. Este “ouvinte” é notificado pelo sistema operacional sempre que as dimensões das barras do sistema mudam (por exemplo, ao girar a tela). - Calcular o Padding: O listener recebe as dimensões exatas da status bar (topo) e da navigation bar (rodapé).
- Aplicar Padding Dinâmico: Com essas dimensões, ele aplica um padding dinâmico à view raiz do seu aplicativo, empurrando seu conteúdo para a área visível e segura, evitando sobreposições.
Resultados e Benefícios
A implementação desta solução restaura completamente o layout esperado da aplicação.
✅ UI Profissional: A interface volta a ser limpa, organizada e profissional.
✅ Compatibilidade Total: Funciona de forma consistente em todas as versões modernas do Android.
✅ Design Responsivo: Adapta-se nativamente a qualquer dispositivo, com ou sem notch.
✅ Manutenibilidade: O código é limpo, segue as melhores práticas e não exigirá manutenção constante a cada nova versão do Android.
Conclusão e Recomendação Final
Embora as mudanças no Android 15 tenham pego muitos de surpresa, a solução baseada em WindowInsets
é a forma correta e definitiva de garantir que seus aplicativos Xamarin.Forms se comportem adequadamente. A implementação é relativamente simples e se concentra apenas na camada nativa do Android, preservando seu código de negócios compartilhado.
Recomendação: Adote esta prática em todos os seus projetos Xamarin.Forms para evitar problemas futuros e garantir uma experiência de usuário impecável.
Data da implementação e teste: 04 de julho de 2025.
Para mais detalhes técnicos, consulte a documentação oficial do Android sobre Edge-to-Edge.