アプリ開発備忘録: ① Android 15からはEdge-to-Edge表示が強制適用に (Part 1)
少し前に手持ちのAndroid携帯の一つにシステムのアップデートが入ったのですが、それ以降、いくつかのアプリで、画面の上下の表示がおかしくなってしまいました。。。
実は、このアップデートでアンドロイドOSが「アップサイドダウンケーキ」(Android 14, SDK 34)から「バニラアイスクリーム」 (Android 15, SDK 35) に更新されて、Android 15からはアプリの表示がデフォルトでEdge-to-Edge (端から端まで) 表示の仕様、つまり全画面表示になったのが原因でした。
これまでもアプリを画面の端から端までの全画面で表示する「没入モード」という機能があったので、そのモードでの画面表示がデフォルトになったという理解に少し近いのですが。。。 実際はそうではありません。
没入モードの場合は、アプリが全画面で表示される以外に、「システムバー」と呼ばれる、画面の上部に表示される「ステータスバー (Status Bar)」と下部に表示される「ナビゲーションバー」の表示を必要に応じて表示/非表示に出来るほか、非表示の場合でもユーザーの操作によっては再表示される仕様になっています。
Android 15よりも前のOSでは、没入モード以外では、アプリは全画面にではなく、自動的に上下のシステムバーの分が差し引かれた部分だけに表示される仕様になっていて、アプリの表示が上下のシステムバーに重なって表示される事がない様になっていました。
Android 15以降からは、システムバーの表示/非表示に関係なく、アプリの表示サイズが端から端までの全画面表示になるので、必要に応じて、インセット(Inset) 情報などを使ってアプリの表示コンテンツがシステムバーの表示と重ならない様に調整する必要があります。。。。。
。。とまでは、簡単な変更事項にも思えるのですが、開発中のアンドロイドアプリに色々な影響があったので備忘録としてここにメモしておきます。
Edge-to-Edge表示
Android 15 (SDK 35)から「Edge-to-Edge表示」がデフォルトの仕様になった訳ですが、アプリを開発している場合は、ターゲットSDKのバージョン(targetSdkVersion)を35以上に設定してビルド (作成) したアプリをAndroid 15以降のデバイスで開くと、デフォルトとしてこの表示が強制的に適用※されます。
この変更は、縦向き表示の場合はそこまで影響は無いと思いますが、画面上に「ノッチ」やカメラやマイクなどによるの「切り抜き」があるデバイスの場合だとアプリの表示が少し変わってしまいます。
これが、「Edge-to-Edge」表示になると、アプリの背景が端から端の全画面表示になるので、実際の操作範囲は変わりませんが、表示範囲が広くなった様なビジュアル効果が期待されます。
Edge-to-Edge表示が強制的に有効になるのはAndroid 15以降のデバイスで、Android 15が発表される前から既にあったアプリの様に、アプリのビルドターゲットがAndroid 15よりも前であれば、Android 15以降のデバイスで開いてもEdge-to-Edge表示が強制的に有効になる事はありません。
また、Android 15以降のデバイスでもEdge-to-Edge表示を有効にしたい場合は、ちょっとしたコード変更が必要になります。
※:強制適用されるEdge-to-Edge表示を一時的に無効にする方法 (SDK 35)
アプリをGoogleのPlayストアで一般公開する場合、以降はアップロード出来るアプリのターゲットSDKバージョンは35以上が必須になります。
ターゲットSDKバージョンが35の場合で、KotlinまたはJaveで開発しているAndroidアプリであれば、AndroidManifest.xmlの<application>か<activity>タグに windowOptOutEdgeToEdgeEnforcement属性をtrueに設定すると「Edge-to-Edge表示」を無効にできます。
<application android:windowOptOutEdgeToEdgeEnforcement="true" ...> : : : </application>
<activity android:name="...." android:windowOptOutEdgeToEdgeEnforcement ="true" ...> : : : </activity>
Flutterで開発している場合は、AndroidMeanifest.xml内ではなく、アプリに適応されるTheme内で設定する必要があるみたいなので、詳細は次のFlutterのガイドを参考にしてみてください:
このオプションはAndroid 16では廃止になるので、コード更新に余裕の無い場合などに一時的に無効にする対策となります。
Android 15の次のバージョンになる「Android 16」では「アダプティブレイアウト」というコンセプトが追加されるみたいで、Edsge-to-edge表示への設定はその準備みたいな変更だと思いますが、今後、1つの画面に1以上のアプリが同時に表示される「分割ウインドウ」や、複数の画面に対応した「マルチウインドウ」といった様々な環境でもアプリが正しく表示される枠組みに繋がっていく事を期待したいかと思います。
これまで開発してきたアプリは、ターゲットSDKのバージョンを35以降にアップデートすると、Android 15以降のデバイス上で全画面表示が強制適用 (オプトアウト可) されます。
なので 、冒頭でリンクしたドキュメントにあるInset情報を使った表示の調整を加えないまま新しくアプリをリリースしてしまうと:
- ステータスバーがAppBarと重なって表示されてしまう
- 3つボタン表示のナビゲーションバーを有効にすると画面下部の表示がナビゲーションバーの下に隠れてしまう
- 画面にノッチや切り抜きがあるデバイスを横向きにした時、表示の一部がノッチや切り抜きの部分に重なってしまって見えない
などの不都合が生じる場合があるみたいです。。
また、ターゲットSDKのバージョンが35よりも低いビルドのアプリは、Android 15以降のデバイスで開いても、表示がステータスバーと重なってしまう事はありませんが、アプリによってはステータスバーとアプリの間に何も表示されない隙間が発生する場合もあるみたいです。
後方互換
Android 15よりも前のバージョンのデバイスでも、Android 15のビルドツールで追加されたメソッドの「enableEgerToedge()」で「Edge-to-Edge」表示を有効に出来るので、Androidのバージョンに関係なく、統一されたアプリの見た目を提供出来る仕様になっています。。
見た目をAndroid 15以降で推薦される、透明なシステムバー (ステータスバーとナビゲーションバー)背景のデザインで統一するのであれば、コード内にあるシステムバーの色を設定する部分を全て透明色に変更 (又は場合によっては無効に)すれば良いのですが、これまで通りにシステムバーの背景色を設定したい場合、
といったネット検索から始めると、本当にシステムバーの背景色を設定する方法が見つかったりするのですが、最終的には「別の方法を使った方が良いのでは??」という結論になったので備忘録を以降にメモしておきます。
テストアプリを使って確認
Android 15で強制適用される「Edge-toEdge表示」がどのように開発中のアプリに影響するか、アプリを「XMLレイアウト」と「JetPack Compose Material 3」で切り替えてビルドができる様にしたテストアプリをAndroid/Kotlinで作ったので、次の3つのAndroid上で表示して確認してみました:
- Android 15 (Samsung SM-A556E、実機)、
- Android 14 エミュレーター (Google Play ARM 64 v8a System Image Rev 14)
- Android 10 エミュレーター (Google Play ARM 64 v8a System Image Rev 9)
テストアプリのビルドでは、IDEにはAndroid Studio の「Meerkat Feature Drop 2024.3.2 Patch 1 (May 22,2025)」、AndroidのSDKはバージョンは35 Rev 2を使用しました。
ターゲットSDKバージョンを35に変更
これ以降はアプリをターゲットSDKを35に設定して、他には変更を加えずにビルドした場合になります。
ターゲットSDKが35のアプリをAndroid 15で表示した場合
次はAndroid 15 (SDK 35)で表示した際のスクショです。
ベースラインのスクショと比べると:
- ステータスバーの背景色が透明になっていて、
- XMLレイアウト版の場合はAppBarの表示がステータスバーと重なってしまっています。
といっても、Android 15での動作の変更点に書いてある通りの結果になっています。
「Edge-to-Edge」表示の場合、XMLレイアウトに関してはステータスバーとAppBarが重なってしまうので、既存のコードに重ならない様にする方法が必要になります。
また、JetPack Composeの場合は、AppBarにデフォルトで表示されるステータスバー分の余白が(Inset情報を使って)追加されている為、この時点では、ステータスバーの背景が透明なのに問題がなければ、特にコード変更は必要がないみたいです。
ターゲットSDKが35のアプリをAndroid 14で表示した場合
今度は同じSDK 35ターゲットのアプリをAndroid 14 (SDK 34)で表示した場合です。
SDKが古いデバイスにも下位互換になっている為、 表示はEdge-to-Edgeにはなりませんが、Android15 よりも古いデバイスでコード変更無しのアプリを開いても問題は無いようです。
また、Android 10でも同様にXMLとCompose版は特に問題なく表示されていました。
既存のアプリを最小限のコード変更で済ませたい場合
ターゲットSDKバージョンが35よりも前の設定で開発された既存のアプリを最小限のコード変更で済ませたい場合:
- Android 15よりも前のデバイスでは「Edge-to-Edge」表示を有効にせず、これまで通りの表示を継続、
- Android 15以降はステータスバーの背景色は透明、
といった点を妥協できるのであれば:
- JetPack Composeの場合は、特に変更は必要が無く、
- XMLレイアウトの場合は、ステータスバーとAppBarが重ならない様にコード変更をすれば良い、
という事になります (※: Android 15以降のデバイスでは冒頭でリンクを張った、Inset情報を使ったUIの調整は必要になります。)
ステータスバーとAppBarが重ならない様にする例 (XMLレイアウト)
XMLレイアウトのアプリでステータスバーがAppBarと重ならない方法を検索すると、ネット検索には:
XMLレイアウト内で、MaterialToolbar またはAppBarLayoutに
を追加すればAppBarがステータスバーに重ならない/p>
といった回答が出てくるかもしれませんが、この方法で重ならない様になるのは間違いはないのですが、この時点では画面の背景が塗られていないのでステータスバーの背景が白または黒になってしまいます。
次はXMLレイアウト版のテストアプリに「fitsSystemWindows」を追加した場合のスクショになります。
- AppBarLayoutに「fitsSystemWindow="true"」を加えた場合: AppBarLayoutがステータスバーと重ならない様に下に移動していますが、 背景が白なのでステータスバーの文字・アイコンが見えなくなっています。
- MaterialToolbarに「fitsSystemWindow="true"」を加えた場合: AppBarLayout内にあるMaterialToolbarがステータスバーと重ならない様に下に移動していますが、 AppBarLayoutの高さがそのままなのでAppBarのタイトルがLayoutより下に移動して見えなくなっています。
XMLレイアウトにステータスバーの背景を追加する
AppBarレイアウトの始めにステータスバーと同じ高さのLinearLayoutを追加してAppBarがステータスバーと重ならない様にします。
XMLレイアウト
ActivityのXMLレイアウトにあるAppBarLayoutにLinearLayoutを追加して、
- 高さ(android:layout_height)は「0dp」にして必要が無い場合は表示されないようします。
- 「android:theme」はAppBarLayoutと同じにしてAppBarと同じ色になる様にします。(※: 好みのステータスバーの色に指定する事も出来ます。)
<com.google.android.material.appbar.AppBarLayout android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/Theme.MyTestApp.AppBarOverlay" app:liftOnScroll="true" > <LinearLayout android:id="@+id/status_bar_background" android:layout_width="match_parent" android:layout_height="0dp" android:orientation="vertical" android:theme="@style/customToolbar" app:layout_scrollFlags="scroll|enterAlways" /> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:theme="@style/customToolbar" : : :
この例では追加したLinearLayoutには「status_bar_background」というIDに設定されています。
MainActivityなどの各Activityで onCreate内で、setContentView(...)以降に次のコードを追加します。
if (Build.VERSION_CODES.VANILLA_ICE_CREAM =< Build.VERSION.SDK_INT) {
findViewById<ViewGroup>(R.id.status_bar_background)?.also { layout ->
ViewCompat.setOnApplyWindowInsetsListener(layout) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars())
view.updateLayoutParams<ViewGroup.MarginLayoutParams> {
height = insets.top
}
windowInsets
}
}
}
このコードでは、XMLレイアウトに追加したLinearLayoutに、OnApplyWindowInsetsListenerを設定してInset情報に基づいて高さをステータスバーと同じ高さにする様にしています
(詳しくは 👉「📑ビューでコンテンツをエッジ ツー エッジで表示する」を参照してみて下さい)。
注: これは、「enableEdgeToEdge()」を使わない、「既存のアプリを最小限のコード変更で済ませたい場合」の対処方です。
コメント
コメントを投稿