WebView gère l'alignement du contenu à l'aide de deux fenêtres d'affichage : la fenêtre d'affichage de la mise en page (taille de la page) et la fenêtre d'affichage visuelle (partie de la page que l'utilisateur voit réellement). Alors que la fenêtre d'affichage de mise en page est généralement statique, la fenêtre d'affichage visuelle change de manière dynamique lorsque les utilisateurs effectuent un zoom, font défiler la page ou lorsque des éléments d'interface utilisateur système (comme le clavier virtuel) s'affichent.
Compatibilité des fonctionnalités
La compatibilité de WebView avec les encarts de fenêtre a évolué au fil du temps pour aligner le comportement du contenu Web sur les attentes des applications Android natives :
| Jalon | Fonctionnalité ajoutée | Champ d'application |
|---|---|---|
| M136 | Compatibilité avec displayCutout() et systemBars() via les marges intérieures de la zone de sécurité CSS. |
Éléments WebView en plein écran uniquement. |
| M139 | Compatibilité avec l'ime() (éditeur de méthode de saisie, qui est un clavier) via le redimensionnement de la fenêtre d'affichage visuelle. |
Tous les composants WebView. |
| M144 | Disponibilité de displayCutout() et systemBars() |
Toutes les WebViews (quel que soit l'état du plein écran). |
Pour en savoir plus, consultez la page WindowInsetsCompat.
Mécaniques de base
WebView gère les encarts via deux mécanismes principaux :
Zones de sécurité (
displayCutout,systemBars) : WebView transmet ces dimensions au contenu Web via les variables CSS safe-area-inset-*. Cela permet aux développeurs d'éviter que leurs propres éléments interactifs (comme les barres de navigation) soient masqués par des encoches ou des barres d'état.Redimensionnement de la fenêtre d'affichage visuelle à l'aide de l'éditeur de mode de saisie (IME) : à partir de la version M139, l'éditeur de mode de saisie (IME) redimensionne directement la fenêtre d'affichage visuelle. Ce mécanisme de redimensionnement est également basé sur l'intersection WebView-Window. Par exemple, en mode multitâche Android, si le bas d'une WebView s'étend de 200 dp sous le bas de la fenêtre, la fenêtre d'affichage visuelle est 200 dp plus petite que la taille de la WebView. Ce redimensionnement de la fenêtre d'affichage visuelle (pour l'IME et l'intersection WebView-Window) n'est appliqué qu'au bas de la WebView. Ce mécanisme n'est pas compatible avec le redimensionnement pour les chevauchements à gauche, à droite ou en haut. Cela signifie que les claviers ancrés qui apparaissent sur ces bords ne déclenchent pas de redimensionnement de la fenêtre d'affichage visuelle.
Auparavant, la fenêtre d'affichage visuelle restait fixe, ce qui masquait souvent les champs de saisie derrière le clavier. En redimensionnant la fenêtre d'affichage, la partie visible de la page devient défilable par défaut, ce qui permet aux utilisateurs d'accéder au contenu masqué.
Logique de limites et de chevauchement
WebView ne doit recevoir des valeurs d'encart non nulles que lorsque les éléments d'interface utilisateur du système (barres, encoches ou clavier) chevauchent directement les limites de l'écran de WebView. Si un WebView ne chevauche pas ces éléments d'interface utilisateur (par exemple, s'il est centré sur l'écran et ne touche pas les barres système), il doit recevoir ces encarts avec une valeur de zéro.
Pour ignorer cette logique par défaut et fournir au contenu Web les dimensions complètes du système, quel que soit le chevauchement, utilisez la méthode setOnApplyWindowInsetsListener et renvoyez l'objet windowInsets d'origine non modifié de l'écouteur. Fournir les dimensions complètes du système peut aider à garantir la cohérence de la conception en permettant au contenu Web de s'aligner sur le matériel de l'appareil, quelle que soit la position actuelle de la WebView. Cela garantit une transition fluide lorsque la WebView se déplace ou s'agrandit pour toucher les bords de l'écran.
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; });
Gérer les événements de redimensionnement
Étant donné que la visibilité du clavier déclenche désormais un redimensionnement de la fenêtre d'affichage visuelle, le code Web peut voir des événements de redimensionnement plus fréquents. Les développeurs doivent s'assurer que leur code ne réagit pas à ces événements de redimensionnement en effaçant la sélection de l'élément. Cela crée une boucle de perte de sélection et de fermeture du clavier qui empêche la saisie de l'utilisateur :
- L'utilisateur sélectionne un élément d'entrée.
- Le clavier s'affiche, ce qui déclenche un événement de redimensionnement.
- Le code du site Web supprime le focus en réponse au redimensionnement.
- Le clavier est masqué, car le curseur n'est plus dans le champ de texte.
Pour atténuer ce comportement, examinez les écouteurs côté Web afin de vous assurer que les modifications de la fenêtre d'affichage ne déclenchent pas involontairement la fonction JavaScript blur() ou les comportements de suppression de la sélection.
Implémenter la gestion des encarts
Les paramètres par défaut de WebView fonctionnent automatiquement pour la plupart des applications. Toutefois, si votre application utilise des mises en page personnalisées (par exemple, si vous ajoutez votre propre marge intérieure pour tenir compte de la barre d'état ou du clavier), vous pouvez utiliser les approches suivantes pour améliorer la façon dont le contenu Web et l'UI native fonctionnent ensemble. Si votre UI native applique une marge intérieure à un conteneur en fonction de WindowInsets, vous devez gérer correctement ces encarts avant qu'ils n'atteignent la WebView pour éviter une double marge intérieure.
Le double padding est une situation dans laquelle la mise en page native et le contenu Web appliquent les mêmes dimensions d'encart, ce qui entraîne un espacement redondant. Par exemple, imaginez un téléphone avec une barre d'état de 40 px. La vue native et la WebView voient l'encart de 40 px. Les deux ajoutent 40 px de marge intérieure, ce qui entraîne un espace de 80 px en haut.
L'approche de mise à zéro
Pour éviter un double remplissage, vous devez vous assurer qu'après qu'une vue native a utilisé une dimension d'encart pour le remplissage, vous réinitialisez cette dimension à zéro à l'aide de Insets.NONE sur un nouvel objet WindowInsets avant de transmettre l'objet modifié dans la hiérarchie des vues à la WebView.
Lorsque vous appliquez une marge intérieure à une vue parente, vous devez généralement utiliser l'approche de mise à zéro en définissant Insets.NONE au lieu de WindowInsetsCompat.CONSUMED.
Le renvoi de WindowInsetsCompat.CONSUMED peut fonctionner dans certaines situations.
Toutefois, des problèmes peuvent survenir si le gestionnaire de votre application modifie les encarts ou ajoute sa propre marge intérieure. L'approche de mise à zéro ne présente pas ces limites.
Éviter le remplissage fantôme en mettant à zéro les encarts
Si vous consommez les encarts alors que l'application a déjà transmis des encarts non consommés, ou si les encarts changent (par exemple, si le clavier est masqué), leur consommation empêche WebView de recevoir la notification de mise à jour nécessaire. Cela peut entraîner la conservation d'un padding fantôme d'un état précédent (par exemple, conserver le padding du clavier après que celui-ci a été masqué).
L'exemple suivant montre une interaction défectueuse entre l'application et WebView :
- État initial : l'application transmet initialement des encarts non consommés (par exemple,
displayCutout()ousystemBars()) à la WebView, qui applique en interne une marge intérieure au contenu Web. - Changement d'état et erreur : si l'application change d'état (par exemple, le clavier se masque) et qu'elle choisit de gérer les encarts obtenus en renvoyant
WindowInsetsCompat.CONSUMED. - Notification bloquée : l'utilisation des encarts empêche le système Android d'envoyer la notification de mise à jour nécessaire à la WebView via la hiérarchie des vues.
- Marge intérieure fantôme : comme WebView ne reçoit pas la mise à jour, elle conserve la marge intérieure de l'état précédent, ce qui entraîne une marge intérieure fantôme (par exemple, en conservant la marge intérieure du clavier après que le clavier a été masqué).
Utilisez plutôt WindowInsetsCompat.Builder pour définir les types gérés sur zéro avant de transmettre l'objet aux vues enfants. Cela informe la WebView que ces encarts spécifiques ont déjà été pris en compte, tout en permettant à la notification de continuer à descendre dans la hiérarchie des vues.
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(); });
Désactiver cette fonctionnalité
Pour désactiver ces comportements modernes et revenir à l'ancien traitement de la fenêtre d'affichage, procédez comme suit :
Intercepter les encarts : utilisez
setOnApplyWindowInsetsListenerou remplacezonApplyWindowInsetsdans une sous-classeWebView.Effacer les encarts : renvoie un ensemble d'encarts consommés (par exemple,
WindowInsetsCompat.CONSUMED) dès le début. Cette action empêche la notification d'encart de se propager à la WebView, ce qui désactive le redimensionnement moderne de la fenêtre d'affichage et force la WebView à conserver sa taille de fenêtre d'affichage visuelle initiale.