让 Android 上的 WebView 使用 prefers-color-scheme: dark
Letting WebView on Android work with prefers-color-scheme: dark
我有一个使用 webview 的 Android 应用程序,最近我正在尝试弄清楚如何使用新的 @media (prefers-color-scheme: dark)
CSS 语法添加深色主题。我在我的页面上写了正确的 CSS,如果我在 Chrome 中打开它并打开 Chrome 的黑暗模式,它就可以工作。我的 AppTheme
也继承了 Theme.AppCompat.DayNight
,当我为设备上的整个 OS 打开暗模式时,我的应用程序显示暗加载对话框等。甚至 <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
工作。
我不认为 WebView 支持首选配色方案 CSS 媒体查询。
setForceDark 的新 API 具有三种状态:开、关或自动。
开启 - 您的内容每次都会变得更暗。
关闭 - 您的内容每次都会呈现为浅色。
AUTO - 如果您的应用程序的主题较暗或设备 OS 处于深色模式,因为用户切换 OS 级别开关或打开省电模式,则内容将呈现较暗。
我相信 AndroidX 即将支持旧版本 Android 并控制是否使用首选配色方案而不是 WebView 的强制暗色。截止日期目前未知。
现在我建议将 WebView 设置为 setForceDark Auto。这将适用于 Android Q 及更高版本。
我会密切关注 AndroidX 发行说明,了解 Android P 及以下设备所需的其他支持。
根据你的问题代码,我做的是根据当前状态强制:
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_OFF
和 FORCE_DARK_AUTO
)
这里有两个相关的文档:
https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#force_dark
https://developer.android.com/reference/android/view/View.html#setForceDarkAllowed(boolean)
重点是
- WebView 必须允许强制暗,它的所有父视图也必须允许。
- 主题必须标记为浅色。
这意味着让 FORCE_DARK_AUTO 行为在 WebView 中工作有点复杂。将以下内容添加到 AppCompat.DayNight 主题确实有效,但可能不是最佳解决方案,因为它可能会对 WebView 以外的视图应用 Android force dark。
<item name="android:forceDarkAllowed">true</item>
<item name="android:isLightTheme">true</item>
另一个选项是手动处理,根据相关配置更改设置 FORCE_DARK_ON/OFF。
目前 WebView 不支持 prefers-color-scheme,部分原因是 force dark 是在 prefers-color-scheme 标准化之前实施的,部分原因是无法将其与 force dark 合理地集成(没有样式闪烁)。目的是提供通过 androidx.webkit.
选择强制暗色或首选配色方案的能力
Android Webview 处理 day/night 模式的方式与其他视图略有不同。
将主题设置为深色会将 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 版本支持黑暗模式(因为这可以独立于 Android 进行更新或降级版本通过 Google 播放)。
注意: setForceDark
功能需要 Android System WebView v76 或更高版本安装在运行 设备。
webview 内容的 force dark 特性有两个所谓的策略:
用户代理变暗: webview 将通过自动反转或变暗其内容的颜色将其内容设置为暗模式。
基于主题的暗化: webview 将根据内容的主题(包括 @media (prefers-color-scheme: dark)
查询)更改为暗模式。
要设置 webview 应使用哪种策略来应用 force dark,您可以使用以下代码:
if(WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
WebSettingsCompat.setForceDarkStrategy(myWebView.getSettings(), WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY);
}
注意: 策略选择需要 Android 系统 WebView v83 或更高版本安装在 运行设备。支持setForceDark
但不支持策略选择的WebView版本(v76到v81)将使用user agent darkening
支持的策略选项有:
- DARK_STRATEGY_USER_AGENT_DARKENING_ONLY: 仅使用用户代理变暗并忽略内容中的任何主题(默认)
- DARK_STRATEGY_WEB_THEME_DARKENING_ONLY:仅使用内容本身的深色主题将页面设置为深色模式。如果内容没有深色主题,webview 将不会应用任何变暗并“按原样”显示。
- DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING:使用内容本身的深色主题,将页面设置为深色模式。如果内容没有深色主题,请使用用户代理变暗。
How do Javascript checks work for darkened webviews?
JavaScript调用window.matchMedia('(prefers-color-scheme: dark)')
将在用户代理暗化和网页主题暗化策略中匹配。
I have my webview set to FORCE_DARK_AUTO and my app is running in a daynight theme, but somehow my webview doesn't apply dark mode automatically based on my app theme. Why does this happen?
这是因为 webview 的 FORCE_DARK_AUTO
设置值不基于主题(如 the documentation 中所述)。
它检查 Android 10 Force Dark 功能(应用程序的“快速修复”暗模式功能。它的名称类似,但与 WebView force dark 没有直接关系)。
如果您没有使用强制暗模式,而是使用应用主题来处理暗模式(按照建议),您必须自行检查何时应用 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
}
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);
}
}
对于高达 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)
请求网站的深色主题,但不强制反转颜色。
我有一个使用 webview 的 Android 应用程序,最近我正在尝试弄清楚如何使用新的 @media (prefers-color-scheme: dark)
CSS 语法添加深色主题。我在我的页面上写了正确的 CSS,如果我在 Chrome 中打开它并打开 Chrome 的黑暗模式,它就可以工作。我的 AppTheme
也继承了 Theme.AppCompat.DayNight
,当我为设备上的整个 OS 打开暗模式时,我的应用程序显示暗加载对话框等。甚至 <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
工作。
我不认为 WebView 支持首选配色方案 CSS 媒体查询。
setForceDark 的新 API 具有三种状态:开、关或自动。
开启 - 您的内容每次都会变得更暗。
关闭 - 您的内容每次都会呈现为浅色。
AUTO - 如果您的应用程序的主题较暗或设备 OS 处于深色模式,因为用户切换 OS 级别开关或打开省电模式,则内容将呈现较暗。
我相信 AndroidX 即将支持旧版本 Android 并控制是否使用首选配色方案而不是 WebView 的强制暗色。截止日期目前未知。
现在我建议将 WebView 设置为 setForceDark Auto。这将适用于 Android Q 及更高版本。
我会密切关注 AndroidX 发行说明,了解 Android P 及以下设备所需的其他支持。
根据你的问题代码,我做的是根据当前状态强制:
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_OFF
和 FORCE_DARK_AUTO
)
这里有两个相关的文档:
https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#force_dark
https://developer.android.com/reference/android/view/View.html#setForceDarkAllowed(boolean)
重点是
- WebView 必须允许强制暗,它的所有父视图也必须允许。
- 主题必须标记为浅色。
这意味着让 FORCE_DARK_AUTO 行为在 WebView 中工作有点复杂。将以下内容添加到 AppCompat.DayNight 主题确实有效,但可能不是最佳解决方案,因为它可能会对 WebView 以外的视图应用 Android force dark。
<item name="android:forceDarkAllowed">true</item>
<item name="android:isLightTheme">true</item>
另一个选项是手动处理,根据相关配置更改设置 FORCE_DARK_ON/OFF。
目前 WebView 不支持 prefers-color-scheme,部分原因是 force dark 是在 prefers-color-scheme 标准化之前实施的,部分原因是无法将其与 force dark 合理地集成(没有样式闪烁)。目的是提供通过 androidx.webkit.
选择强制暗色或首选配色方案的能力Android Webview 处理 day/night 模式的方式与其他视图略有不同。 将主题设置为深色会将 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 版本支持黑暗模式(因为这可以独立于 Android 进行更新或降级版本通过 Google 播放)。
注意: setForceDark
功能需要 Android System WebView v76 或更高版本安装在运行 设备。
webview 内容的 force dark 特性有两个所谓的策略:
用户代理变暗: webview 将通过自动反转或变暗其内容的颜色将其内容设置为暗模式。
基于主题的暗化: webview 将根据内容的主题(包括
@media (prefers-color-scheme: dark)
查询)更改为暗模式。
要设置 webview 应使用哪种策略来应用 force dark,您可以使用以下代码:
if(WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
WebSettingsCompat.setForceDarkStrategy(myWebView.getSettings(), WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY);
}
注意: 策略选择需要 Android 系统 WebView v83 或更高版本安装在 运行设备。支持setForceDark
但不支持策略选择的WebView版本(v76到v81)将使用user agent darkening
支持的策略选项有:
- DARK_STRATEGY_USER_AGENT_DARKENING_ONLY: 仅使用用户代理变暗并忽略内容中的任何主题(默认)
- DARK_STRATEGY_WEB_THEME_DARKENING_ONLY:仅使用内容本身的深色主题将页面设置为深色模式。如果内容没有深色主题,webview 将不会应用任何变暗并“按原样”显示。
- DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING:使用内容本身的深色主题,将页面设置为深色模式。如果内容没有深色主题,请使用用户代理变暗。
How do Javascript checks work for darkened webviews?
JavaScript调用window.matchMedia('(prefers-color-scheme: dark)')
将在用户代理暗化和网页主题暗化策略中匹配。
I have my webview set to FORCE_DARK_AUTO and my app is running in a daynight theme, but somehow my webview doesn't apply dark mode automatically based on my app theme. Why does this happen?
这是因为 webview 的 FORCE_DARK_AUTO
设置值不基于主题(如 the documentation 中所述)。
它检查 Android 10 Force Dark 功能(应用程序的“快速修复”暗模式功能。它的名称类似,但与 WebView force dark 没有直接关系)。
如果您没有使用强制暗模式,而是使用应用主题来处理暗模式(按照建议),您必须自行检查何时应用 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
}
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);
}
}
对于高达 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)
请求网站的深色主题,但不强制反转颜色。