Help us understand the problem. What is going on with this article?

Android Edge-to-edge入門

この記事は概ね、Gesture Navigation: Going edge-to-edge (I)Gesture Navigation: Handling visual overlaps (II)の記事に沿って書いています。


Android Qから新しいシステムナビゲーションが追加されました。
iOSではおなじみですが、ボタンではなくジェスチャーによって前の画面に戻ったりAndroidのホーム画面に遷移することが可能になりました。
gesture.gif
ジェスチャー ナビゲーション: エッジ ツー エッジへの対応より

このジェスチャーナビゲーションを利用することで、従来の3ボタンナビゲーションよりも多くの描画領域をアプリに提供できるため、より没入感のあるUXを提供することができるようになりました。

Edge-to-edgeとは

いわゆるこれです。
edgetoedge.gif
ジェスチャー ナビゲーション: エッジ ツー エッジへの対応より

よりアプリへの没入感の高い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対応させていく流れで進めてみます。
2019-11-30 22.54.21.png
普通に作るとこんな感じ。コードはすべてこちらに。
左はAPI 27、右はAPI 29です。
ブランチを変えるとそれぞれのステップに対応しています。

全画面表示に対応する

まずはステータスバーの背後にアプリを描画できるようにしましょう。
これ自体は非常に簡単で、systemUiVisibilityにflagを設定してあげるだけです。

MainActivity.kt
view.systemUiVisibility =
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

スクリーンショット 2019-11-30 22.59.14.png
善し悪しは置いておいて、ステータスバー・ナビゲーションバーの背後にUIが配置されています。

ステータスバーの背後のみや、ナビゲーションバーの背後のみということも可能です。
自分のアプリに適した設定にしましょう。

システムバーの色を変える

次に重なって表示されたUIを見えるようにするため、透過された色をシステムバーに設定しましょう。
Android Qでは単純に透明に設定するのみでよいです。システムが動的に色を調整してくれます。
Pie以前では動的な色の調整は行われないため、半透明に設定することが推奨されています。
その際、不透明度が70%から始め、各自のアプリに表示されるコンテンツに応じて調整することが推奨されています。

resources.xml
<!-- 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>

スクリーンショット 2019-11-30 23.13.59.png
描画領域が広がったことがわかるかと思います。

視覚的な重なりに対応する

最後に、システムバーの背後に描画されたUIについて適切に対応していきましょう。肝です。

たとえば、FloatingActionButtonを使っている場合、ボタンがナビゲーションバーと重なってしまっていることに気づくでしょう。
あるいは、このようにリストを表示している場合、リストの最後の要素がナビゲーションバーに重なってしまい、見づらくなってしまっています。

よくない対応

よくない対応として、それぞれのマージンあるいはパッディングにシステムバー分の高さを足すパターンがあります。
システムバーはAndroidのバージョンや端末により異なる場合場あるので、そこを考慮していない場合は表示が崩れることになります。今後のことを考えても、これは避けたほうが良いでしょう。

スクリーンショット 2019-11-30 23.15.24.png
これは、layout_marginBottomを、ナビゲーションバーの48dpとFABのマージンの16dpを足して、64dpとした時のスクリーンショットです。
API 27では一見良さそうですが、API 29でみると崩れていますね。

適切な対応

System window insetsを利用し、適切に対応していきましょう。
WindowInsetについては詳しく説明しません。こちらに非常に丁寧に説明されているのでぜひ御覧ください。

ViewCompatsetOnApplyWindowInsetsListenerからWindowInsetにアクセスできるので、ここでViewのマージンあるはパッディングを調整しましょう。

MainActivity.kt
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
    }
}

スクリーンショット 2019-11-30 23.36.57.png
WindowInsetsがバージョン毎のサイズを適切に判断してくれるので、それぞれきれいにマージンが指定できました。

その他の対応方法

上記で説明した流れは、Insetterというライブラリで一貫して対応が可能です。
ここまで説明しといてなんですが、かなり便利なのでこちらを使うのもオススメです。

簡単に解説したスライドを公開しているので、参考までに。

まとめ

・入門は簡単だけど、それぞれでWindowInsetsに対応するのに骨が折れそう
・UXは高まるので対応していきたい。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした