Base On Android Developer Guide
尽管应用不应依赖轻触手势来执行基本行为(因为手势可能无法在所有上下文中供所有用户使用,用户可能不知道需要轻触手势来执行某些操作),但向应用添加基于轻触的互动可以极大地提升其实用性和吸引力(可以考虑做一个行为备份,即用户可以同时通过更常见的方式来执行这些基本操作)。
本文中,我们主要介绍以下几个方面的内容
- 检测常用手势:了解如何使用
GestureDetector
检测基本轻触手势,例如滚动、滑动和点按两次。 - 跟踪轻触和指针移动:了解如何跟踪指针移动。
- 以动画方式显示滚动手势:了解如何使用滚动条(
Scroller
或OverScroller
)生成滚动动画以响应轻触事件。 - 处理多点触控手势:了解如何检测多指针(手指)手势。
- 拖动和缩放:了解如何实现基于轻触的拖动和缩放。
- 在 ViewGroup 中管理轻触事件:了解如何在
ViewGroup
中管理轻触事件,以确保将轻触事件正确分配给目标视图。
View的dispatchTouchEvent
为什么这里要先讲一下 View 的 dispatchTouchEvent 呢,因为我们平常处理手势事件,也是在 View 层面,由于 View 可以注册很多监听器,所以一个事件到了 View 层级,是一个怎么样的处理流程就很重要了。
下面结合一下源码来分析一下这个 “分发” 过程(这里的分发,意指分发给哪个监听事件来处理)
1 | ``` /** |
2 | * Pass the touch screen motion event down to the target view, or this |
3 | * view if it is the target. |
4 | * |
5 | * @param event The motion event to be dispatched. |
6 | * @return True if the event was handled by the view, false otherwise. |
7 | */ |
8 | public boolean dispatchTouchEvent(MotionEvent event) { |
9 | // If the event should be handled by accessibility focus first. |
10 | if (event.isTargetAccessibilityFocus()) { |
11 | // We don't have focus or no virtual descendant has it, do not handle the event. |
12 | if (!isAccessibilityFocusedViewOrHost()) { |
13 | return false; |
14 | } |
15 | // We have focus and got the event, then use normal event dispatch. |
16 | event.setTargetAccessibilityFocus(false); |
17 | } |
18 | |
19 | boolean result = false; |
20 | |
21 | if (mInputEventConsistencyVerifier != null) { |
22 | mInputEventConsistencyVerifier.onTouchEvent(event, 0); |
23 | } |
24 | |
25 | final int actionMasked = event.getActionMasked(); |
26 | if (actionMasked == MotionEvent.ACTION_DOWN) { |
27 | // Defensive cleanup for new gesture |
28 | stopNestedScroll(); |
29 | } |
30 | |
31 | if (onFilterTouchEventForSecurity(event)) { |
32 | if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { |
33 | result = true; |
34 | } |
35 | //noinspection SimplifiableIfStatement |
36 | ListenerInfo li = mListenerInfo; |
37 | if (li != null && li.mOnTouchListener != null |
38 | && (mViewFlags & ENABLED_MASK) == ENABLED |
39 | && li.mOnTouchListener.onTouch(this, event)) { |
40 | result = true; |
41 | } |
42 | |
43 | if (!result && onTouchEvent(event)) { |
44 | result = true; |
45 | } |
46 | } |
47 | |
48 | if (!result && mInputEventConsistencyVerifier != null) { |
49 | mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); |
50 | } |
51 | |
52 | // Clean up after nested scrolls if this is the end of a gesture; |
53 | // also cancel it if we tried an ACTION_DOWN but we didn't want the rest |
54 | // of the gesture. |
55 | if (actionMasked == MotionEvent.ACTION_UP || |
56 | actionMasked == MotionEvent.ACTION_CANCEL || |
57 | (actionMasked == MotionEvent.ACTION_DOWN && !result)) { |
58 | stopNestedScroll(); |
59 | } |
60 | |
61 | return result; |
62 | } |
检测常用手势
“轻触手势”指的是用户将 一个或多个 手指放在触摸屏上,并且您的应用会将这种轻触模式解读为特定手势(为手势赋归类,并赋予含义)。手势检测可相应地划为两个阶段:
- 收集轻触事件的相关数据
- 解读数据以确定其是否复合您的应用支持的任何手势的事件
支持库类:GestureDetectorCompat 和 MotionEventCompat, 这些类均包含在支持库中。以便与搭载在 Android 1.6 及更高版本的设备兼容
收集数据
手势会在用户首次轻触屏幕时开始,在系统跟踪用户手指的位置时继续,并在捕获到用户手指离开屏幕的最终事件时结束。在此互动过程中,传递给 onTouchEvent()
的 MotionEvent
会提供每次互动的详细信息。您的应用可以使用 MotionEvent
提供的数据确定是否发生了它所关注的手势。
常用的事件
1 | public static final int ACTION_DOWN = 0; |
2 | public static final int ACTION_UP = 1; |
3 | public static final int ACTION_MOVE = 2; |
4 | public static final int ACTION_CANCEL = 3; |
5 | public static final int ACTION_OUTSIDE = 4; |
6 | public static final int ACTION_POINTER_DOWN = 5; |
7 | public static final int ACTION_POINTER_UP = 6; |
1 | public class MainActivity extends Activity { |
2 | ... |
3 | // This example shows an Activity, but you would use the same approach if |
4 | // you were subclassing a View. |
5 |
|
6 | public boolean onTouchEvent(MotionEvent event){ |
7 | |
8 | int action = MotionEventCompat.getActionMasked(event); |
9 | |
10 | switch(action) { |
11 | case (MotionEvent.ACTION_DOWN) : |
12 | Log.d(DEBUG_TAG,"Action was DOWN"); |
13 | return true; |
14 | case (MotionEvent.ACTION_MOVE) : |
15 | Log.d(DEBUG_TAG,"Action was MOVE"); |
16 | return true; |
17 | case (MotionEvent.ACTION_UP) : |
18 | Log.d(DEBUG_TAG,"Action was UP"); |
19 | return true; |
20 | case (MotionEvent.ACTION_CANCEL) : |
21 | Log.d(DEBUG_TAG,"Action was CANCEL"); |
22 | return true; |
23 | case (MotionEvent.ACTION_OUTSIDE) : |
24 | Log.d(DEBUG_TAG,"Movement occurred outside bounds " + |
25 | "of current screen element"); |
26 | return true; |
27 | default : |
28 | return super.onTouchEvent(event); |
29 | } |
30 | } |
除了重写 onTouchEvent 外,您还可以设置 View.OnTouchListener . 这样可以避免创建现有 View 的子类
1 | mView.setOnTouchListener(new OnTouchListener(){ |
2 | public boolean onTouch(View v,MotionEvent event){ |
3 | //... respond to touch events |
4 | return true; |
5 | } |
6 | }) |
检测手势
Android 提供了检测常用手势的 GestureDetector 类,该类支持的一些手势包括 onDown() / onLongPress() / onFling() 等等。您可以将 GestureDetector 与上述 onTouchEvent 结合使用(结合使用不是说一起使用,而是视具体情况使用)