Android 实现手势返回

1,582 阅读2分钟

在App的使用过程中,经常会用到返回键。随着主流手机屏幕尺寸的增大,以及全面屏的普及,手势返回也越来越常见。如果App仅仅是在页面上固定的位置放个返回按钮,而不支持手势返回,对于用户来说,体验感就没那没好了。

本篇文章介绍下如何通过GestureDetector来实现手势回退功能。

GestureDetector

直接通过重写onTouchEvent来实现手势返回也是可以的,但GestureDetector是Android提供的用于检测常用手势的类,我们封装好了计算的代码,方便了很多。

GestureDetector实例化时需要传入的参数之一是实现GestureDetector.OnGestureListener接口的类,发生特定轻触事件时,GestureDetector.OnGestureListener 中相应的回调就会触发。

可以直接实现GestureDetector.OnGestureListener

private val gestureListener = object : GestureDetector.OnGestureListener {
        override fun onDown(e: MotionEvent?): Boolean {
            //首次按下
            return false
        }

        override fun onSingleTapUp(e: MotionEvent?): Boolean {
            //单击
            return false
        }

        override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
            //滑动
            return false
        }

        override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
            //快速滑动
            return false
        }

        override fun onShowPress(e: MotionEvent?) {
            //触摸
        }

        override fun onLongPress(e: MotionEvent?) {
            //长按
        }
    }

或者,当你仅需关注部分手势时,可以继承GestureDetector.SimpleOnGestureListener


 private val simpleOnGestureListener = object : GestureDetector.SimpleOnGestureListener() {
        override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
            return super.onFling(e1, e2, velocityX, velocityY)
        }
    }

GestureDetector.OnGestureListener中的返回值表示是否消耗了事件,true表示事件被消耗,false表示未消耗继续向下传递。

实现手势返回

我们可以创建基类BaseActivity,在BaseActivity中实现手势返回,所有的Activity都继承BaseActivity,那么所有的Activity都支持手势返回了。

实现代码如下:

open class BaseGestureDetectorActivity : AppCompatActivity() {
    
    private lateinit var gestureDetectorCompat: GestureDetectorCompat
    private var widthPixels: Int = 0
    private val simpleOnGestureListener = object : GestureDetector.SimpleOnGestureListener() {
        override fun onDown(e: MotionEvent): Boolean {
            onUserInteraction()
            return e.x < 100 || e.x > resources.displayMetrics.widthPixels - 100
        }
        
        override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
            //计算移动的距离
            val distantX = abs((e2?.x ?: 0f) - (e1?.x ?: 0f))
            val distantY = abs((e2?.y ?: 0f) - (e1?.y ?: 0f))
            e1?.x?.let {
                //判定事件起点是屏幕左侧或右侧边缘(根据需求也可以只判断一侧)
                if (it < 100 || it > widthPixels - 100) {
                    //判定x轴移动距离大于y轴移动距离
                    if (distantX > distantY) {
                        onBackPressed()
                    }
                }
            }
            return super.onFling(e1, e2, velocityX, velocityY)
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        gestureDetectorCompat = GestureDetectorCompat(this, simpleOnGestureListener)
        widthPixels = resources.displayMetrics.widthPixels
    }

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        val consumeMotionEvent = gestureDetectorCompat.onTouchEvent(ev)
        return if (consumeMotionEvent) {
            true
        } else {
            super.dispatchTouchEvent(ev)
        }
    }
}

这边使用onFling而不是onScroll,是为了与普通的滑动事件区分。

效果如图:

9421c404f583df0299281ee4ba14636f.gif