检测常用手势

尝试 Compose 方法
Jetpack Compose 是推荐的 Android UI 工具包。了解如何在 Compose 中使用触摸和输入。

当用户将一个或多个手指放在触摸屏上,并且您的应用将此触摸模式解释为手势时,就会发生触摸手势。手势检测有两个阶段

  1. 收集触摸事件数据。
  2. 解释数据以确定它是否符合您的应用支持的手势标准。

AndroidX 类

本文档中的示例使用GestureDetectorCompatMotionEventCompat类。这些类位于AndroidX 库中。尽可能使用 AndroidX 类以提供与早期设备的兼容性。MotionEventCompat *不是*MotionEvent类的替代品。相反,它提供静态实用程序方法,您可以将MotionEvent对象传递给这些方法以接收与该事件关联的操作。

收集数据

当用户将一个或多个手指放在屏幕上时,这会在接收触摸事件的视图上触发回调onTouchEvent()。对于每个被识别为手势的触摸事件序列(例如位置、压力、大小以及另一个手指的添加),onTouchEvent()都会触发多次。

当用户第一次触摸屏幕时,手势开始;当系统跟踪用户手指的位置时,手势继续;当捕获用户最后离开屏幕的手指的最终事件时,手势结束。在整个交互过程中,传递给onTouchEvent()MotionEvent提供了每次交互的详细信息。您的应用可以使用MotionEvent提供的数据来确定它关心的手势是否发生。

捕获活动或视图的触摸事件

要拦截ActivityView中的触摸事件,请重写onTouchEvent()回调。

以下代码片段使用getAction()event参数中提取用户执行的操作。这为您提供了确定您关心的手势是否发生所需的基本数据。

Kotlin

class MainActivity : Activity() {
    ...
    // This example shows an Activity. You can use the same approach if you are 
    // subclassing a View.
    override fun onTouchEvent(event: MotionEvent): Boolean {
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(DEBUG_TAG, "Action was DOWN")
                true
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(DEBUG_TAG, "Action was MOVE")
                true
            }
            MotionEvent.ACTION_UP -> {
                Log.d(DEBUG_TAG, "Action was UP")
                true
            }
            MotionEvent.ACTION_CANCEL -> {
                Log.d(DEBUG_TAG, "Action was CANCEL")
                true
            }
            MotionEvent.ACTION_OUTSIDE -> {
                Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element")
                true
            }
            else -> super.onTouchEvent(event)
        }
    }
}

Java

public class MainActivity extends Activity {
...
// This example shows an Activity. You can use the same approach if you are
// subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){
    switch(event.getAction()) {
        case (MotionEvent.ACTION_DOWN) :
            Log.d(DEBUG_TAG,"Action was DOWN");
            return true;
        case (MotionEvent.ACTION_MOVE) :
            Log.d(DEBUG_TAG,"Action was MOVE");
            return true;
        case (MotionEvent.ACTION_UP) :
            Log.d(DEBUG_TAG,"Action was UP");
            return true;
        case (MotionEvent.ACTION_CANCEL) :
            Log.d(DEBUG_TAG,"Action was CANCEL");
            return true;
        case (MotionEvent.ACTION_OUTSIDE) :
            Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element");
            return true;
        default :
            return super.onTouchEvent(event);
    }
}

当用户点击、触摸并按住以及拖动时,此代码会在 Logcat 中生成如下消息:

GESTURES D   Action was DOWN
GESTURES D   Action was UP
GESTURES D   Action was MOVE

对于自定义手势,您可以对这些事件进行自己的处理以确定它们是否代表您需要处理的手势。但是,如果您的应用使用常见手势,例如双击、触摸并按住、抛掷等,您可以利用GestureDetector类。GestureDetector使您可以更轻松地检测常见手势,而无需自己处理单个触摸事件。这将在检测手势中进一步讨论。

捕获单个视图的触摸事件

作为onTouchEvent()的替代方法,您可以使用setOnTouchListener()方法将View.OnTouchListener对象附加到任何View对象。这使得监听触摸事件成为可能,而无需像以下示例所示那样对现有View进行子类化。

Kotlin

findViewById<View>(R.id.my_view).setOnTouchListener { v, event ->
    // Respond to touch events.
    true
}

Java

View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        // Respond to touch events.
        return true;
    }
});

注意不要创建一个对于ACTION_DOWN事件返回false的侦听器。如果您这样做,则不会为后续的ACTION_MOVEACTION_UP事件序列调用侦听器。这是因为ACTION_DOWN是所有触摸事件的起点。

如果您正在创建自定义视图,则可以像前面所述那样重写onTouchEvent()

检测手势

