Android 15 以降を搭載したデバイスで SDK 35 以降をターゲットにすると、アプリはエッジ ツー エッジ表示になります。ウィンドウは、システムバーの背後に描画することで、ディスプレイの幅と高さ全体に広がります。システムバーには、ステータスバー、キャプション バー、ナビゲーション バーがあります。
多くのアプリにはトップ アプリバーがあります。トップ アプリバーは画面の上端まで伸び、ステータスバーの背後に表示されます。必要に応じて、コンテンツのスクロール時に上部のアプリバーをステータスバーの高さに縮小できます。
多くのアプリには、ボトム アプリバーまたはボトム ナビゲーション バーもあります。また、これらのバーは画面の下端まで伸び、ナビゲーション バーの背後に表示する必要があります。そうでない場合は、ナビゲーション バーの背後にスクロール コンテンツを表示する必要があります。
アプリにエッジツーエッジ レイアウトを実装する場合は、次の点に注意してください。
- エッジ ツー エッジ表示を有効にする
- 視覚的な重複を処理します。
- システムバーの背後にスクリムを表示することを検討してください。
エッジ ツー エッジ表示を有効にする
アプリが SDK 35 以降をターゲットとしている場合、Android 15 以降のデバイスではエッジツーエッジが自動的に有効になります。
以前のバージョンの Android でエッジツーエッジを有効にするには、次の操作を行います。
アプリまたはモジュールの
build.gradle
ファイルで、androidx.activity
ライブラリへの依存関係を追加します。Kotlin
dependencies { val activity_version =
activity_version
// Java language implementation implementation("androidx.activity:activity:$activity_version") // Kotlin implementation("androidx.activity:activity-ktx:$activity_version") }Groovy
dependencies { def activity_version =
activity_version
// Java language implementation implementation 'androidx.activity:activity:$activity_version' // Kotlin implementation 'androidx.activity:activity-ktx:$activity_version' }enableEdgeToEdge
拡張関数をアプリにインポートします。
Activity
の onCreate
で enableEdgeToEdge
を呼び出して、エッジ ツー エッジを手動で有効にします。setContentView
の前に呼び出す必要があります。
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) ... }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { EdgeToEdge.enable(this); super.onCreate(savedInstanceState); ... }
デフォルトでは、enableEdgeToEdge()
はシステムバーを透明にします。ただし、ステータスバーに半透明のスクリムが適用される 3 ボタン ナビゲーション モードを除きます。システム アイコンとスクリムの色は、システムのライトモードまたはダークモードに基づいて調整されます。
enableEdgeToEdge()
関数は、アプリを端から端まで配置する必要があることを自動的に宣言し、システムバーの色を調整します。
enableEdgeToEdge()
関数を使用せずにアプリでエッジ ツー エッジ表示を有効にするには、エッジ ツー エッジ表示を手動で設定するをご覧ください。
インセットを使用して重なりを処理する
図 3 に示すように、アプリのビューの一部がシステムバーの背後に描画される場合があります。
重複に対処するには、インセットに反応します。インセットは、画面のどの部分がシステム UI(ナビゲーション バーやステータスバーなど)と交差するかを指定します。交差とは、コンテンツの上に表示されることを意味する場合もありますが、システム ジェスチャーに関係することもあります。
アプリを端から端まで表示する場合に適用されるインセットのタイプは次のとおりです。
システムバーのインセット: タップ可能で、システムバーで視覚的に隠れてはならないビューに最適です。
ディスプレイ カットアウトの切り欠き: デバイスの形状により画面の切り欠きがある可能性がある領域。
システム ジェスチャー インセット: アプリよりも優先される、システムで使用されるジェスチャー ナビゲーション領域。
システムバーのインセット
システムバー インセットは、最もよく使用されるインセットです。システム UI がアプリの上に Z 軸で表示される領域を表します。タップ可能で、システムバーで視覚的に隠れてはならないアプリ内のビューを移動またはパディングする場合に適しています。
たとえば、図 3 のフローティング アクション ボタン(FAB)は、ナビゲーション バーによって部分的に隠れています。
ジェスチャー モードまたはボタンモードでこのような視覚的な重複を回避するには、WindowInsetsCompat.Type.systemBars()
で getInsets(int)
を使用してビューの余白を増やすことができます。
次のコード例は、システム バーの切り欠きを実装する方法を示しています。
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(fab) { v, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) // Apply the insets as a margin to the view. This solution sets // only the bottom, left, and right dimensions, but you can apply whichever // insets are appropriate to your layout. You can also update the view padding // if that's more appropriate. v.updateLayoutParams<MarginLayoutParams> { leftMargin = insets.left bottomMargin = insets.bottom rightMargin = insets.right } // Return CONSUMED if you don't want want the window insets to keep passing // down to descendant views. WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(fab, (v, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); // Apply the insets as a margin to the view. This solution sets only the // bottom, left, and right dimensions, but you can apply whichever insets are // appropriate to your layout. You can also update the view padding if that's // more appropriate. MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); mlp.leftMargin = insets.left; mlp.bottomMargin = insets.bottom; mlp.rightMargin = insets.right; v.setLayoutParams(mlp); // Return CONSUMED if you don't want want the window insets to keep passing // down to descendant views. return WindowInsetsCompat.CONSUMED; });
このソリューションを図 3 の例に適用すると、図 4 に示すように、ボタンモードで視覚的な重複がなくなります。
図 5 に示すように、ジェスチャー ナビゲーション モードにも同じことが言えます。
ディスプレイ カットアウトの切り欠き
一部のデバイスにはディスプレイのカットアウトがあります。通常、カットアウトは画面の上部にあり、ステータスバーに含まれます。デバイスの画面が横向きの場合、切り欠きは垂直エッジに配置される場合があります。デフォルトでは、アプリはディスプレイ カットアウト内に描画されるため、アプリが画面に表示するコンテンツに応じて、ディスプレイ カットアウトを回避するためのパディングを実装する必要があります。
たとえば、多くのアプリ画面にはアイテムのリストが表示されます。ディスプレイの切り欠きやシステムバーでリストアイテムを隠さないでください。
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, insets -> val bars = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) v.updatePadding( left = bars.left, top = bars.top, right = bars.right, bottom = bars.bottom, ) WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(mBinding.recyclerView, (v, insets) -> { Insets bars = insets.getInsets( WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() ); v.setPadding(bars.left, bars.top, bars.right, bars.bottom); return WindowInsetsCompat.CONSUMED; });
システムバーとディスプレイの切り欠きタイプの論理 OR を取得して、WindowInsetsCompat
の値を決定します。
clipToPadding
を RecyclerView
に設定して、パディングがリストアイテムとともにスクロールされるようにします。これにより、次の例に示すように、ユーザーがスクロールするとアイテムがシステムバーの背後に移動します。
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
システム ジェスチャー インセット
システム ジェスチャー インセットは、システム ジェスチャーがアプリよりも優先されるウィンドウの領域を表します。これらの領域は図 6 の黄色で示されています。
システムバーの切り欠きと同様に、getInsets(int)
と WindowInsetsCompat.Type.systemGestures()
を使用して、システム操作の切り欠きの重複を回避できます。
このインセットは、スワイプ可能なビューを端から離れた位置に移動またはパディングする場合に使用します。一般的なユースケースには、ボトムシート、ゲームでのスワイプ、ViewPager2
を使用して実装されたカルーセルなどがあります。
Android 10 以降では、システム ジェスチャー インセットには、ホーム表示ジェスチャー用の下部インセットと、戻るジェスチャー用の左右のインセットが含まれます。
次のコード例は、システム ジェスチャーの枠線を実装する方法を示しています。
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) // Apply the insets as padding to the view. Here, set all the dimensions // as appropriate to your layout. You can also update the view's margin if // more appropriate. view.updatePadding(insets.left, insets.top, insets.right, insets.bottom) // Return CONSUMED if you don't want the window insets to keep passing down // to descendant views. WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()); // Apply the insets as padding to the view. Here, set all the dimensions // as appropriate to your layout. You can also update the view's margin if // more appropriate. view.setPadding(insets.left, insets.top, insets.right, insets.bottom); // Return CONSUMED if you don't want the window insets to keep passing down // to descendant views. return WindowInsetsCompat.CONSUMED; });
マテリアル コンポーネント
BottomAppBar
、BottomNavigationView
、NavigationRailView
、NavigationView
など、多くのビューベースの Android マテリアル コンポーネント(com.google.android.material)は、インセットを自動的に処理します。{:.external}
ただし、AppBarLayout
はインセットを自動的に処理しません。android:fitsSystemWindows="true"
を追加して、上部インセットを処理します。
Compose のマテリアル コンポーネントでインセットを処理する方法をご覧ください。
下位互換性のあるインセット ディスパッチ
子ビューへのインセットのディスパッチを停止し、過剰なパディングを回避するには、WindowInsetsCompat.CONSUMED
定数を使用してインセットを使用します。ただし、Android 10(API レベル 29 以前)を搭載したデバイスでは、WindowInsetsCompat.CONSUMED
を呼び出した後にインセットが兄弟にディスパッチされないため、意図しない視覚的な重複が発生する可能性があります。
サポートされているすべての Android バージョンでインセットが兄弟にディスパッチされることを確認するには、インセットを使用する前に ViewGroupCompat#installCompatInsetsDispatch
を使用します。これは AndroidX Core および Core-ktx 1.16.0-alpha01 以降で利用できます。
Kotlin
// Use the i.d. assigned to your layout's root view, e.g. R.id.main val rootView = findViewById(R.id.main) // Call before consuming insets ViewGroupCompat.installCompatInsetsDispatch(rootView)
Java
// Use the i.d. assigned to your layout's root view, e.g. R.id.main LinearLayout rootView = findViewById(R.id.main); // Call before consuming insets ViewGroupCompat.installCompatInsetsDispatch(rootView);
没入モード
一部のコンテンツは、全画面表示で視聴することで、より没入感のあるエクスペリエンスを提供できます。WindowInsetsController
ライブラリと WindowInsetsControllerCompat
ライブラリを使用して、没入モードのシステムバーを非表示にできます。
Kotlin
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView) // Hide the system bars. windowInsetsController.hide(Type.systemBars()) // Show the system bars. windowInsetsController.show(Type.systemBars())
Java
Window window = getWindow(); WindowInsetsControllerCompat windowInsetsController = WindowCompat.getInsetsController(window, window.getDecorView()); if (windowInsetsController == null) { return; } // Hide the system bars. windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()); // Show the system bars. windowInsetsController.show(WindowInsetsCompat.Type.systemBars());
この機能の実装について詳しくは、没入モードのシステムバーを非表示にするをご覧ください。
システムバーのアイコン
enableEdgeToEdge
を呼び出すと、デバイスのテーマが変更されたときにシステムバーのアイコンの色が更新されます。
エッジ ツー エッジに変更する際に、アプリの背景と対照的な色になるようにシステムバーのアイコンの色を手動で更新しなければならない場合があります。たとえば、明るいステータスバー アイコンを作成するには:
Kotlin
WindowCompat.getInsetsController(window, window.decorView) .isAppearanceLightStatusBars = false
Java
WindowCompat.getInsetsController(window, window.getDecorView()) .setAppearanceLightStatusBars(false);
システムバーの保護
アプリが SDK 35 以降をターゲットとしている場合、エッジツーエッジが適用されます。システム ステータスバーとジェスチャー ナビゲーション バーは透明ですが、3 ボタン ナビゲーション バーは半透明です。
デフォルトの半透明の 3 ボタン ナビゲーションの背景保護を削除するには、Window.setNavigationBarContrastEnforced
を false
に設定します。
参考情報
WindowInsets
、ジェスチャー ナビゲーション、インセットの仕組みについて詳しくは、次のリファレンスをご覧ください。
- Android システムバー
- Android 15 のエッジ ツー エッジの適用に関するインセット処理のヒント
- WindowInsets - リスナーからレイアウトまで
- ジェスチャー ナビゲーション: インセット
- Android でインセットがどのように機能するか