Photoruction工事中!

Photoructionの開発ブログです!

Android15対応時のEdge-to-Edgeを回避する

Androidエンジニアの藤井です。今回は、既存アプリのターゲットをAndroid 15に更新した際のEdge-to-Edge回避について、社内で対応した事例も交えて紹介します。

はじめに:Android 15とEdge-to-Edgeの現状、そして既存アプリの課題

Android 15の登場により、アプリのEdge-to-Edge表示は、より多くのアプリで標準的な振る舞いとして推奨されるようになりました。これにより、アプリのコンテンツがシステムバー(ステータスバーやナビゲーションバー)の背後にまで広がり、より没入感のあるユーザー体験を提供できるようになります。

しかし、既存のAndroidアプリをAndroid 15(API Level 35)をターゲットに更新する際、このEdge-to-Edgeの強制的な適用が、予期せぬUIの崩れやレイアウトの問題を引き起こすことがあります。特に、多くのカスタムビューや複雑なレイアウトを持つアプリでは、システムバーとコンテンツの重なりによって、ボタンが隠れたり、重要な情報が欠けたりといった問題が発生しがちです。

私が担当するプロジェクトでも、API Levelを34から35に上げた際、Android 15のテスト端末で一部の画面においてEdge-to-Edgeによる画面表示の崩れが発覚しました。このような状況下で、既存のUIデザインとユーザー体験を維持しつつ、迅速にAndroid 15に対応するために、私たちはEdge-to-Edge表示を意図的に回避する選択をしました。本記事では、その具体的な対応策と、Android 15で新たに導入されたandroid:windowOptOutEdgeToEdgeEnforcement属性の活用について詳しく解説します。


Edge-to-Edge回避のアプローチ:android:windowOptOutEdgeToEdgeEnforcementの活用

Android 15では、開発者がEdge-to-Edgeの強制適用からオプトアウトするための新しい属性が導入されました。それがandroid:windowOptOutEdgeToEdgeEnforcementです。この属性をtrueに設定することで、Android 15以降のシステムが適用しようとするEdge-to-Edgeレイアウトの強制を回避し、従来のシステムバーがコンテンツの上に描画される挙動を維持することができます。

これにより、過去のAndroidバージョンでWindowCompat.setDecorFitsSystemWindows(window, true)などを用いて行っていたシステムバーの処理と連携しやすくなり、既存のレイアウトを大きく変更することなく、Android 15への対応を進めることが可能になります。


具体的な実装と対応詳細

プロジェクトでEdge-to-Edgeを回避するために行った具体的な対応は以下の通りです。

1. テーマへのandroid:windowOptOutEdgeToEdgeEnforcement属性の追加

まず、styles.xml(またはthemes.xml)に、各Activityに適用するテーマを定義し、その中にandroid:windowOptOutEdgeToEdgeEnforcement属性を追加しました。

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="upsideDownCake">true</item>
        </style>

    <style name="Theme.MyApp.FeatureA" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="upsideDownCake">true</item>
        </style>

    <style name="Theme.MyApp.FeatureB" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="upsideDownCake">true</item>
        </style>

    <style name="Theme.MyApp.Dialog" parent="Theme.MaterialComponents.DayNight.Dialog">
        <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="upsideDownCake">true</item>
        </style>
</resources>

ポイント:

  • tools:targetApi="upsideDownCake": この属性はAndroid 15 (Upside Down Cake) 以降で有効な属性であることを示し、それ以前のAPIレベルをターゲットにしているビルドでコンパイルエラーになるのを防ぎます。
  • プロジェクトでは、特定の画面や機能に特化したテーマ(例: Theme.MyApp.FeatureATheme.MyApp.FeatureBなど)にもこの属性を追加しました。これにより、アプリ全体で一律にEdge-to-Edgeを回避するだけでなく、必要に応じて細かく制御することが可能になります。

2. AndroidManifest.xml でのテーマの適用

