WebView gestisce l'allineamento dei contenuti utilizzando due aree visibili: l'area visibile del layout (le dimensioni 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 in modo dinamico quando gli utenti aumentano lo zoom, scorrono o quando vengono visualizzati elementi UI di sistema (come la tastiera su schermo).
Compatibilità delle funzionalità
Il supporto di WebView per gli inset delle finestre si è evoluto nel tempo per allineare il comportamento dei contenuti web alle aspettative delle app per Android native:
| Traguardo | Funzionalità aggiunta | Ambito |
|---|---|---|
| M136 | Supporto di displayCutout() e systemBars() tramite safe-area-insets CSS. |
Solo WebView a schermo intero. |
| M139 | Supporto di ime() (editor del metodo di immissione, ovvero una tastiera) tramite il ridimensionamento dell'area visibile. |
Tutte le WebView. |
| M144 | Supporto di 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 evitare che i propri elementi interattivi (come le barre di navigazione) vengano oscurati da notch o barre di stato.Ridimensionamento della visual viewport utilizzando l'editor del metodo di immissione (IME): a partire da M139, l'editor del metodo di immissione (IME) ridimensiona direttamente la visual viewport. Questo meccanismo di ridimensionamento si basa anche sull'intersezione WebView-finestra. Ad esempio, in modalità multitasking di Android, se la parte inferiore di una WebView si estende di 200 dp sotto la parte inferiore della finestra, l'area visibile è di 200 dp più piccola rispetto alle dimensioni della WebView. Questo ridimensionamento della visual viewport (sia per l'IME che per l'intersezione WebView-finestra) viene applicato solo alla parte inferiore della WebView. Questo meccanismo non supporta il ridimensionamento per la sovrapposizione a sinistra, a destra o in alto. Ciò significa che le tastiere ancorate visualizzate su questi bordi non attivano un ridimensionamento dell'area visibile.
In precedenza, l'area visibile rimaneva fissa, spesso nascondendo i campi di immissione dietro la tastiera. Ridimensionando l'area visibile, la parte visibile della pagina diventa scorrevole per impostazione predefinita, garantendo agli utenti la possibilità di raggiungere i contenuti oscurati.
Logica di limiti e sovrapposizione
WebView deve ricevere valori di inset diversi da zero solo quando gli elementi UI di sistema (barre, ritagli display o tastiera) si sovrappongono direttamente ai limiti dello schermo di WebView. Se un componente WebView non si sovrappone a questi elementi UI (ad esempio, se un WebView è centrato sullo schermo e non tocca le barre di sistema), dovrebbe ricevere questi inset come zero.
Per eseguire l'override di questa logica predefinita e fornire ai contenuti web le dimensioni complete del sistema indipendentemente dalla sovrapposizione, utilizza il metodo setOnApplyWindowInsetsListener e restituisci l'oggetto windowInsets originale non modificato dal listener. Fornire le dimensioni complete del sistema può contribuire a garantire la coerenza del design, consentendo ai contenuti web di allinearsi all'hardware del dispositivo indipendentemente dalla posizione corrente 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; });
Gestisci gli eventi di ridimensionamento
Poiché la visibilità della tastiera ora attiva un ridimensionamento dell'area visibile, il codice web potrebbe rilevare eventi di ridimensionamento più frequenti. Gli sviluppatori devono assicurarsi che il loro codice non reagisca a questi eventi di ridimensionamento cancellando lo stato attivo dell'elemento. In questo modo si crea un loop di perdita dello stato attivo 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é lo stato attivo è stato perso.
Per mitigare questo comportamento, esamina i listener lato web per assicurarti che le modifiche alla viewport non attivino involontariamente la funzione JavaScript blur() o i comportamenti di cancellazione dello stato attivo.
Implementa 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 il padding per tenere conto della barra di stato o della tastiera), puoi utilizzare i seguenti approcci per migliorare l'interazione tra i contenuti web e l'UI nativa. Se la UI nativa applica il padding a un container in base a WindowInsets, devi gestire questi inset correttamente prima che raggiungano WebView per evitare il doppio padding.
Il doppio padding è una situazione in cui il layout nativo e i contenuti web applicano le stesse dimensioni di inset, con conseguente spaziatura ridondante. Ad esempio, immagina uno smartphone con una barra di stato di 40 px. Sia la visualizzazione nativa sia la WebView vedono l'inset di 40 px. Entrambi aggiungono 40 px di spaziatura interna, per cui l'utente vede uno spazio di 80 px nella parte superiore.
L'approccio Zeroing
Per evitare il doppio padding, devi assicurarti che, dopo che una visualizzazione nativa utilizza una dimensione di inset per il padding, questa dimensione venga reimpostata su zero utilizzando Insets.NONE su un nuovo oggetto WindowInsets prima di passare l'oggetto modificato nella gerarchia di oggetti View alla WebView.
Quando applichi il padding a una vista genitore, 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 padding. L'approccio di azzeramento non presenta queste limitazioni.
Evita il padding fantasma azzerando gli inset
Se utilizzi gli inset quando l'app ha precedentemente passato inset non utilizzati o se gli inset cambiano (ad esempio, la tastiera viene nascosta), l'utilizzo degli inset impedisce alla WebView di ricevere la notifica di aggiornamento necessaria. Ciò può comportare la conservazione del padding fantasma da uno stato precedente (ad esempio, mantenendo il padding della tastiera dopo che questa è stata nascosta).
L'esempio seguente mostra un'interazione non funzionante tra l'app e WebView:
- Stato iniziale: l'app inizialmente passa gli inset non consumati (ad esempio
displayCutout()osystemBars()) alla WebView, che applica internamente il padding ai contenuti web. - Cambio di 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 di oggetti View alla WebView.
- Padding fantasma:poiché WebView non riceve l'aggiornamento, mantiene il padding dello stato precedente, causando un padding fantasma (ad esempio, mantenendo il padding della tastiera dopo che questa è stata nascosta).
Utilizza invece WindowInsetsCompat.Builder per impostare i tipi gestiti su zero prima di passare l'oggetto alle viste bambino. In questo modo, WebView viene informato che
questi inset specifici sono già stati presi in considerazione, consentendo al contempo alla
notifica di continuare nella gerarchia di oggetti View.
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 effettuare la disattivazione
Per disattivare questi comportamenti moderni e tornare alla gestione della visualizzazione precedente, segui questi passaggi:
Intercetta gli inset:utilizza
setOnApplyWindowInsetsListenero esegui l'override dionApplyWindowInsetsin una sottoclasseWebView.Cancella gli inset:restituisci un insieme di inset consumati (ad esempio
WindowInsetsCompat.CONSUMED) dall'inizio. Questa azione impedisce del tutto la propagazione della notifica di inset alla WebView, disattivando di fatto il ridimensionamento moderno della viewport e forzando la WebView a mantenere le dimensioni iniziali della viewport visiva.