WebView управляет выравниванием контента с помощью двух областей просмотра: области макета (размер страницы) и визуальной области просмотра (часть страницы, которую видит пользователь). В то время как область макета обычно статична, визуальная область просмотра динамически изменяется при масштабировании, прокрутке или появлении элементов системного пользовательского интерфейса (например, программной клавиатуры).
Совместимость функций
Поддержка отступов окон в WebView развивалась с течением времени, чтобы привести поведение веб-контента в соответствие с ожиданиями нативных приложений Android:
| Важный этап | Добавлена новая функция. | Объем |
|---|---|---|
| М136 | displayCutout() и systemBars() поддерживаются с помощью CSS-свойств safe-area-insets. | Только для полноэкранных веб-просмотров. |
| М139 | Поддержка функции ime() (редактор методов ввода, представляющий собой клавиатуру) посредством визуального изменения размера области просмотра. | Все WebViews. |
| М144 | Поддержка displayCutout() и systemBars() . | Все WebView (независимо от полноэкранного режима). |
Для получения более подробной информации см. WindowInsetsCompat .
Основные механики
WebView обрабатывает отступы с помощью двух основных механизмов:
Безопасные области (
displayCutout,systemBars): WebView передает эти размеры веб-контенту через переменные CSS safe-area-inset-*. Это позволяет разработчикам предотвратить перекрытие собственных интерактивных элементов (например, панелей навигации) вырезами или строками состояния.Изменение визуального размера области просмотра с помощью редактора методов ввода (IME): Начиная с M139, редактор методов ввода (IME) напрямую изменяет визуальный размер области просмотра. Этот механизм изменения размера также основан на пересечении WebView и Window. Например, в режиме многозадачности Android, если нижняя часть WebView выходит на 200dp ниже нижней части окна, визуальная область просмотра становится на 200dp меньше размера WebView. Это изменение визуального размера области просмотра (как для IME, так и для пересечения WebView и Window) применяется только к нижней части WebView. Этот механизм не поддерживает изменение размера для левого, правого или верхнего перекрытия. Это означает, что закрепленные клавиатуры, появляющиеся на этих краях, не вызывают изменения визуального размера области просмотра.
Ранее область просмотра оставалась фиксированной, часто скрывая поля ввода за клавиатурой. Изменение размера области просмотра позволяет по умолчанию прокручивать видимую часть страницы, обеспечивая пользователям доступ к скрытому содержимому.
Логика границ и перекрытий
WebView должен получать ненулевые значения отступов только в том случае, если элементы системного пользовательского интерфейса (панели, вырезы на экране или клавиатура) непосредственно перекрывают границы экрана WebView. Если WebView не перекрывает эти элементы пользовательского интерфейса (например, если WebView центрирован на экране и не касается панелей), он должен получать эти значения отступов равными нулю.
Чтобы переопределить эту логику по умолчанию и предоставить веб-контент с полными системными размерами независимо от перекрытия, используйте метод setOnApplyWindowInsetsListener и верните исходный, неизмененный объект windowInsets из слушателя. Предоставление полных системных размеров может помочь обеспечить согласованность дизайна, позволяя веб-контенту выравниваться с аппаратным обеспечением устройства независимо от текущего положения WebView. Это обеспечивает плавный переход при перемещении или расширении WebView до краев экрана.
Котлин
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;
});
Управление событиями изменения размера
Поскольку теперь видимость клавиатуры вызывает визуальное изменение размера области просмотра, веб-код может чаще сталкиваться с событиями изменения размера. Разработчики должны убедиться, что их код не реагирует на эти события изменения размера, сбрасывая фокус элемента. Это создаст цикл потери фокуса и закрытия клавиатуры, что препятствует вводу данных пользователем.
- Пользователь фокусируется на элементе ввода.
- Появляется клавиатура, что запускает событие изменения размера.
- Код веб-сайта сбрасывает фокус в ответ на изменение размера окна.
- Клавиатура скрывается, потому что фокус был потерян.
Чтобы смягчить это поведение, проверьте обработчики событий на стороне веб-интерфейса, чтобы убедиться, что изменения области просмотра непреднамеренно не запускают функцию JavaScript blur() или процессы очистки фокуса.
Реализуйте обработку вставок.
Настройки WebView по умолчанию работают автоматически для большинства приложений. Однако, если ваше приложение использует пользовательские макеты (например, если вы добавляете собственные отступы для учета строки состояния или клавиатуры), вы можете использовать следующие подходы для улучшения взаимодействия веб-контента и нативного пользовательского интерфейса. Если ваш нативный пользовательский интерфейс применяет отступы к контейнеру на основе WindowInsets , необходимо правильно управлять этими отступами до того, как они достигнут WebView, чтобы избежать двойного отступа.
Двойной отступ — это ситуация, когда исходный макет и веб-контент применяют одинаковые размеры отступа, что приводит к избыточному пространству. Например, представьте себе телефон со строкой состояния шириной 40 пикселей. И исходный макет, и WebView видят отступ в 40 пикселей. Оба добавляют по 40 пикселей отступа, в результате чего пользователь видит зазор в 80 пикселей сверху.
Метод обнуления
Чтобы предотвратить двойное отступление, необходимо убедиться, что после того, как нативное представление использует размер отступа для заполнения, этот размер обнуляется с помощью Insets.NONE в новом объекте WindowInsets прежде чем передавать измененный объект вниз по иерархии представлений в WebView.
При применении отступов к родительскому представлению обычно следует использовать подход с обнулением, устанавливая Insets.NONE вместо WindowInsetsCompat.CONSUMED . Возврат WindowInsetsCompat.CONSUMED может сработать в определенных ситуациях. Однако это может вызвать проблемы, если обработчик вашего приложения изменяет отступы или добавляет свои собственные отступы. Подход с обнулением не имеет этих ограничений.
Избегайте появления остаточных отступов, обнуляя значения вступов.
Если вы используете отступы, когда приложение ранее уже использовало неиспользованные отступы, или если отступы изменяются (например, при скрытии клавиатуры), их использование препятствует получению WebView необходимого уведомления об обновлении. Это может привести к тому, что WebView сохранит остаточные отступы из предыдущего состояния (например, отступы клавиатуры после ее скрытия).
Следующий пример демонстрирует некорректное взаимодействие между приложением и WebView:
- Начальное состояние: приложение изначально передает неиспользуемые отступы (например,
displayCutout()илиsystemBars()) в WebView, который внутренне применяет отступы к веб-контенту. - Изменение состояния и ошибка: Если приложение изменяет состояние (например, клавиатура скрывается) и решает обработать возникшие в результате этого отступы, возвращая
WindowInsetsCompat.CONSUMED. - Уведомление заблокировано: использование вложенных элементов препятствует отправке системой Android необходимого уведомления об обновлении вниз по иерархии представлений в WebView.
- «Призрачное» отступление: Поскольку WebView не получает обновления, он сохраняет отступы из предыдущего состояния, что приводит к «призрачному» отступу (например, сохранение отступов от клавиатуры после того, как клавиатура скрыта).
Вместо этого используйте WindowInsetsCompat.Builder , чтобы установить обрабатываемые типы в ноль перед передачей объекта дочерним представлениям. Это сообщит WebView, что эти конкретные отступы уже учтены, и позволит уведомлению продолжить отображение вниз по иерархии представлений.
Котлин
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();
});
Как отказаться от участия
Чтобы отключить эти современные функции и вернуться к устаревшему способу обработки области просмотра, выполните следующие действия:
Перехват вставок: используйте
setOnApplyWindowInsetsListenerили переопределитеonApplyWindowInsetsв подклассеWebView.Очистить отступы: Возвращает использованный набор отступов (например,
WindowInsetsCompat.CONSUMED) с самого начала. Это действие предотвращает распространение уведомления об отступах на WebView, фактически отключая современное изменение размера области просмотра и заставляя WebView сохранять свой первоначальный визуальный размер области просмотра.