Android Tech And Thoughts.

GestureDetector

Word count: 1.3kReading time: 5 min
2019/12/08 Share

Base On Android Developer Guide

尽管应用不应依赖轻触手势来执行基本行为(因为手势可能无法在所有上下文中供所有用户使用,用户可能不知道需要轻触手势来执行某些操作),但向应用添加基于轻触的互动可以极大地提升其实用性和吸引力(可以考虑做一个行为备份,即用户可以同时通过更常见的方式来执行这些基本操作)。

本文中,我们主要介绍以下几个方面的内容

  1. 检测常用手势:了解如何使用 GestureDetector 检测基本轻触手势,例如滚动、滑动和点按两次。
  2. 跟踪轻触和指针移动:了解如何跟踪指针移动。
  3. 以动画方式显示滚动手势:了解如何使用滚动条(ScrollerOverScroller)生成滚动动画以响应轻触事件。
  4. 处理多点触控手势:了解如何检测多指针(手指)手势。
  5. 拖动和缩放:了解如何实现基于轻触的拖动和缩放。
  6. 在 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
    }

检测常用手势

“轻触手势”指的是用户将 一个或多个 手指放在触摸屏上,并且您的应用会将这种轻触模式解读为特定手势(为手势赋归类,并赋予含义)。手势检测可相应地划为两个阶段:

  1. 收集轻触事件的相关数据
  2. 解读数据以确定其是否复合您的应用支持的任何手势的事件

支持库类: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
@Override
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 结合使用(结合使用不是说一起使用,而是视具体情况使用)

CATALOG
  1. 1. View的dispatchTouchEvent
  2. 2. 检测常用手势
    1. 2.1. 收集数据
    2. 2.2. 检测手势