Android 提供了GestureDetector类用于检测常见手势。它支持的一些手势包括onDown()onLongPress()onFling()。您可以将GestureDetector与前面介绍的onTouchEvent()方法结合使用。

检测所有支持的手势

当您实例化GestureDetectorCompat对象时,它接受的参数之一是实现GestureDetector.OnGestureListener接口的类。GestureDetector.OnGestureListener在发生特定触摸事件时通知用户。为了使您的GestureDetector对象能够接收事件,请重写视图或活动的onTouchEvent()方法并将所有观察到的事件传递给检测器实例。

在以下代码片段中,各个on<TouchEvent>方法中true的返回值表示已处理触摸事件。false的返回值将事件向下传递到视图堆栈,直到成功处理触摸为止。

在测试应用中运行以下代码片段,您可以体验到当您与触摸屏交互时操作是如何触发的,以及每次触摸事件的MotionEvent内容是什么。然后您可以看到简单的交互会产生多少数据。

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity :
        Activity(),
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {

    private lateinit var mDetector: GestureDetectorCompat

    // Called when the activity is first created.
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = GestureDetectorCompat(this, this)
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return if (mDetector.onTouchEvent(event)) {
            true
        } else {
            super.onTouchEvent(event)
        }
    }

    override fun onDown(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDown: $event")
        return true
    }

    override fun onFling(
            event1: MotionEvent,
            event2: MotionEvent,
            velocityX: Float,
            velocityY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onFling: $event1 $event2")
        return true
    }

    override fun onLongPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onLongPress: $event")
    }

    override fun onScroll(
            event1: MotionEvent,
            event2: MotionEvent,
            distanceX: Float,
            distanceY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onScroll: $event1 $event2")
        return true
    }

    override fun onShowPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onShowPress: $event")
    }

    override fun onSingleTapUp(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapUp: $event")
        return true
    }

    override fun onDoubleTap(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTap: $event")
        return true
    }

    override fun onDoubleTapEvent(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: $event")
        return true
    }

    override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event")
        return true
    }

}

Java

public class MainActivity extends Activity implements
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener{

    private static final String DEBUG_TAG = "Gestures";
    private GestureDetectorCompat mDetector;

    // Called when the activity is first created.
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = new GestureDetectorCompat(this,this);
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent event) {
        Log.d(DEBUG_TAG,"onDown: " + event.toString());
        return true;
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2,
            float velocityX, float velocityY) {
        Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
    }

    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
            float distanceY) {
        Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onShowPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
        return true;
    }
}

检测受支持手势的子集

如果您只想处理少量手势,您可以扩展GestureDetector.SimpleOnGestureListener,而不是实现GestureDetector.OnGestureListener接口。

GestureDetector.SimpleOnGestureListener为所有on<TouchEvent>方法提供了实现,对所有方法返回false。这使您可以只覆盖您关心的方法。例如,以下代码片段创建了一个扩展GestureDetector.SimpleOnGestureListener并覆盖onFling()onDown()的类。

无论您使用GestureDetector.OnGestureListener还是GestureDetector.SimpleOnGestureListener,最佳实践都是实现一个返回trueonDown()方法。这是因为所有手势都始于onDown()消息。如果您从onDown()返回falseGestureDetector.SimpleOnGestureListener默认情况下会这样做),系统会认为您要忽略其余的手势,并且不会调用GestureDetector.OnGestureListener的其他方法。这可能会导致您的应用出现意外问题。只有当您确实想要忽略整个手势时,才从onDown()返回false

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity : Activity() {

    private lateinit var mDetector: GestureDetectorCompat

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mDetector = GestureDetectorCompat(this, MyGestureListener())
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        mDetector.onTouchEvent(event)
        return super.onTouchEvent(event)
    }

    private class MyGestureListener : GestureDetector.SimpleOnGestureListener() {

        override fun onDown(event: MotionEvent): Boolean {
            Log.d(DEBUG_TAG, "onDown: $event")
            return true
        }

        override fun onFling(
                event1: MotionEvent,
                event2: MotionEvent,
                velocityX: Float,
                velocityY: Float
        ): Boolean {
            Log.d(DEBUG_TAG, "onFling: $event1 $event2")
            return true
        }
    }
}

Java

public class MainActivity extends Activity {

    private GestureDetectorCompat mDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDetector = new GestureDetectorCompat(this, new MyGestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
              return true;
        }
        return super.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        private static final String DEBUG_TAG = "Gestures";

        @Override
        public boolean onDown(MotionEvent event) {
            Log.d(DEBUG_TAG,"onDown: " + event.toString());
            return true;
        }

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2,
                float velocityX, float velocityY) {
            Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
            return true;
        }
    }
}

附加资源