Androidアプリを開発・運用していると、こんな警告がGoogle Play Consoleに届いていませんか?

「2025年8月31日までにtargetSdkVersionを35以上に更新してください」

対応しないとアップデートが配信できなくなるわけですが、targetSdkVersion 35への引き上げで厄介なのが Edge-to-Edge(エッジ・トゥ・エッジ)の強制適用 です。何も考えずにバージョンを上げると「ステータスバーにコンテンツが被って読めない」「ナビゲーションバーにボタンが隠れて押せない」といったレイアウト崩れが容赦なく発生します。

しかもAndroid 16ではオプトアウト手段すら廃止されるため、実質的に逃げ道がなくなります。

この記事ではEdge-to-Edgeの概要からAndroid 15・16での対応方針、よくあるハマりどころまでまとめています。

目次

この記事でわかること

  • Edge-to-Edgeとは何か(基本的な概念)
  • Android 15(targetSdk 35)とAndroid 16(targetSdk 36)での変更内容の違い
  • Google Playのtarget API level要件と期限
  • 対応方針の選び方(View・Compose別)
  • オプトアウトの一時しのぎと、その限界
  • よくあるレイアウト崩れのパターンと解決の糸口

Edge-to-Edgeとは

Edge-to-Edgeとは、アプリのコンテンツをステータスバーやナビゲーションバー(いわゆるシステムバー)の裏側まで描画領域を広げる表示方式です。

これまでのAndroidアプリは「システムバーの外側」に表示領域が収まる仕様でした。Edge-to-Edgeが有効になると、コンテンツがシステムバーの背後にまで広がり、見た目はスッキリするのですが対応を入れていないと大事なUIがシステムバーに隠れてしまいます。

【従来】
 ┌───────────────────┐  ← ステータスバー(アプリ領域外)
 ├───────────────────┤
 │                   │
 │   アプリコンテンツ  │
 │                   │
 ├───────────────────┤
 └───────────────────┘  ← ナビゲーションバー(アプリ領域外)

【Edge-to-Edge有効後】
 ┌───────────────────┐
 │  ステータスバー     │  ← コンテンツが裏に描画される
 │                   │
 │   アプリコンテンツ  │
 │                   │
 │  ナビゲーションバー │  ← コンテンツが裏に描画される
 └───────────────────┘

適切な対応(Insetsを使ったpadding制御)を入れることで、コンテンツがシステムバーに重ならないようにレイアウトを調整できます。

Android 15・16での変更点と対応期限

Android 15(targetSdk 35)の変更

2025年8月31日以降、Google PlayへのアプリリリースにはtargetSdk 35以上が必要です。

targetSdk 35をAndroid 15以降のデバイスで実行すると、Edge-to-Edgeが強制適用されます。ただしAndroid 15の段階では、windowOptOutEdgeToEdgeEnforcement 属性を true に設定することでオプトアウト(無効化)が可能です。

AndroidManifest.xml

xml

<application
    android:windowOptOutEdgeToEdgeEnforcement="true"
    ...>
</application>

これを設定すると、Android 15端末でもEdge-to-Edgeが適用されない従来の挙動を維持できます。

ただしこれはあくまで 一時しのぎ です。

Android 16(targetSdk 36)の変更

Android 16(targetSdk 36)では、windowOptOutEdgeToEdgeEnforcement によるオプトアウトが廃止されます。

つまり、targetSdk 36を設定したアプリはEdge-to-Edgeを回避できなくなります。現時点でのtargetSdk 36の要件適用時期は公式発表待ちですが、毎年8月末に引き上げられるパターンを踏まえると、2026年8月31日ごろに要求される可能性が高いと考えておくのが無難です。

OSバージョンごとの挙動をまとめると以下のとおりです。

targetSdkAndroid 14以下Android 15Android 16以上
34以下Edge-to-Edge無効Edge-to-Edge無効Edge-to-Edge強制
35Edge-to-Edge無効Edge-to-Edge強制(オプトアウト可)Edge-to-Edge強制
36Edge-to-Edge無効Edge-to-Edge強制Edge-to-Edge強制(オプトアウト不可)

注意: Android 16以上の端末では、targetSdkのバージョンに関わらずEdge-to-Edgeが適用されるケースがあります。公式ドキュメントを必ずご確認ください。 動作の変更点: Android 16 以上をターゲットとするアプリ | Android Developers

対応方針の選び方

短期対応(Android 15のみ)

時間がないときの応急処置として windowOptOutEdgeToEdgeEnforcement="true" でオプトアウトする方法があります。手順は前述の通りです。ただしAndroid 16ではこの手段が使えないため、根本対応を先送りにしているだけという認識は持っておく必要があります。

長期対応(推奨):全バージョンでEdge-to-Edgeを正式有効化する