次に、AndroidManifest.xml内で、Edge-to-Edgeを回避したい各Activityに対して、上記で定義したテーマを適用しました。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApp"> <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/Theme.MyApp.NoActionBar" tools:targetApi="35">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".FeatureAActivity"
            android:label="@string/feature_a_title"
            android:theme="@style/Theme.MyApp.FeatureA" /> <activity
            android:name=".FeatureBActivity"
            android:theme="@style="Theme.MyApp.FeatureB" /> </application>
</manifest>

このように、AndroidManifest.xmlで適切なテーマを適用することで、Android 15の端末であっても、各画面が意図した通りのレイアウトで表示されることを確認しました。


Android 15対応におけるその他の考慮事項:16 KBページサイズデバイスへの対応

今回のEdge-to-Edge回避以外にも、Android 15への対応で新たに考慮すべき点があります。それは、Android 15以降でサポートされる16 KBのページサイズを使用するように構成されたデバイス(以下、16 KBデバイス)への対応です。

標準的なAndroidバイスのメモリページサイズは4 KBですが、Android 15では一部のデバイスで16 KBページサイズが採用される可能性があります。これは、デバイスのメモリ管理効率向上を目的としたもので、ほとんどのアプリには直接的な影響を与えません。しかし、アプリでネイティブコード(JNIやNDKを使用するコード)を扱っている場合、この16 KBページサイズに起因する問題が発生する可能性があります。

具体的には、ネイティブコードが4 KBのページサイズを前提としたメモリ管理やアライメント処理を行っている場合、16 KBデバイス上でクラッシュしたり、パフォーマンスが低下したりするリスクがあります。

現時点では、私たちのプロジェクトではこの16 KBデバイスへの対応はまだ完了していません。今後は、ネイティブコード部分を詳細にレビューし、16 KBページサイズ環境でも適切に動作するよう修正を加えていく必要があります。


注意点とトレードオフ

今回の対応は、既存のアプリのUIを迅速にAndroid 15に適合させるための効果的な手段でした。しかし、このアプローチにはいくつかのトレードオフが存在します。

  • 既存UIの維持 vs. 最新のUIトレンド: Edge-to-Edgeを回避することで、既存のUIの一貫性を保つことができますが、Androidが推奨するより没入感のある体験からは逸れることになります。将来的には、Edge-to-Edgeに対応したデザインへの移行も視野に入れる必要があるかもしれません。
  • 開発コストの削減: 短期的な対応としては、UIの大きな手直しが不要となるため、開発コストを抑えることができます。
  • 長期的なメンテナンス: Androidのバージョンアップに伴い、Edge-to-Edgeに関する振る舞いがさらに変更される可能性もあります。

まとめ:今後の展望

今回のAndroid 15への対応は、android:windowOptOutEdgeToEdgeEnforcementという新しい属性を活用することで、既存アプリのUIを維持しつつ、迅速に最新のAndroidバージョンに対応できることを示しました。特に、複雑なレイアウトを持つ既存アプリにとっては、このオプトアウト機能は非常に有用な選択肢となるでしょう。

しかし、この属性は将来的に廃止される(※)ため、今回の対応はあくまで暫定的なものと認識しています。今後は、よりモダンなUI設計への移行を視野に入れ、Edge-to-Edgeを前提としたレイアウト調整や、WindowInsetsの適切な取り扱いに関する恒久的な対応を計画していく必要があります。 ※ https://developer.android.com/about/versions/16/behavior-changes-16?hl=ja#edge-to-edge

また、Edge-to-Edge対応だけでなく、16 KBデバイスへのネイティブコードの対応など、Android 15への移行には複数の側面での検討が必要です。まずは今回の対応によって、ユーザーに安定した体験を提供しながら、今後のUI/UX戦略と技術的な課題にじっくりと取り組む時間を確保することができました。


株式会社フォトラクションでは一緒に働く仲間を募集しています

www.wantedly.com