この記事は概ね、Gesture Navigation: Going edge-to-edge (I)とGesture Navigation: Handling visual overlaps (II)の記事に沿って書いています。
Android Qから新しいシステムナビゲーションが追加されました。
iOSではおなじみですが、ボタンではなくジェスチャーによって前の画面に戻ったりAndroidのホーム画面に遷移することが可能になりました。
ジェスチャー ナビゲーション: エッジ ツー エッジへの対応より
このジェスチャーナビゲーションを利用することで、従来の3ボタンナビゲーションよりも多くの描画領域をアプリに提供できるため、より没入感のあるUXを提供することができるようになりました。
Edge-to-edgeとは
いわゆるこれです。
ジェスチャー ナビゲーション: エッジ ツー エッジへの対応より
よりアプリへの没入感の高いUXを提供できる表現のことをこう呼びます。
より詳しくはぜひGoogle Developersのブログを御覧ください。
*Gesture Navigation: Going edge-to-edge (I)*に関しては日本語訳記事もあるので一読すると理解が深まります!
実装方法
前提
本記事はandroidx.core:core-ktx:1.2.0-rc01
を用いてコードの検証を行っています。
また、コードの中でデータバインディングを用いている箇所があります。
dependencies {
...
implementation 'androidx.core:core-ktx:1.2.0-rc01'
}
題材として、適当な文字列をリストで表示。またFloatingActionButtonのある以下のようなUIをEdge-to-edge対応させていく流れで進めてみます。
普通に作るとこんな感じ。コードはすべてこちらに。
左はAPI 27、右はAPI 29です。
ブランチを変えるとそれぞれのステップに対応しています。
全画面表示に対応する
まずはステータスバーの背後にアプリを描画できるようにしましょう。
これ自体は非常に簡単で、systemUiVisibility
にflagを設定してあげるだけです。
view.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
ステータスバーの背後のみや、ナビゲーションバーの背後のみということも可能です。
自分のアプリに適した設定にしましょう。
システムバーの色を変える
次に重なって表示されたUIを見えるようにするため、透過された色をシステムバーに設定しましょう。
Android Qでは単純に透明に設定するのみでよいです。システムが動的に色を調整してくれます。
Pie以前では動的な色の調整は行われないため、半透明に設定することが推奨されています。
その際、不透明度が70%から始め、各自のアプリに表示されるコンテンツに応じて調整することが推奨されています。
<!-- styles.xml -->
<resources>
<style name="Base.App" parent="Theme.MaterialComponents.Light.NoActionBar">
...
<item name="android:navigationBarColor">@color/nav_bar</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
<!-- values/colors.xml -->
<resources>
...
<color name="light_scrim">#B3FFFFFF</color>
<color name="nav_bar">@color/light_scrim</color>
</resources>
<!-- values-v29/colors.xml -->
<resources>
<color name="nav_bar">@android:color/transparent</color>
</resources>
視覚的な重なりに対応する
最後に、システムバーの背後に描画されたUIについて適切に対応していきましょう。肝です。
たとえば、FloatingActionButtonを使っている場合、ボタンがナビゲーションバーと重なってしまっていることに気づくでしょう。
あるいは、このようにリストを表示している場合、リストの最後の要素がナビゲーションバーに重なってしまい、見づらくなってしまっています。
よくない対応
よくない対応として、それぞれのマージンあるいはパッディングにシステムバー分の高さを足すパターンがあります。
システムバーはAndroidのバージョンや端末により異なる場合場あるので、そこを考慮していない場合は表示が崩れることになります。今後のことを考えても、これは避けたほうが良いでしょう。
適切な対応
System window insetsを利用し、適切に対応していきましょう。
WindowInsetについては詳しく説明しません。こちらに非常に丁寧に説明されているのでぜひ御覧ください。
ViewCompat
のsetOnApplyWindowInsetsListener
からWindowInset
にアクセスできるので、ここでViewのマージンあるはパッディングを調整しましょう。
private fun handlingInsets(view: View) {
// リソースからマージンを取得
val fabMargin = resources.getDimensionPixelSize(R.dimen.fab_margin)
// XML等から既にviewで指定されているpaddingを取得
val listBottomPadding = binding.listText.paddingBottom
// WindowInsetsにアクセス
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
// マージンを更新するときはこんな感じ
binding.toolbar.updateLayoutParams<AppBarLayout.LayoutParams> {
topMargin = insets.systemWindowInsetTop
}
binding.fab.updateLayoutParams<CoordinatorLayout.LayoutParams> {
leftMargin = fabMargin + insets.systemWindowInsetLeft
rightMargin = fabMargin + insets.systemWindowInsetRight
bottomMargin = fabMargin + insets.systemWindowInsetBottom
}
// パッディングを更新するときはこんな感じ
binding.listText.updatePadding(
bottom = insets.systemWindowInsetBottom + listBottomPadding
)
insets
}
}
その他の対応方法
上記で説明した流れは、Insetterというライブラリで一貫して対応が可能です。
ここまで説明しといてなんですが、かなり便利なのでこちらを使うのもオススメです。
簡単に解説したスライドを公開しているので、参考までに。
まとめ
・入門は簡単だけど、それぞれでWindowInsetsに対応するのに骨が折れそう
・UXは高まるので対応していきたい。