Разрешение WebView на Android работать с prefers-color-scheme: dark

У меня есть приложение для Android, в котором используется веб-просмотр, и в последнее время я пытаюсь понять, как добавить темную тему с помощью нового синтаксиса @media (prefers-color-scheme: dark) CSS. У меня на странице написан правильный CSS, и если я открою его в Chrome с включенным темным режимом Chrome, он заработает. У меня также есть AppTheme, наследующий Theme.AppCompat.DayNight, и мое приложение показывает темное диалоговое окно загрузки и т. Д., Когда я включаю темный режим для всей ОС на своем устройстве. Даже параметры автозаполнения для <input> элементов становятся темными. Но все же веб-страница, загруженная с моим веб-просмотром, не становится темной. Согласно этой странице, веб-просмотры должен поддерживать эту функцию, но я просто не могу заставить ее работать. Что мне здесь не хватает?

Я также только что узнал, что в API 29 есть этот WebSettings.setForceDark() метод; это может быть то, что я ищу? Тем не менее, я надеюсь найти решение, которое будет работать с более низким уровнем API.

Кстати, мой текущий обходной путь - внедрить интерфейс JavaScript, подобный этому:

webView.addJavascriptInterface(new JSInterface(), "jsInterface");

...

public class JSInterface {
    @JavascriptInterface
    public boolean isNightMode() {
        int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
        return nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
    }
}

А затем на моей веб-странице вызовите метод jsInterface.isNightMode() и динамически загрузите другой файл CSS в зависимости от результата. Он, безусловно, работает и реагирует на глобальную настройку темного режима по своему усмотрению, но мне все еще интересно, смогу ли я заставить prefers-color-scheme работать.


person Mu-Tsun Tsai    schedule 11.08.2019    source источник


Ответы (4)


Android Webview обрабатывает дневной / ночной режим немного иначе, чем остальные виды. Установка темной темы изменит компоненты WebView (полоса прокрутки, кнопки масштабирования и т. Д.) На версию темного режима, но не изменит загруженный контент.

Чтобы изменить содержимое, вам необходимо использовать метод setForceDark в настройках веб-просмотра, чтобы он также изменил свое содержимое. Версию для совместимости этого метода можно найти в пакете AndroidX webkit.

Добавьте в свою сборку gradle следующую зависимость:

implementation 'androidx.webkit:webkit:1.3.0'

(1.3.0 - это минимально необходимая версия этого пакета. Но более высокие версии также должны работать.)

И добавьте следующие строки кода в инициализацию вашего веб-просмотра:

