kako.dev

開発、自作アプリのこと

ジェスチャーナビゲーションに対応させる

この記事は Android #2 Advent Calendar 2019 13日目の記事です。

qiita.com

約1月半前、Pixel4を買いました。 ジェスチャーナビゲーションに慣れつつあるこの頃ですが、最近は欲が出てきて自分のアプリをジェスチャーナビゲーションに対応させたくなってきたので書きました。

ジェスチャーナビゲーション

f:id:h3-birth:20191213040930j:plain:w300

ホームや戻る操作などを全てジェスチャーで操作可能なナビゲーションシステムです。 Android10以降でサポートされています。

以前までは3つボタンのあのAndroidらしいシステムも好きだったのですが、ジェスチャーナビゲーションも好きになってきました。

アプリを対応させるには

大きく以下の2つを行います。

  • アプリの表示域を全画面化
  • 競合するアプリのジェスチャーに対応

アプリの表示域を全画面化

Edge-to-edge

ジェスチャーナビゲーション未対応のアプリだと、以下のように上部のステータスバーと下部のナビゲーションバーの余白が残ったままです。これではAndroid10らしくありません。

f:id:h3-birth:20191213040506j:plain

Edge-to-edgeはステータスバーとナビゲーションバーの背後までコンテンツを表示させてより没入感を高めていきます。

システム バーを透明にする

以下のようにテーマを設定します。

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:navigationBarColor">@android:color/transparent</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

動的に変更したい場合は、Window.setNavigationBarColor()Window.setStatusBarColor() を使用することができます。

UIフラグを設定する

全画面にViewをレイアウトさせるためにフラグを設定します。

view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)

CoordinatorLayoutDrawerLayoutはすでにSYSTEM_UI_FLAG_LAYOUT_STABLE フラグと SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN フラグが設定されていることがあります。 他のフラグをsetSystemUiVisibilityで設定している場合は、上書きしてしまわないように気をつける必要があります。

ここまでするとだいたいこんな感じになると思います。 画面全体使われてていいですね。

f:id:h3-birth:20191213040542j:plain

インセットを使用する

全画面にレイアウトさせるようになるとFabなどの画面縁での操作がしにくくなる恐れがあります。

カスタムビュー などを使用している場合、以下のようにインセットを使用してマージン(またはパディング)を設定します。

ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
            v.updatePadding(bottom = insets.systemWindowInsetBottom)
            insets
        }

ただし、CoordinatorLayoutConstraintLayoutは自動的に対応されるので意識する必要はありません。

ジェスチャー領域範囲をオプトアウトする

ジェスチャーナビゲーションでは、画面左右縁や下の操作はシステムUIの操作になります。(戻る、ホームを表示など)

DrawerLayout および SeekBar コンポーネントは、自動でジェスチャーナビゲーションに対応されているので対処は不要です。

カスタムビュー などで対応させてたい場合には、onLayoutonDraw で対応させることができます。

var exclusionRects = listOf(rect1, rect2, rect3)

fun onLayout(
        changedCanvas: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
  // Update rect bounds and the exclusionRects list
  setSystemGestureExclusionRects(exclusionRects)
}

fun onDraw(canvas: Canvas) {
  // Update rect bounds and the exclusionRects list
  setSystemGestureExclusionRects(exclusionRects)
}

ただし、このsetSystemGestureExclusionRects を使うことはあまり推奨されていません。 ユーザーが画面縁の操作は戻るなどを期待しているためです。

例外として、

  • 音楽や動画のSeekBar
  • 画像のトリミングなどのドラッグ

が挙げられます。

ただし、https://medium.com/androiddevelopers/gesture-navigation-handling-gesture-conflicts-8ee9c2665c69 の音楽プレイヤーSeekBarの例のように、レイアウトを作り直すなどして競合を避けることも視野に入れておきたいものです。

まとめ

UXとしての向上を図るイメージですね。

自分のアプリにも早く対応させてみた〜い!!

参考

https://developer.android.com/guide/navigation/gesturenav?hl=ja

https://medium.com/androiddevelopers/gesture-navigation-handling-gesture-conflicts-8ee9c2665c69