Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

Android 16のダークテーマ対応、WebViewで意図しない挙動になってませんか?

こんにちは、Androidエンジニアの伊藤と申します!

今回は、遅ればせながらAndroid 16対応でダークテーマの挙動を調整した際に、WebViewが意図せずダークモード固定になってしまった問題について共有します👀

android:isLightTheme 属性の理解が不十分だったために発生した問題で、同じような状況に遭遇する方もいるかもしれないため、経緯と解決策をまとめました。

背景:Android 16のダークテーマ拡張オプション

Android 16 QPR2から、ダークテーマに「拡張オプション」という新しい設定が追加されました。

Android 16 QPR2 Beta 1のリリースノートによると、この拡張オプションを有効にすると、ダークテーマに対応していないアプリでも強制的にダークテーマが適用されるようになります。

これは一見便利な機能ですが、私たちのアプリではダークテーマの強制適用は回避したかったので、ダークテーマ固定の設定は行いたくありませんでした。

最初の対応:isLightTheme = false

この強制ダークテーマを回避するために、最初に行った対応は、アプリのテーマに android:isLightTheme="false" を設定することでした。

<!-- styles.xml -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:isLightTheme">false</item>
    <!-- その他の設定 -->
</style>

この設定により、Android 16の拡張オプションによる強制ダークテーマは回避できました。

※ isLightThemeとは?システムがアプリの画面を自動的に暗く(色反転)すべきかを判断するために使われる、現在のテーマが明るいかどうかを示す設定属性です。isLightTheme="false" と設定することで、システムによる自動的な色の反転処理が適用されなくなり、アプリ(特にすでにダークテーマのもの)が意図せず反転されて表示が崩れるのを防ぐことができます。

※公式の記事はこちら

よかったよかった、と思っていたのですが、ここで新たな問題が発生しました。

問題の発覚:WebViewがダークモード固定に

isLightTheme="false" を設定した後、WebViewで表示するページがダークモード固定になってしまうという事象が報告されました。

具体的には:

  • ユーザーの端末設定がライトモードでも、WebView内のコンテンツがダークモードで表示される
  • prefers-color-scheme に対応しているWebページが、常にダークモードとして判定される

最初は「WebView側の問題では?」と思っていましたが、調査を進めるうちに、isLightTheme 属性の意味を誤解していたことが判明しました。

原因:isLightTheme属性の誤解

Android公式ドキュメント「WebView でのダーク テーマの適用」を確認したところ、以下の挙動が記載されていました:

つまり、isLightTheme="false" を設定すると、WebViewは「アプリがダークモードを使用している」と判断し、prefers-color-scheme に対応しているWebページを常にダークモードで表示してしまうのです。

私たちが意図していたのは「Android 16の強制ダークテーマを回避する」ことでしたが、結果として「WebViewに対してダークモードを使っていると伝えてしまった」ことになります。

解決策:WebView専用のライトテーマを作成

この問題を解決するために、WebView専用のテーマを作成し、isLightTheme="true" を設定することにしました。

※今回の対応はWebView内をライト固定にするものなので、Web側もダーク対応が必要な場合は別の制御が必要です。

1. WebView専用テーマの定義

<!-- styles.xml -->
<!-- ウェブビューでisLightThemeがfalseで適応されると強制的にダークモードで表示されてしまうため -->
<!-- ライトテーマになるようにtrueに設定する -->
<style name="Theme_WebView_Light" parent="AppTheme">
    <item name="android:isLightTheme">true</item>
</style>

2. カスタムWebViewクラスの作成

/**
 * ウェブビューをライトテーマにするためのクラス
 * ダークモード対応が可能になった場合はこちらのクラスは不要になる想定
 */
open class LightThemeWebView : WebView {
    companion object {
        private fun wrapContext(context: Context): ContextThemeWrapper {
            return ContextThemeWrapper(context, R.style.Theme_WebView_Light)
        }
    }

    constructor(context: Context) : super(wrapContext(context))
    constructor(context: Context, attrs: AttributeSet?) : super(wrapContext(context), attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        wrapContext(context),
        attrs,
        defStyleAttr
    )
}

今回作成したカスタムWebViewクラスは、コンストラクタでContextを ContextThemeWrapper でラップし、Theme_WebView_Light テーマを適用しています。

3. 既存のWebViewを置き換え

既存のWebViewを使用している箇所を、LightThemeWebView に置き換えました。

// Before
class HogehogeWebView : WebView {
    // ...
}

// After
class HogehogeWebView : LightThemeWebView {
    // ...
}

レイアウトXMLでも、カスタムWebViewクラスを指定しました。

<li.yapp.sdk.core.presentation.view.LightThemeWebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

結果

この変更により、以下の動作が実現できました

✅ Android 16のダークテーマ拡張オプションによる強制ダークテーマは回避(アプリ全体は isLightTheme="false"

✅ WebViewは isLightTheme="true" として動作し、prefers-color-scheme が正しく端末設定を反映

まとめ

今回の問題は、android:isLightTheme 属性の意味を正しく理解していなかったことが原因でした。

重要なポイントisLightTheme はAndroidシステムへの「このアプリはライトテーマを使っているか」の宣言です。WebViewは、この属性を参照して prefers-color-scheme の挙動を決定します。アプリ全体とWebViewで異なるテーマ設定が必要な場合は、ContextThemeWrapper を活用する必要があるということですね!

Android 16の新機能に対応する際、既存の機能(今回はWebView)への影響を見落としてしまいましたが、公式ドキュメントを確認し、属性の本来の意味を理解することで解決できました。

今回の件で、ドキュメントの前提条件をしっかり読むことの重要性を改めて痛感しました😭

同じような問題に遭遇した方、またはAndroid 16対応を検討している方の参考になれば幸いです。

参考資料