if(WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    WebSettingsCompat.setForceDark(myWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}

Проверка isFeatureSupported предназначена для того, чтобы убедиться, что Android System WebView версия, которую пользователь установил на свое устройство, поддерживает темный режим (поскольку он может быть обновлен или понижен независимо от версии Android через Google Play).

Примечание. Для использования функции setForceDark на работающем устройстве необходимо установить Android System WebView v76 или более поздней версии.

Функция принудительного затемнения контента веб-просмотра имеет две так называемые стратегии:

  • Затемнение пользовательского агента: веб-просмотр переведет свое содержимое в темный режим, автоматически инвертируя или затемняя цвета его содержимого.

  • Затемнение на основе темы: веб-просмотр перейдет в темный режим в соответствии с темой контента (включая запрос @media (prefers-color-scheme: dark)).

Чтобы установить, какую стратегию веб-просмотр должен использовать для применения силы темноты, вы можете использовать следующий код:

if(WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
    WebSettingsCompat.setForceDarkStrategy(myWebView.getSettings(), WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY);
}

Примечание. Для выбора стратегии на работающем устройстве должна быть установлена ​​система Android System WebView v83 или более поздняя. Версии WebView, которые поддерживают setForceDark, но не поддерживают выбор стратегии (с v76 по v81), будут использовать затемнение пользовательского агента.

Поддерживаемые варианты стратегии:

  • DARK_STRATEGY_USER_AGENT_DARKENING_ONLY: используйте только затемнение пользовательского агента и игнорируйте любые темы в контенте (по умолчанию)
  • DARK_STRATEGY_WEB_THEME_DARKENING_ONLY: используйте только темную тему самого содержания, чтобы перевести страницу в темный режим. Если контент не имеет темной темы, веб-просмотр не будет применять затемнение и покажет его как есть.
  • DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING: используйте темную тему самого содержания, чтобы перевести страницу в темный режим. Если контент не имеет темной темы, используйте затемнение пользовательского агента.

Как работает проверка Javascript для затемненных веб-страниц?

Вызов JavaScript window.matchMedia('(prefers-color-scheme: dark)') будет соответствовать как стратегии затемнения пользовательского агента, так и стратегии затемнения веб-темы.

У меня для веб-просмотра установлено значение FORCE_DARK_AUTO, и мое приложение работает в дневной ночной теме, но каким-то образом мое веб-представление не применяет темный режим автоматически в зависимости от темы моего приложения. Почему это происходит?

Это потому, что значение параметра FORCE_DARK_AUTO веб-просмотра не работает на основе тем (как указано в документация). Он проверяет наличие функции Android 10 Force Dark. (функция быстрого исправления темного режима для приложений. Она называется так же, но не имеет прямого отношения к WebView force dark).

Если вы не используете принудительное затемнение, а тему приложения для обработки темного режима (как рекомендуется), вам необходимо реализовать собственную проверку того, когда применять функцию принудительного затемнения веб-просмотра. Пример использования темы DayNight:

int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
   //Code to enable force dark using FORCE_DARK_ON and select force dark strategy 
}
person Leon Lucardie    schedule 06.05.2020
comment
Ваши полные знания и опыт по этому вопросу невероятны. Хотя ваш ответ касается пакета альфа-версии, он работает должным образом. - person Mu-Tsun Tsai; 13.05.2020
comment
@ Mu-TsunTsai Это из-за того, что некоторые функции (например, выбор стратегии) ​​стали доступны только в версии 1.3.0 пакета androidx webkit, который (вместе с упомянутой версией 83 Android System Webview) все еще находится в альфа / бета-версии. на момент написания. Я добавил примечания по этому поводу к ответу и соответствующим образом скорректирую ответ, как только пакеты станут стабильными. - person Leon Lucardie; 13.05.2020
comment
Я наткнулся на это, потому что с публичным выпуском версии 83 для Android System WebView наш предыдущий подход (webSettings.setForceDark(WebSettings.FORCE_DARK_ON);) перестал работать. Я заметил, что и setForceDark, и setForceDarkStrategy уже доступны в версии 1.2.0 androidx.webkit, но setForceDarkStrategy помечен как RestrictTo.Scope.LIBRARY_GROUP, поэтому его нельзя вызвать из пользовательского кода (и константы называются по-разному). Я могу обойти это ограничение с помощью аннотации @SuppressLint, и, похоже, это сработает, но я не уверен, насколько стабильно это будет - person derpirscher; 29.05.2020
comment
А что касается проверки версии: если я сделаю isFeatureSupported в v81 webView, он вернет true для setForceDark и false для setForceDarkStrategy. Если я сделаю то же самое на v83, он оба раза вернет true. Разве этого не должно быть достаточно вместо проверки конкретной версии компонента веб-просмотра? - person derpirscher; 29.05.2020
comment
@derpirscher Хотя v1.2.0 пакета webkit действительно имеет функции стратегии, они все еще были экспериментальными, поэтому они ограничены. Использование их в то время, когда они ограничены, приведет к проблемам или, иногда, к прямому отказу от установки приложения на устройствах, не отмеченных «userdebug». См .: Issueetracker.google.com/issues/148330092 и source.android.com/setup/develop/new-device - person Leon Lucardie; 30.05.2020
comment
@derpirscher Что касается проверки версии: когда я писал ответ, я все еще испытывал некоторые проблемы с isFeatureSupported проверкой для forceDarkStrategy (особенно на v82, которая так и не вышла из бета-версии). Теперь, когда версия v83 стабильна, проверка isFeatureSupported, похоже, работает нормально, я соответствующим образом скорректирую ответ. - person Leon Lucardie; 30.05.2020
comment
Спасибо за подробное описание. Кто-нибудь знает какие-нибудь простые сайты для тестирования этих стратегий? единственное, что я могу заставить работать, - это выбрать FORCE_DARK_ON, и это применимо ко всем страницам, независимо от стратегии. Я уже использую WebView 83, поэтому не уверен, что делаю не так. Мое приложение использует тему материала для темного режима, и сейчас я использую темный режим. Думаю, может быть, я тестирую не те сайты. - person casolorz; 06.07.2020
comment
@casolorz Если вы включили принудительное затемнение в веб-просмотре с помощью стратегии DARK_STRATEGY_WEB_THEME_DARKENING_ONLY, он должен загружать страницы, которые не поддерживают темный режим на основе темы, без применения затемнения. Возможно, вы тестируете сайты, которые поддерживают темный режим на основе тем? Вы можете протестировать такие сайты, как google.com, которые не поддерживают темный режим на основе тем (пока) afaik. Но самый последовательный способ проверить это - загрузить очень простой пользовательский HTML-код без какой-либо тематики и посмотреть, как разные стратегии справляются с этим. Например: <html><body>Test content</body></html>. - person Leon Lucardie; 07.07.2020
comment
Спасибо, похоже, это правильно. Для чего тогда FORCE_DARK_AUTO? Я предполагал, что это было, когда приложение или телефон уже находятся в темном режиме. - person casolorz; 07.07.2020
comment
@casolorz FORCE_DARK_AUTO включает принудительный темный режим веб-просмотра на основе принудительного темного режима его родительских представлений. Force Dark for views - это новая функция автоматического затемнения в Android 10, которая отключена при использовании темной темы приложения: developer.android.com/guide/topics/ui/look-and-feel/ - person Leon Lucardie; 08.07.2020
comment
Поэтому я установил WebView на FORCE_DARK_AUTO, запустил AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) и загрузил androidpolice.com, но не переключился в темный режим. Если я установлю FORCE_DARK_ON, то перейду в темный режим. Я что-то не понимаю? Я думаю, что идеальной ситуацией было бы иметь возможность установить MODE_NIGHT_FOLLOW_SYSTEM и FORCE_DARK_AUTO и иметь темный режим, таким образом, по умолчанию он будет делать то, что пользователь, вероятно, хочет, без изменения настроек, но не похоже, что это будет работать таким образом . - person casolorz; 09.07.2020
comment
@casolorz Чтобы немного прояснить: функция Force Dark для приложений / представлений Android и функция Force Dark для контента веб-просмотра - это две разные (но похожие) функции, которые имеют одно и то же имя. Эти функции обычно не связаны, за исключением параметра WebView FORCE_DARK_AUTO. Этот параметр гарантирует, что функция принудительного затемнения веб-просмотра включена всякий раз, когда включена функция принудительного включения темноты приложения / представления его родительских представлений. - person Leon Lucardie; 09.07.2020
comment
@casolorz Однако, поскольку вы используете тему DayNight или Dark, а не Force Dark, чтобы затемнить ваше приложение, флаг FORCE_DARK_AUTO не будет работать. Вам нужно будет использовать проверку ночного режима, как описано в ответе. Надеюсь, это изменится в будущем, но пока это единственный способ. - person Leon Lucardie; 09.07.2020
comment
Понятно, я не понимаю, что означает Force Dark для приложения, я думал, что это связано с DayNight и AppCompatDelegate.setDefaultNightMode. Мне нужно будет провести небольшое исследование относительно того, что это значит, чтобы увидеть, смогу ли я найти способ автоматически настроить это для моих пользователей темного режима. - person casolorz; 09.07.2020