全OSバージョンで意図的にEdge-to-Edgeを有効化し、WindowInsetsを使ってpaddingを制御するアプローチが推奨されます。Android 16での強制適用に備えつつOSバージョン間の挙動を統一できるため保守性が高くなります。

Viewベースの場合

Step 1: enableEdgeToEdge() をActivityで呼び出す

MainActivity.kt

kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()  // ← これを追加
    setContentView(R.layout.activity_main)
}

Step 2: ViewCompat.setOnApplyWindowInsetsListener でInsetsを取得してpaddingを設定する

kotlin

ViewCompat.setOnApplyWindowInsetsListener(binding.rootLayout) { v, insets ->
    val sysBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    val ime = insets.getInsets(WindowInsetsCompat.Type.ime())
    val bottomPadding = maxOf(sysBars.bottom, ime.bottom)
    v.setPadding(sysBars.left, sysBars.top, sysBars.right, bottomPadding)
    insets
}
  • Type.systemBars(): ステータスバーとナビゲーションバーのインセットを取得します。
  • Type.ime(): キーボード(ソフトウェアキーボード)のインセットを取得します。
  • maxOf(sysBars.bottom, ime.bottom): キーボード表示時にナビゲーションバーより背が高い場合を考慮してpaddingを切り替えています。

Jetpack Composeの場合

Composeで androidx.compose.material3Scaffold を使っている場合、Material3が自動でWindowInsetsを処理してくれるため追加対応は最小限で済みます。

kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContent {
        MyAppTheme {
            Scaffold(
                modifier = Modifier.fillMaxSize()
            ) { paddingValues ->
                // paddingValuesをコンテンツに渡す
                MainContent(modifier = Modifier.padding(paddingValues))
            }
        }
    }
}

ただし子画面(Child Screen)で別途 Scaffold を使っている場合、Insetsが二重に適用されて余白が広くなりすぎることがあります。この場合は子側のWindowInsetsを WindowInsets(0, 0, 0, 0) で打ち消す対応が必要になります。

kotlin

@Composable
fun ChildScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("画面タイトル") },
                windowInsets = WindowInsets(0, 0, 0, 0)  // ← ここで打ち消す
            )
        },
        contentWindowInsets = WindowInsets(0, 0, 0, 0)  // ← ここも
    ) { paddingValues ->
        LazyColumn(contentPadding = paddingValues) { ... }
    }
}

よくあるレイアウト崩れのパターン

ステータスバーにタイトルや文字が被る

ステータスバー分の paddingTop が取れていないケースです。Type.statusBars() のインセットを取得してpaddingに反映させます。

ナビゲーションバーにボタンが隠れて押せない

ナビゲーションバー分の paddingBottom が不足しているケースです。BottomNavigationやFABなど画面下部の操作要素に特に注意が必要です。

キーボード表示時にコンテンツが隠れる

Type.ime() のインセットを考慮していないと、キーボード表示時にスクロールできずコンテンツが見えなくなります。前述の maxOf(sysBars.bottom, ime.bottom) で対応できます。

画面上部に謎の余白が生まれる

Composableのネスト構造でInsetsが二重適用されているケースです。Scaffold + 子Scaffoldの組み合わせで起きやすいです。

Q&A

Android 14以下のデバイスではどうなる?

enableEdgeToEdge() を呼び出した場合でもAndroid 14以下では従来の表示になります。対応を入れた場合はAndroid 14以下でもデザインが崩れないか確認が必要です。

targetSdk 36への対応期限はいつまで?

2026年6月現在、Googleから正式な期限は発表されていません。ただし毎年8月末に要件が引き上げられるパターンを踏まえると、2026年8月末ごろにtargetSdk 36が要求される可能性が高いと見込んでおくのが無難です。公式のGoogle Play ターゲット API レベル要件を定期的に確認することをおすすめします。

React Nativeで開発しているアプリにも影響ある?

React NativeアプリもAndroidネイティブのEdge-to-Edge変更の影響を受けます。React Native側での対応については各ライブラリのドキュメントや公式の対応状況を確認してください。

オプトアウトしたまま放置するとどうなる?

targetSdk 35の間はオプトアウトで動作し続けますが、targetSdk 36への引き上げが必要になったタイミングで強制的に対応が必要になります。

まとめ

  • Android 15(targetSdk 35)からEdge-to-Edgeが強制適用されるよ。
  • 2025年8月31日以降はtargetSdk 35以上でないとGoogle Playにリリースできないよ。
  • Android 15ではwindowOptOutEdgeToEdgeEnforcement="true"でオプトアウトできるけど、Android 16では廃止されるよ。
  • 長期的にはWindowInsetsを使ったpaddingの制御で全OSバージョンの挙動を統一するのが推奨アプローチだよ。
  • Jetpack Compose(Material3)はScaffoldが自動でInsetsを処理してくれるけど、子画面での二重適用には注意してね。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次