LoginSignup
8
5

More than 3 years have passed since last update.

Android QのGesturalNavigationとDrawerLayoutの競合に対するボクなりの最適解

Last updated at Posted at 2019-05-28

Android Qでは Gestural Navigation が基本となり、アプリ側で対応が必要となりました。

Gestural Navigationについて詳しくはこちらのスライドで触れています。

何に注意が必要なのか?

上のスライドでも触れていますが、Gestural Navigationは

画面の両サイドからスワイプすると戻る挙動
をします。

ということは、これまでDrawerLayoutを画面左右からのスワイプで表示していたのが競合します。
何も対応しないと

Drawerを表示したいのに、アプリが終了してしまう

という動きになってしまい、ユーザー体験は良くないものとなってしまいます。

どう対処したらいい?

方法は2つあります。

  • DrawerLayoutをAndroidXの物を使う
  • 自分で対応する(私が出した最適解)

DrawerLayoutをAndroidXの物を使う

build.gradle
implementation 'androidx.drawerlayout:drawerlayout:1.1.0-alpha01'

このAndroidXのDrawerLayoutを使うと
- Drawerが閉じてる時:Drawerが開く
- Drawerが開いている時:アプリが終了する
という挙動になります。

果たしてこれは本当にユーザーが求めている挙動なのか・・・?
Gestural Navigationは始まったばかりの機能なので「ユーザーが求める挙動」の定義は難しいですが
なんだか違和感があります。

ということで、最適解を考えてみました。

自分で対応する(私が出した最適解)

Android版のTwitter公式アプリがやっているDrawerの出し方が、体験としては非常に良いのではないかと感じています。

  • 画面のどこでも左から右にスワイプ:Drawerが開く
  • 画面両サイドからスワイプ:Gestural Navigationでアプリが終了する

私はAndroid Q Beta3を入れて日々Gestural Navigationを使っていますが、

画面両サイドからスワイプすると「戻る」挙動

を感覚的に求めていました。なので、Twitter公式アプリの挙動が最適解なのではないかと感じました。

具体的な実装

サンプルコードは https://github.com/furusin/GesturalNavigationSample に上げています。

画面上でのスワイプ(フリック)の検知は
Androidでフリックイベントを取得する
の記事を参考にしました。

AndroidXのDrawerLayoutを使わず、GestureDetector.SimpleOnGestureListeneronFling() で画面上でのスワイプ(フリック)を検知するようにしています。

MainActivity.kt
class MainActivity : AppCompatActivity() {
    companion object {
        // X軸最低スワイプ距離
        private const val SWIPE_MIN_DISTANCE = 50
        // X軸最低スワイプスピード
        private const val SWIPE_THRESHOLD_VELOCITY = 200
    }

    lateinit var gestureDetector: GestureDetector

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 左から右へのスワイプでDrawerLayoutを開く
        gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
            override fun onFling(
                event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float
            ): Boolean {
                // 左から右へのフリックの判定
                if (event2.x - event1.x > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    findViewById<DrawerLayout>(R.id.drawerLayout).openDrawer(Gravity.LEFT)
                }
                return false
            }
        })
    }

    // ListViewの場合はonTouchEventではなく
    // dispatchTouchEventを使う
    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        // GestureDetectorにイベントを流す
        gestureDetector.onTouchEvent(ev)
        return super.dispatchTouchEvent(ev)
    }
}

まとめ

いかがだったでしょうか。
まだ本記事執筆時点(2019/05/28)ではAndroid QはBeta3ですし、Gestural Navigationの挙動自体が変わる可能性はあります。

ですので「こんな考え方(回避のしかた)もあるんだな」程度で捉えていただければと思います。

また、「横向きスクロールのViewがあった場合どうするの」といった問題もあり、そこは考慮できてません。
もう少し考える必要はあるかなぁと思います。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5