Что я делаю, основываясь на вашем коде вопроса, так это принудительно в зависимости от текущего статуса:

int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
  webSettings.setForceDark(WebSettings.FORCE_DARK_ON);
}

Таким образом @media (prefers-color-scheme: dark) работает, но @media (prefers-color-scheme: light) по-прежнему не работает (пробовал использовать FORCE_DARK_OFF и FORCE_DARK_AUTO в другом)

person jcesarmobile    schedule 20.10.2019
comment
Я проверил веб-просмотр и проверил соответствие медиа-запроса с window.matchMedia("(prefers-color-scheme: ...)") (возможные значения: light, dark и no-preference). Когда режим изначально установлен на DARK_ON, он соответствует схеме dark. Любой другой режим соответствует схеме no-preference. Более того, как только я установил режим на DARK_OFF или DARK_AUTO, установка его на DARK_ON позже не будет правильно распространяться на веб-просмотр. Но работает наоборот (т.е. сначала DARK_ON, а затем DARK_OFF). - person derpirscher; 04.11.2019
comment
Чтобы быть более точным: значение, возвращаемое getForceDark(), всегда является ожидаемым значением (т. Е. 2, когда последняя операция установки была DARK_ON и 0, когда это было DARK_OFF). Но установка DARK_ON после установки DARK_OFF или DARK_AUTO больше не имеет видимого эффекта. - person derpirscher; 04.11.2019

Здесь есть два важных документа:

https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#force_dark

https://developer.android.com/reference/android/view/View.html#setForceDarkAllowed(boolean)

Ключевые моменты:

  1. WebView должен разрешать принудительную темноту, как и все его родители.
  2. Тема должна быть отмечена как светлая.

