WebView verwaltet die Ausrichtung von Inhalten mithilfe von zwei Darstellungsbereichen: dem Layout-Darstellungsbereich (der Seitengröße) und dem visuellen Darstellungsbereich (dem Teil der Seite, den der Nutzer tatsächlich sieht). Der Layout-Darstellungsbereich ist in der Regel statisch, der visuelle Darstellungsbereich ändert sich jedoch dynamisch, wenn Nutzer zoomen, scrollen oder wenn System-UI-Elemente (z. B. die Softwaretastatur) angezeigt werden.
Funktionskompatibilität
Die WebView-Unterstützung für Fenstereinblendungen hat sich im Laufe der Zeit weiterentwickelt, um das Verhalten von Webinhalten an die Erwartungen nativer Android-Apps anzupassen:
| Meilenstein | Funktion hinzugefügt | Umfang |
|---|---|---|
| M136 | displayCutout() und systemBars() werden über CSS-Safe-Area-Insets unterstützt. |
Nur Vollbild-WebViews. |
| M139 | ime() (Input Method Editor, eine Tastatur) durch das Anpassen der Größe des visuellen Darstellungsbereichs. |
Alle WebViews. |
| M144 | displayCutout() und systemBars() unterstützen. |
Alle WebViews (unabhängig vom Vollbildmodus). |
Weitere Informationen finden Sie unter WindowInsetsCompat.
Kernmechaniken
WebView verarbeitet Insets über zwei primäre Mechanismen:
Sichere Bereiche (
displayCutout,systemBars): WebView leitet diese Dimensionen über CSS-Variablen „safe-area-inset-*“ an Webinhalte weiter. So können Entwickler verhindern, dass ihre eigenen interaktiven Elemente (z. B. Navigationsleisten) durch Aussparungen oder Statusleisten verdeckt werden.Visuelle Größenanpassung des Darstellungsbereichs über den Eingabemethoden-Editor (IME): Ab M139 wird der visuelle Darstellungsbereich direkt über den Eingabemethoden-Editor (IME) angepasst. Dieser Mechanismus zur Größenanpassung basiert ebenfalls auf der Überschneidung des WebView-Fensters. Wenn beispielsweise im Android-Multitasking-Modus der untere Rand eines WebView 200 dp unter dem unteren Rand des Fensters liegt, ist der visuelle Viewport 200 dp kleiner als die Größe des WebView. Die Größenanpassung des visuellen Darstellungsbereichs (sowohl für die IME- als auch für die WebView-Fensterschnittmenge) wird nur auf den unteren Rand der WebView angewendet. Bei diesem Mechanismus wird die Größe nicht für Überlappungen links, rechts oder oben angepasst. Das bedeutet, dass angedockte Tastaturen, die an diesen Rändern angezeigt werden, keine visuelle Größenänderung des Darstellungsbereichs auslösen.
Bisher blieb der visuelle Darstellungsbereich unverändert, sodass Eingabefelder oft hinter der Tastatur verborgen waren. Durch die Anpassung der Größe des Darstellungsbereichs wird der sichtbare Teil der Seite standardmäßig scrollbar, sodass Nutzer auf verdeckte Inhalte zugreifen können.
Grenzen und Überlappungslogik
WebView sollte nur dann Inset-Werte ungleich null erhalten, wenn System-UI-Elemente (Leisten, Displayausschnitte oder die Tastatur) direkt mit den Bildschirmgrenzen von WebView überlappen. Wenn sich ein WebView nicht mit diesen UI-Elementen überschneidet (z. B. wenn ein WebView auf dem Bildschirm zentriert ist und die Systemleisten nicht berührt), sollten die Insets den Wert 0 haben.
Wenn Sie diese Standardlogik überschreiben und die Web-Inhalte mit den vollständigen Systemabmessungen unabhängig von Überschneidungen bereitstellen möchten, verwenden Sie die Methode setOnApplyWindowInsetsListener und geben Sie das ursprüngliche, unveränderte windowInsets-Objekt vom Listener zurück. Wenn Sie vollständige Systemabmessungen angeben, können Sie für Designkonsistenz sorgen, da Webinhalte unabhängig von der aktuellen Position der WebView an die Gerätehardware angepasst werden können. So wird ein reibungsloser Übergang gewährleistet, wenn sich die WebView bewegt oder ausgedehnt wird, um die Bildschirmränder zu berühren.
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;
});
Größenänderungsereignisse verwalten
Da die Sichtbarkeit der Tastatur jetzt eine Größenanpassung des visuellen Darstellungsbereichs auslöst, werden im Webcode möglicherweise häufiger Ereignisse zur Größenanpassung angezeigt. Entwickler müssen dafür sorgen, dass ihr Code nicht auf diese Ereignisse reagiert, indem er den Fokus des Elements löscht. Dadurch entsteht eine Schleife aus Fokusverlust und Schließen der Tastatur, die die Eingabe durch den Nutzer verhindert:
- Der Nutzer konzentriert sich auf ein Eingabeelement.
- Die Tastatur wird angezeigt und löst ein Ereignis zum Ändern der Größe aus.
- Der Code der Website entfernt den Fokus als Reaktion auf die Größenänderung.
- Die Tastatur wird ausgeblendet, weil der Fokus verloren gegangen ist.
Um dieses Verhalten zu minimieren, sollten Sie die Listener auf der Webseite überprüfen, damit Änderungen des Viewports nicht unbeabsichtigt die JavaScript-Funktion blur() oder das Entfernen des Fokus auslösen.
Inset-Handling implementieren
Die Standardeinstellungen von WebView funktionieren automatisch für die meisten Apps. Wenn Ihre App jedoch benutzerdefinierte Layouts verwendet (z. B. wenn Sie Ihr eigenes Padding hinzufügen, um die Statusleiste oder Tastatur zu berücksichtigen), können Sie die folgenden Ansätze verwenden, um die Zusammenarbeit von Webinhalten und nativer Benutzeroberfläche zu verbessern. Wenn in Ihrer nativen Benutzeroberfläche für einen Container basierend auf WindowInsets ein Innenabstand angewendet wird, müssen Sie diese Insets richtig verwalten, bevor sie die WebView erreichen, um einen doppelten Innenabstand zu vermeiden.
Doppeltes Padding tritt auf, wenn das native Layout und die Webinhalte dieselben Einrückungsdimensionen verwenden, was zu redundantem Abstand führt. Stellen Sie sich beispielsweise ein Smartphone mit einer 40 px hohen Statusleiste vor. Sowohl die native Ansicht als auch die WebView sehen den 40 px großen Einzug. Beide fügen 40 Pixel Padding hinzu, sodass der Nutzer oben einen Abstand von 80 Pixeln sieht.
Der Zeroing-Ansatz
Um doppeltes Padding zu vermeiden, müssen Sie dafür sorgen, dass Sie nach der Verwendung einer Inset-Dimension für das Padding in einer nativen Ansicht diese Dimension mit Insets.NONE für ein neues WindowInsets-Objekt auf null zurücksetzen, bevor Sie das geänderte Objekt in der Ansichtshierarchie an die WebView übergeben.
Wenn Sie Padding auf eine übergeordnete Ansicht anwenden, sollten Sie in der Regel den Zeroing-Ansatz verwenden, indem Sie Insets.NONE anstelle von WindowInsetsCompat.CONSUMED festlegen.
Die Rückgabe von WindowInsetsCompat.CONSUMED ist unter Umständen möglich.
Es kann jedoch zu Problemen kommen, wenn der Handler Ihrer App Insets ändert oder eigenes Padding hinzufügt. Beim Ansatz mit dem Wert 0 gibt es diese Einschränkungen nicht.
Ghost-Padding vermeiden, indem Insets auf null gesetzt werden
Wenn Sie die Insets verwenden, nachdem die App zuvor nicht verwendete Insets übergeben hat, oder wenn sich die Insets ändern (z. B. wenn die Tastatur ausgeblendet wird), wird durch die Verwendung verhindert, dass die WebView die erforderliche Aktualisierungsbenachrichtigung erhält. Dadurch kann es passieren, dass im WebView ein Ghost-Padding aus einem vorherigen Zustand beibehalten wird, z. B. ein Tastatur-Padding, nachdem die Tastatur ausgeblendet wurde.
Das folgende Beispiel zeigt eine fehlerhafte Interaktion zwischen der App und WebView:
- Anfangszustand:Die App übergibt zuerst nicht verarbeitete Insets (z. B.
displayCutout()odersystemBars()) an das WebView, das intern Padding auf die Webinhalte anwendet. - Statusänderung und Fehler:Wenn sich der Status der App ändert (z. B. wird die Tastatur ausgeblendet) und die App die resultierenden Insets durch Zurückgeben von
WindowInsetsCompat.CONSUMEDverarbeitet. - Benachrichtigung blockiert:Wenn die Insets verwendet werden, kann das Android-System die erforderliche Updatebenachrichtigung nicht über die Ansichtshierarchie an die WebView senden.
- Ghost-Padding:Da die WebView das Update nicht erhält, behält sie das Padding aus dem vorherigen Zustand bei. Das führt zu Ghost-Padding, z. B. wenn das Tastatur-Padding beibehalten wird, nachdem die Tastatur ausgeblendet wurde.
Verwenden Sie stattdessen WindowInsetsCompat.Builder, um die verarbeiteten Typen auf null zu setzen, bevor Sie das Objekt an die untergeordneten Ansichten übergeben. Dadurch wird WebView informiert, dass diese spezifischen Insets bereits berücksichtigt wurden, sodass die Benachrichtigung in der Ansichtshierarchie weitergeleitet werden kann.
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();
});
Teilnahme am Programm beenden
So deaktivieren Sie diese modernen Verhaltensweisen und kehren zur alten Viewport-Verarbeitung zurück:
Insets abfangen:Verwenden Sie
setOnApplyWindowInsetsListeneroder überschreiben SieonApplyWindowInsetsin einerWebView-Unterklasse.Leere Insets:Gibt von Anfang an einen verbrauchten Satz von Insets zurück (z. B.
WindowInsetsCompat.CONSUMED). Diese Aktion verhindert, dass die Inset-Benachrichtigung an die WebView weitergegeben wird. Dadurch wird die moderne Größenanpassung des Viewports deaktiviert und die WebView behält ihre ursprüngliche Größe des visuellen Viewports bei.