【问题标题】:Letting WebView on Android work with prefers-color-scheme: dark让 Android 上的 WebView 使用 prefers-color-scheme: dark
【发布时间】:2019-12-18 08:48:18
【问题描述】:

我有一个使用 webview 的 Android 应用程序,最近我试图弄清楚如何使用新的 @media (prefers-color-scheme: dark) CSS 语法来添加深色主题。我在我的页面上写了正确的 CSS,如果我在 Chrome 中打开它并打开 Chrome 的暗模式,它就可以工作。我的AppTheme 也继承了Theme.AppCompat.DayNight,当我为设备上的整个操作系统打开暗模式时,我的应用程序会显示暗加载对话框等。甚至<input> 元素的自动完成选项也会变暗。但是,我的 webview 加载的网页仍然没有变暗。根据this page,webviews 应该支持这个功能,但我就是无法让它工作。我在这里错过了什么?

我还刚刚发现在 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 工作。

【问题讨论】:

  • 我的深色模式样式没有得到应用。我正在使用 Cordova,并且正在使用 inappbrowser 插件运行 ASP.NET MVC 应用程序。如何在我的 cordova 应用程序(包括应用程序内浏览器)中禁用暗模式,或使我的 MVC 应用程序中的 prefers-color-scheme: dark 设置正常工作?
  • 我在我的 Cordova 应用程序(包括 MVC 中的 inappbrowser webview 应用程序)中禁用了暗模式,方法是在我的网页的所有布局中的 <head> 标签中添加 <meta name="color-scheme" content="light dark">

标签: android css android-studio android-webview android-night-mode


【解决方案1】:

Android Webview 处理日/夜模式与其他视图略有不同。 将主题设置为深色会将 WebView 组件(滚动条、缩放按钮等)更改为深色模式版本,但不会更改它加载的内容。

要更改内容,您需要使用 webview 设置的 setForceDark 方法使其也更改其内容。此方法的兼容版本可以在 AndroidX webkit 包中找到。

将以下依赖项添加到您的 gradle 构建中:

implementation 'androidx.webkit:webkit:1.3.0'

(1.3.0 是此软件包所需的最低版本。但更高版本也应该可以工作。)

并将以下代码行添加到您的 webview 初始化中:

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

isFeatureSupported 检查用于确保用户在其设备上安装的Android System WebView 版本支持暗模式(因为这可以通过 Google Play 独立于 Android 版本进行更新或降级)。

注意:setForceDark 功能需要 Android System WebView v76 或更高版本才能安装在正在运行的设备上。

webview 内容的强制黑暗功能有两个所谓的策略:

  • 用户代理变暗: webview 将通过自动反转或变暗其内容的颜色将其内容设置为暗模式。

  • 基于主题的暗化: webview 将根据内容的主题(包括@media (prefers-color-scheme: dark) 查询)更改为暗模式。

要设置 webview 应该使用哪种策略来应用强制黑暗,您可以使用以下代码:

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

注意:策略选择需要在运行设备上安装Android System WebView v83或更高版本。支持setForceDark但不支持策略选择的WebView版本(v76到v81)将使用用户代理变暗

支持的策略选项有:

  • DARK_STRATEGY_USER_AGENT_DARKENING_ONLY:仅使用用户代理变暗并忽略内容中的任何主题(默认)
  • DARK_STRATEGY_WEB_THEME_DARKENING_ONLY:仅使用内容本身的深色主题将页面设置为深色模式。如果内容没有深色主题,则 webview 不会应用任何变暗并“按原样”显示。
  • DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING:使用内容本身的深色主题将页面设置为深色模式。如果内容没有深色主题,请使用用户代理变暗。

Javascript 检查如何处理变暗的网页视图?

JavaScript 调用 window.matchMedia('(prefers-color-scheme: dark)') 将匹配用户代理变暗和 Web 主题变暗策略。

我的 webview 设置为 FORCE_DARK_AUTO 并且我的应用程序在昼夜主题下运行,但不知何故,我的 webview 不会根据我的应用程序主题自动应用暗模式。为什么会发生这种情况?

这是因为 webview 的 FORCE_DARK_AUTO 设置值不适用于主题(如 the documentation 中所述)。 它检查Android 10 Force Dark 功能(应用程序的“快速修复”暗模式功能。它的名称类似,但与 WebView 强制暗模式没有直接关系)。

如果您没有使用强制黑暗,而是使用应用主题来处理黑暗模式(如推荐的那样),您必须自行检查何时应用 webview 的强制黑暗功能。 使用 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 
}

【讨论】:

  • 您在该主题上的完整知识和专业知识令人难以置信。虽然您的回答涉及 alpha 版本包,但它确实可以按要求工作。
  • @Mu-TsunTsai 这是因为某些功能(例如策略选择)仅在 androidx webkit 包的 1.3.0 版本中可用,其中(以及 Android 系统 Webview 的版本 83提到)在撰写本文时仍处于 alpha/beta 阶段。我已经在答案中添加了注释,一旦软件包稳定,我将相应地调整答案。
  • 我偶然发现了这一点,因为随着 Android System WebView v83 的公开推出,我们之前的方法 (webSettings.setForceDark(WebSettings.FORCE_DARK_ON);) 停止工作。我注意到setForceDarksetForceDarkStrategy 已经在androidx.webkit 的v 1.2.0 中可用,但是setForceDarkStrategy 被标记为RestrictTo.Scope.LIBRARY_GROUP,所以它不能从用户代码中调用(并且常量的名称不同)。我可以用@SuppressLint 注释绕过这个限制,它似乎可以工作,但我不确定这会有多稳定
  • 关于版本检查:如果我在 v81 webView 上执行 isFeatureSupported,它会为 setForceDark 返回 true,而对于 setForceDarkStrategy 返回 false。如果我在 v83 上做同样的事情,它两次都返回 true。这还不够,而不是检查特定版本的 webview 组件?
  • @derpirscher 虽然 webkit 包的 v1.2.0 确实具有策略功能,但它们目前仍处于试验阶段,因此受到限制。在受限制的情况下使用它们会出现问题,有时甚至会直接拒绝在未标记为“userdebug”的设备上安装应用程序。请参阅:issuetracker.google.com/issues/148330092source.android.com/setup/develop/new-device
