WebView gestisce l'allineamento dei contenuti utilizzando due aree visibili: l'area visibile di layout (la dimensione della pagina) e l'area visibile visiva (la parte della pagina che l'utente vede effettivamente). Mentre l'area visibile del layout è generalmente statica, l'area visibile visiva cambia dinamicamente quando gli utenti eseguono lo zoom, scorrono o quando vengono visualizzati elementi dell'interfaccia utente di sistema (come la tastiera software).
Compatibilità delle funzionalità
Il supporto di WebView per gli inset della finestra si è evoluto nel tempo per allineare il comportamento dei contenuti web alle aspettative delle app Android native:
| Traguardo | Funzionalità aggiunta | Ambito |
|---|---|---|
| M136 | displayCutout() e systemBars() tramite CSS safe-area-insets. |
Solo WebView a schermo intero. |
| M139 | Supporto dell'ime() (editor del metodo di input, ovvero una tastiera) tramite il ridimensionamento visivo dell'area visibile. |
Tutte le WebView. |
| M144 | Supporto per displayCutout() e systemBars(). |
Tutte le WebView (indipendentemente dallo stato a schermo intero). |
Per saperne di più, consulta WindowInsetsCompat.
Meccaniche di base
WebView gestisce gli inset tramite due meccanismi principali:
Aree sicure (
displayCutout,systemBars): WebView inoltra queste dimensioni ai contenuti web tramite le variabili CSS safe-area-inset-*. In questo modo, gli sviluppatori possono impedire che i propri elementi interattivi (come le barre di navigazione) vengano oscurati da tacche o barre di stato.Ridimensionamento dell'area visibile visiva tramite l'Input Method Editor (IME): a partire da M139, l'Input Method Editor (IME) ridimensiona direttamente l'area visibile visiva. Questo meccanismo di ridimensionamento si basa anche sull'intersezione tra WebView e finestra. Ad esempio, in modalità multitasking di Android, se la parte inferiore di una WebView si estende 200 dp sotto la parte inferiore della finestra, l'area visibile è 200 dp più piccola delle dimensioni della WebView. Questo ridimensionamento dell'area visibile (sia per l'IME che per l'intersezione WebView-Window) viene applicato solo alla parte inferiore di WebView. Questo meccanismo non supporta il ridimensionamento per la sovrapposizione a sinistra, a destra o in alto. Ciò significa che le tastiere agganciate che compaiono su questi bordi non attivano un ridimensionamento visivo dell'area visibile.
In precedenza, l'area visibile visiva rimaneva fissa, spesso nascondendo i campi di input dietro la tastiera. Se ridimensioni l'area visibile, la parte visibile della pagina diventa scorrevole per impostazione predefinita, garantendo agli utenti di raggiungere i contenuti oscurati.
Logica di limiti e sovrapposizione
WebView deve ricevere valori di rientro diversi da zero solo quando gli elementi dell'interfaccia utente di sistema (barre, intagli del display o tastiera) si sovrappongono direttamente ai limiti dello schermo di WebView. Se un componente WebView non si sovrappone a questi elementi dell'interfaccia utente (ad esempio, se un componente WebView è centrato sullo schermo e non tocca le barre di sistema), deve ricevere questi insets come zero.
Per ignorare questa logica predefinita e fornire i contenuti web con le dimensioni
complete del sistema indipendentemente dalla sovrapposizione, utilizza il
metodo setOnApplyWindowInsetsListener e restituisci l'oggetto
windowInsets originale e non modificato dal listener. Fornire le dimensioni complete del sistema
può contribuire a garantire la coerenza del design consentendo l'allineamento dei contenuti web con
l'hardware del dispositivo indipendentemente dalla posizione attuale di WebView. In questo modo, la transizione è fluida quando WebView si sposta o si espande fino a toccare i bordi dello schermo.
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(myWebView) { _, windowInsets ->
// By returning the original windowInsets object, we override the default
// behavior that zeroes out system insets (like system bars or display
// cutouts) when they don't directly overlap the WebView's screen bounds.
windowInsets
}
Java
ViewCompat.setOnApplyWindowInsetsListener(myWebView, (v, windowInsets) -> {
// By returning the original windowInsets object, we override the default
// behavior that zeroes out system insets (like system bars or display
// cutouts) when they don't directly overlap the WebView's screen bounds.
return windowInsets;
});
Gestire gli eventi di ridimensionamento
Poiché ora la visibilità della tastiera attiva un ridimensionamento dell'area visibile visiva, il codice web potrebbe visualizzare eventi di ridimensionamento più frequenti. Gli sviluppatori devono assicurarsi che il codice non reagisca a questi eventi di ridimensionamento cancellando lo stato attivo dell'elemento. In questo modo si crea un ciclo di perdita di messa a fuoco e chiusura della tastiera che impedisce l'input dell'utente:
- L'utente si concentra su un elemento di input.
- Viene visualizzata la tastiera, che attiva un evento di ridimensionamento.
- Il codice del sito web cancella lo stato attivo in risposta al ridimensionamento.
- La tastiera si nasconde perché il focus è stato perso.
Per mitigare questo comportamento, rivedi i listener lato web per assicurarti che le modifiche
alla finestra non attivino involontariamente la funzione JavaScript blur() o
i comportamenti di cancellazione della messa a fuoco.
Implementare la gestione degli inset
Le impostazioni predefinite di WebView funzionano automaticamente per la maggior parte delle app. Tuttavia, se la tua
app utilizza layout personalizzati (ad esempio, se aggiungi un tuo padding per tenere conto
della barra di stato o della tastiera), puoi utilizzare i seguenti approcci per migliorare il modo in cui
i contenuti web e l'interfaccia utente nativa funzionano insieme. Se la tua UI nativa applica il padding
a un contenitore in base a WindowInsets, devi gestire questi inset
correttamente prima che raggiungano il componente WebView per evitare il doppio padding.
Il doppio padding si verifica quando il layout nativo e i contenuti web applicano le stesse dimensioni di rientro, con conseguente spaziatura ridondante. Ad esempio, immagina uno smartphone con una barra di stato di 40 px. Sia la visualizzazione nativa che WebView vedono l'inset di 40 px. Entrambi aggiungono 40 px di spaziatura interna, il che comporta una distanza di 80 px nella parte superiore della pagina.
L'approccio di azzeramento
Per evitare il doppio padding, devi assicurarti che dopo che una visualizzazione nativa utilizza una
dimensione di rientro per il padding, reimposti questa dimensione su zero utilizzando
Insets.NONE su un nuovo oggetto WindowInsets prima di passare l'oggetto modificato
nella gerarchia delle visualizzazioni a WebView.
Quando applichi il padding a una visualizzazione principale, in genere devi utilizzare l'approccio
di azzeramento impostando Insets.NONE anziché WindowInsetsCompat.CONSUMED.
La restituzione di WindowInsetsCompat.CONSUMED potrebbe funzionare in determinate situazioni.
Tuttavia, possono verificarsi problemi se il gestore dell'app modifica gli inset o aggiunge il proprio
spazio interno. L'approccio di azzeramento non presenta queste limitazioni.
Evita il padding fantasma impostando gli inset a zero
Se utilizzi gli inset quando l'app ha precedentemente superato gli inset non utilizzati o se gli inset cambiano (ad esempio la tastiera si nasconde), il loro utilizzo impedisce a WebView di ricevere la notifica di aggiornamento necessaria. Ciò può far sì che WebView mantenga la spaziatura interna fantasma di uno stato precedente (ad esempio, mantenendo la spaziatura interna della tastiera dopo che è stata nascosta).
L'esempio seguente mostra un'interazione interrotta tra l'app e WebView:
- Stato iniziale: l'app inizialmente passa gli inset non consumati (ad esempio,
displayCutout()osystemBars()) al componente WebView, che applica internamente il padding ai contenuti web. - Modifica dello stato ed errore:se l'app cambia stato (ad esempio, la tastiera si nasconde) e sceglie di gestire gli inset risultanti restituendo
WindowInsetsCompat.CONSUMED. - Notifica bloccata:l'utilizzo degli inset impedisce al sistema Android di inviare la notifica di aggiornamento necessaria lungo la gerarchia delle visualizzazioni a WebView.
- Spazio interno fantasma:poiché WebView non riceve l'aggiornamento, mantiene lo spazio interno dello stato precedente, causando uno spazio interno fantasma (ad esempio, mantenendo lo spazio interno della tastiera dopo che è stata nascosta).
Utilizza invece WindowInsetsCompat.Builder per impostare i tipi gestiti su
zero prima di passare l'oggetto alle visualizzazioni secondarie. In questo modo, la webview viene informata che
questi specifici margini interni sono già stati presi in considerazione durante l'attivazione della
notifica per continuare nella gerarchia delle visualizzazioni.
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, windowInsets ->
// 1. Identify the inset types you want to handle natively
val types = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
// 2. Extract the dimensions and apply them as padding to the native container
val insets = windowInsets.getInsets(types)
view.setPadding(insets.left, insets.top, insets.right, insets.bottom)
// 3. Return a new WindowInsets object with the handled types set to NONE (zeroed).
// This informs the WebView that these areas are already padded, preventing
// double-padding while still allowing the WebView to update its internal state.
WindowInsetsCompat.Builder(windowInsets)
.setInsets(types, Insets.NONE)
.build()
}
Java
ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, windowInsets) -> {
// 1. Identify the inset types you want to handle natively
int types = WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout();
// 2. Extract the dimensions and apply them as padding to the native container
Insets insets = windowInsets.getInsets(types);
rootView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
// 3. Return a new Insets object with the handled types set to NONE (zeroed).
// This informs the WebView that these areas are already padded, preventing
// double-padding while still allowing the WebView to update its internal
// state.
return new WindowInsetsCompat.Builder(windowInsets)
.setInsets(types, Insets.NONE)
.build();
});
Come annullare la partecipazione
Per disattivare questi comportamenti moderni e tornare alla gestione legacy della finestra, segui questi passaggi:
Intercept insets:utilizza
setOnApplyWindowInsetsListenero esegui l'override dionApplyWindowInsetsin una sottoclasseWebView.Cancella rientri:restituisce un insieme di rientri utilizzati (ad esempio,
WindowInsetsCompat.CONSUMED) dall'inizio. Questa azione impedisce la propagazione della notifica all'interno di WebView, disattivando di fatto il ridimensionamento moderno del viewport e forzando WebView a mantenere le dimensioni iniziali del viewport visivo.