Это означает, что заставить поведение FORCE_DARK_AUTO работать в WebView немного сложно. Добавление следующего в тему AppCompat.DayNight действительно работает, но, возможно, это не лучшее решение, поскольку оно может применить силу Android к темным представлениям, отличным от WebView.

<item name="android:forceDarkAllowed">true</item>
<item name="android:isLightTheme">true</item>

Другой вариант - справиться с этим вручную, установив FORCE_DARK_ON / OFF на основе соответствующего изменения конфигурации.

Currentlly WebView не поддерживает схему prefers-color-scheme отчасти потому, что принудительное затемнение было реализовано до стандартизации предпочтительной цветовой схемы, а отчасти потому, что нет возможности разумно интегрировать это (без стилей) с принудительным затемнением. Цель состоит в том, чтобы предоставить возможность выбрать либо принудительно темную, либо предпочитаемую цветовую схему через androidx.webkit.

person Tobias Sargeant    schedule 05.11.2019
comment
Отлично. Это также работает для темы DayNight. Этот ответ должен быть отмечен как решенный! - person dphans; 08.11.2019

Я не думаю, что WebView пока поддерживает медиа-запрос CSS prefers-color-scheme.

Новый API для setForceDark имеет три состояния: включено, выключено или автоматически.

ВКЛ - ваш контент всегда будет темнее с каждым разом.

ВЫКЛ. - ваш контент всегда будет отображаться светлым.

АВТО - контент будет темнее, если тема вашего приложения темнее ИЛИ ОС устройства находится в темном режиме, потому что пользователь переключает переключатель уровня ОС или включен режим экономии заряда батареи.

Я считаю, что поддержка более старых версий Android, а также контроль над тем, использовать ли prefers-color-scheme вместо Force Dark WebView, скоро появится через AndroidX. Срок платежа в настоящее время неизвестен.

На данный момент я бы рекомендовал установить для WebView значение ForceDark Auto. Это будет работать на Android Q и выше.

Я хотел бы следить за примечаниями к выпуску AndroidX для остальной поддержки, которая вам требуется для устройств на Android P и ниже.

person Community    schedule 20.08.2019
comment
К сожалению, кажется, что AUTO всегда разрешает prefers-color-scheme: no-preference даже при компиляции с целью Android Q, работающей на Android Q. Таким образом, веб-просмотр не реагирует на переключение системы в темный режим, даже если собственное приложение делает . - person derpirscher; 05.11.2019
comment
Привет, дерпиршер. Я не уверен, что понимаю. Если вы настроили веб-просмотр на автоматический режим, он должен соответствовать настройке уровня ОС Android для темной темы. Итак, если вы переключите свое устройство Android на темную тему, тогда веб-просмотр должен сделать веб-контент темным. Разве это не так? - person cmd; 05.11.2019
comment
Для меня это не так. Я установил тему приложения на AppCompat.DayNight и вижу, что приложение следует настройкам ОС для темного режима. Но даже если я установил websettings.setForceDark(FORCE_DARK_AUTO), веб-просмотр не переходит в темный режим, когда я переключаю настройки ОС. См. Также мои комментарии к другому ответу в этой ветке. - person derpirscher; 05.11.2019
comment
Но, может быть, я что-то упускаю. Что я делаю в минимальном примере: 1) AppTheme = AppCompat.DayNight, 2) У меня values/colors.xml и values-night/colors.xml. 3) В onCreate поставил AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_FOLLOW_SYSTEM) и 4) поставил websettings.setForceDark(FORCE_DARK_AUTO). Я вижу приложение после переключателя темного / светлого режима (строка состояния меняет цвета), но не веб-просмотр. Он всегда показывает цвета, как определено в @media (prefers-color-scheme: no-preference), независимо от того, установлен ли темный или светлый режим. - person derpirscher; 05.11.2019
comment
@derpirscher, как вы можете проверить значение prefers-color-scheme в веб-просмотре? - person diAz; 11.03.2020
comment
@diAz У меня есть медиа-запросы для prefers-color-scheme dark light и no-preference с разными цветами, определенными в них, и посмотрите, какие цвета используются. - person derpirscher; 11.03.2020
comment
@diAz Вы также можете проверить JavaScript с помощью window.matchMedia . Например, window.matchMedia("(prefers-color-scheme: light)").matches вернет true, если prefers-color-scheme установлен в light - person derpirscher; 11.03.2020
comment
я ожидал это значение для Android webview ^^ - person diAz; 11.03.2020