“轻触手势”是指用户将一个或多个手指放在触摸屏上,并且您的应用会将这种轻触模式解读为特定手势。手势检测可相应地划分为两个阶段:
- 收集轻触事件的相关数据。
- 解读数据以确定其是否符合您的应用支持的任何手势的条件。
支持库类
本课程中的示例使用 GestureDetectorCompat
和 MotionEventCompat
类。这些类均包含在支持库中。您应尽量使用支持库类,以便与搭载 Android 1.6 及更高版本的设备兼容。请注意,MotionEventCompat
不能替代 MotionEvent
类。相反,它可以提供静态实用程序方法,您可以向这些方法传递 MotionEvent
对象,以接收与相应事件相关的预期操作。
收集数据
当用户在屏幕上放置一个或多个手指时,触发事件的视图上的回调onTouchEvent()
。
对于最终被识别为手势的每个轻触事件(定位、按压、调整大小、添加另一个手指等)序列,onTouchEvent()
都会多次被触发。
手势会在用户首次轻触屏幕时开始,在系统跟踪用户手指的位置时继续,并在捕获到用户手指离开屏幕的最终事件时结束。在整个互动过程中,传送给 onTouchEvent()
的 MotionEvent
会提供每次互动的详细信息。您的应用可以使用 MotionEvent
提供的数据确定是否发生了它所关注的手势。
捕获 Activity 或视图的轻触事件
如需拦截 Activity 或视图中的轻触事件,请替换 onTouchEvent()
回调。
以下代码段使用 getActionMasked()
从 event
参数中提取用户执行的操作。这可为您提供所需的原始数据,以确定是否发生了您所关注的手势:
Kotlin
class MainActivity : Activity() { ... // This example shows an Activity, but you would use the same approach if // you were subclassing a View. override fun onTouchEvent(event: MotionEvent): Boolean { val action: Int = MotionEventCompat.getActionMasked(event) return when (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, but you would use the same approach if // you were subclassing a View. @Override public boolean onTouchEvent(MotionEvent event){ int action = MotionEventCompat.getActionMasked(event); switch(action) { 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); } }
然后,您可以自行处理这些事件,以确定是否已发生某个手势。您需要对自定义手势执行此类处理。但是,如果应用使用的是常用手势(例如点按两次、长按、滑动等),就可以利用 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_MOVE
和 ACTION_UP
事件调用监听器。这是因为 ACTION_DOWN
是所有轻触事件的起点。
如需创建自定义视图,可以按照上文所述替换 onTouchEvent()
。
检测手势
Android 提供了用于检测常用手势的 GestureDetector
类。该类支持的一些手势包括 onDown()
、onLongPress()
、onFling()
等。您可以将 GestureDetector
与上述 onTouchEvent()
方法结合使用。
检测所有受支持的手势
实例化 GestureDetectorCompat
对象时,此对象需要的参数之一是实现 GestureDetector.OnGestureListener
接口的类。发生特定轻触事件时,GestureDetector.OnGestureListener
会通知用户。如需让 GestureDetector
对象能够接收事件,您可以替换视图或 Activity 的 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
,最佳做法都是实现返回 true
的 onDown()
方法。这是因为所有手势都以 onDown()
消息开头。如果您从 onDown()
返回 false
(与 GestureDetector.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){ this.mDetector.onTouchEvent(event); 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; } } }