【解决方案2】:

根据您的问题代码,我所做的是根据当前状态强制:

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) 仍然无效(尝试在 else 中使用 FORCE_DARK_OFFFORCE_DARK_AUTO

【讨论】:

  • 我检查了 webview 并检查了与window.matchMedia("(prefers-color-scheme: ...)") 匹配的媒体查询(可能的值为lightdarkno-preference)。当模式最初设置为DARK_ON 时,它匹配dark 方案。任何其他模式都匹配no-preference 方案。此外,一旦我将模式设置为DARK_OFFDARK_AUTO,稍后将其设置为DARK_ON 不会正确传播到webview。但反过来(即先DARK_ON,然后DARK_OFF)也可以。
  • 更准确地说:getForceDark()返回的值始终是期望值(即2,当最后一次设置操作为DARK_ON0,当它为DARK_OFF) .但是在设置DARK_OFFDARK_AUTO 之后设置DARK_ON,不再有任何可见的效果。
【解决方案3】:

这里有两个相关的文档:

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。

目前,WebView 不支持 prefers-color-scheme,部分原因是强制暗是在首选颜色方案标准化之前实现的,部分原因是无法将其明智地(没有样式闪烁)与强制暗集成。目的是提供通过 androidx.webkit 选择强制黑暗或偏好颜色方案的能力。

【讨论】:

  • 不错。它也适用于 DayNight 主题。此答案应标记为已解决!
【解决方案4】:

我认为 WebView 还不支持 prefers-color-scheme CSS 媒体查询。

setForceDark 的新 API 具有三种状态:开、关或自动。

开启 - 您的内容每次都会被渲染得更暗。

关闭 - 您的内容每次都会被渲染得很轻。

AUTO - 如果您的应用的主题较暗或设备操作系统处于深色模式,因为用户切换了操作系统级别开关或打开了省电模式,则内容将呈现较暗。

我相信 AndroidX 很快就会支持旧版本的 Android 并控制是否使用 prefers-color-scheme 而不是 WebView 的 force dark。截止日期目前未知。

现在我建议将 WebView 设置为 setForceDark Auto。这将适用于 Android Q 及更高版本。

我会密切关注 AndroidX 发行说明,了解您对 Android P 及以下设备所需的其余支持。

【讨论】:

  • 不幸的是,AUTO 似乎总是解析为 prefers-color-scheme: no-preference 即使在使用运行在 Android Q 上的 Android Q 的目标进行编译时。因此,webview 不会响应切换系统即使本机应用程序这样做,也要进入暗模式。
  • 你好,derpirscher。我不确定我是否理解。如果您将 webview 设置为 auto,它应该遵循 Android OS 级别的深色主题设置。因此,如果您将 android 设备切换为深色主题,则 webview 应该强制 web 内容变暗。不是这样吗?
  • 对我来说不是这样。我将应用程序的主题设置为AppCompat.DayNight,我可以看到应用程序遵循黑暗模式的操作系统设置。但即使我设置了websettings.setForceDark(FORCE_DARK_AUTO),当我切换操作系统设置时,webview 也不会进入暗模式。另请参阅我的 cmets 到此线程中的其他答案
  • 但也许,我只是错过了一些东西。我在一个最小的例子中做什么:1)AppTheme = AppCompat.DayNight,2)我有values/colors.xmlvalues-night/colors.xml。 3) 在onCreate 中设置AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_FOLLOW_SYSTEM) 和4) 我设置websettings.setForceDark(FORCE_DARK_AUTO)。我可以在暗/亮模式切换(状态栏更改颜色)之后看到应用程序,但看不到 web 视图。它始终显示@media (prefers-color-scheme: no-preference) 下定义的颜色,无论是否设置了深色或浅色模式。
  • @diAz 您也可以使用window.matchMedia 签入javascript。例如window.matchMedia("(prefers-color-scheme: light)").matches 将返回true,如果prefers-color-scheme 设置为light
【解决方案5】:
implementation 'androidx.webkit:webkit:1.4.0'


WebSettings settings = webView.getSettings();

if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
   if (isNightMode) {
      WebSettingsCompat.setForceDark(settings, WebSettingsCompat.FORCE_DARK_ON);
   } else {
      WebSettingsCompat.setForceDark(settings, WebSettingsCompat.FORCE_DARK_OFF);
   }
}

【讨论】:

    【解决方案6】:

    对于最高 v93 的 Webview(实现 'androidx.webkit:webkit:1.4.0'),在我使用此代码之前,标志 DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING 对我不起作用:

    if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
                if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)){            
                WebSettingsCompat.setForceDarkStrategy(
                    webView.settings,
                    WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY
                )
            }
            WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
        }    
    webView.evaluateJavascript("document.documentElement.setAttribute('data-color-mode', 'dark');", null)
    

    请求网站的深色主题,但不强制颜色反转。

    【讨论】:

      猜你喜欢
      • 2021-05-27
      • 2022-10-23
      • 2021-07-15
      • 2020-03-19
      • 1970-01-01
      • 2020-11-18
      • 1970-01-01
      • 1970-01-01
      • 2019-09-11
      相关资源
      最近更新 更多