Capacitor 6 でステータスバーが重なる。CSSでは直らなかった理由

ヘッダーの文字が、時計と重なった

節約記録アプリで、こんな症状が出ました。

画面いちばん上のヘッダーの文字が、ステータスバーの時計やバッテリー表示と重なっていたのです。

アプリの画面が、ステータスバーの下まで潜り込んでいる状態です。

同時に、画面下の方でも同じことが起きていました。ジェスチャー操作用の帯が、画面の一番下に置いた広告バナーに重なりかけていました。

原因は Android 15 の「エッジツーエッジ」

調べると、原因は Android 15 の仕様変更でした。

アプリの対象バージョン(targetSdk)を 35 以上にすると、Android 15 以降の端末では、アプリの表示領域がステータスバーやナビゲーションバーの下まで強制的に広がります。これを「エッジツーエッジ」と呼びます。

画面の端から端まで使えるようになる代わりに、バーの下に隠れる部分をどう避けるかは、アプリ側の責任になりました。

まず CSS で直そうとした

私のアプリは、HTML で作った画面を Capacitor でアプリにしたものです。

こういうときの定番は CSS の env(safe-area-inset-top) です。ブラウザが「画面上端の安全な余白」を教えてくれるので、その分だけヘッダーを下げればいい。

ところが、直りませんでした。

CSS では直らなかった理由

ここが今回いちばんの落とし穴でした。

Android の WebView では、env(safe-area-inset-top) がステータスバーの高さを返さないことがあります。

WebView が反映しているのは、主に画面の切り欠き(ノッチやパンチホール)のほうでした。

そのため、切り欠きのない端末では値が 0 になります。検証に使ったエミュレーターの端末もそうでした。CSS を書いても、ずらす量が0なのですから、何も変わりません。

「CSS を書いたのに効かない」のではなく、「CSS が受け取る値そのものが0だった」のです。

Android の WebView と CSS の食い違いには、別の形でも足をすくわれたことがあります(たった1行のCSSが、Android WebViewのタッチスクロールを殺していた)。

同じ条件なのに、もう1本のアプリは平気だった

不思議なことに、ワードバトルは同じ targetSdk 36 なのに、この症状が出ていませんでした。

違いは、Capacitor のバージョンでした。

アプリ Capacitor 症状
ワードバトル 8 系 出ない
節約記録アプリ 6 系 出る

Capacitor 8 系では、フレームワークの側でステータスバーやナビゲーションバーの余白を処理してくれるようです。6 系ではその処理が効きませんでした。(公式の変更履歴までは追っていません。私の2本のアプリで起きた違いです。)

同じ Android、同じ targetSdk でも、Capacitor のバージョンで結果が変わる。これに気づくまでに時間がかかりました。

対処:Android 側で余白を渡す

Capacitor を 8 系に上げる手もありましたが、影響範囲が大きくなります。

そこで、Android 側の入口である MainActivity.java に、余白の処理を足すことにしました。修正はこの1ファイルだけです。

public class MainActivity extends BridgeActivity {

    // ヘッダーの色。ステータスバーの背後の帯をこの色で塗り、ヘッダーとつながって見せる
    private static final int SYSTEM_BAR_BACKGROUND = Color.parseColor("#1a3a2a");

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        View content = findViewById(android.R.id.content);
        content.setBackgroundColor(SYSTEM_BAR_BACKGROUND);

        ViewCompat.setOnApplyWindowInsetsListener(content, (view, windowInsets) -> {
            Insets bars = windowInsets.getInsets(
                    WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
            view.setPadding(bars.left, bars.top, bars.right, bars.bottom);
            // キーボードなどの余白は子に渡す必要があるので、ここでは消費しない
            return windowInsets;
        });
    }
}

やっていることは3つです。

  1. システムのバー(ステータスバー・ナビゲーションバー)と切り欠きの大きさを、Android から直接受け取る
  2. その大きさを、画面全体の余白(padding)として与える
  3. 余白の部分が白く浮かないよう、背景をヘッダーと同じ色で塗る

CSS と違い、Android から受け取る値なので、切り欠きのない端末でもステータスバーの高さがきちんと入ります。

直ったことの確認

修正版(バージョン 1.2.1)で、次の2点を確かめました。

  • ヘッダーの文字が時計と重ならない
  • 画面下のジェスチャー用の帯が、広告バナーを隠さない

上と下、両方が同時に解決しました。

節約記録アプリの画面の上端。時計などのステータスバーの下に、ヘッダーの「節約記録アプリ」の文字が重ならずに表示されている
いま(10月)の節約記録アプリの画面の上端。時計の帯とヘッダーの文字が重ならずに並んでいる

同じ症状で困っている方へ

HTML で作った画面をアプリにしていて、Android 15 でヘッダーがステータスバーに潜り込んだら、次の順で確認してみてください。

  1. Capacitor のバージョンを確認する。 8 系なら、フレームワーク側の設定を疑う
  2. 6 系なら、CSS の env() にこだわらない。 受け取る値が0になっている可能性がある
  3. MainActivity に余白の処理を足す。 アップグレードより影響範囲が小さい

私は CSS で何度も試して時間を使いました。値が0なら、CSS をいくら書いても動きません。 そこに早く気づければ、遠回りせずに済みます。

この記事のアプリ:節約記録アプリ(紹介ページ)

コメント

タイトルとURLをコピーしました