Android手势检测和缩放手势检测

0 简单认识

0.1 应用场景

我们平常在使用App时,会作出各种手势,例如:单击进入新闻详情——单击、双击放大图片——双击、长按弹出菜单——长按、滑动新闻列表——滑动、多个手指放大缩小图片——缩放等等。我们如何判断一个控件是被作出了何种手势呢?这便用到了手势检测(GestureDetector)和缩放手势检测(ScaleGestureDetector)。

0.2 MotionEvent

MotionEvent:直译是“动作事件”,它指的是在手指接触屏幕后所产生的一系列事件。典型的如:

  • ACTION_DOWN:手指刚接触屏幕;
  • ACTION_MOVE:手指在屏幕上移动;
  • ACTION_UP:手指从屏幕上松开的一瞬间。

通过MotionEvent对象我们可以得到点击事件发生的x和y坐标,为此系统提供了两种方法:

  1. getX/getY:返回的是相对于当前View左上角的x和y坐标;
  2. getRawX/getRawY:返回的是相对于手机屏幕左上角的x和y坐标。

1 GestureDetector

1.1 构造方法

在源码中,GestureDetector一共有5种构造函数,如下图所示:其中2种已被废弃,1种是重复的,所以只需关注其中2种构造方法即可。
GestureDetector_构造方法

  • 第1种构造方法里需要传递两个参数,上下文(Context)和手势监听器(OnGestureListener)
  • 第2种构造方法则需要在第一种的基础上多传递一个Handler对象作为参数,这个Handler主要是为了给GestureDetector提供一个Looper。

通常情况下是不需要这个Handler的,因为在GestureDetector内部会自动创建一个Handler用于处理数据,但是若在子线程中创建,则需要Handler。

  1. 若在主线程中创建GestureDetector,那么它内部创建的Handler会自动获得主线程的Looper,不必创建Handler;
  2. 若在一个没有创建Looper的子线程中创建GestureDetector,则需要传递一个带有Looper的Handler给它,否则就会因为无法获取到Looper导致创建失败。而若要在子线程中创建GestureDetector则有3种方式:
    1. 在主线程中创建Handler;
    2. 在子线程中创建Handler并指定Looper;
    3. 若子线程准备了Looper,则不必传递Handler,可以直接使用第1种构造方法进行创建。
  1. 在主线程中创建GestureDetector,使用构造方法1即可
GestureDetector gestureDetector = new GestureDetector(this,
        new GestureDetector.SimpleOnGestureListener());
  1. 在子线程中创建GestureDetector-方式1:在主线程中创建Handler
final Handler handler = new Handler();
new Thread(new Runnable() {
	@Override
	public void run() {
    	GestureDetector gestureDetector = new GestureDetector(MainActivity.this,
	        	new GestureDetector.SimpleOnGestureListener(), handler);
        ...
    }
 }).start();
  1. 在子线程中创建GestureDetector-方式2:在子线程中创建Handler并指定Looper
new Thread(new Runnable() {
	@Override
	public void run() {
		Handler handler = new Handler(Looper.getMainLooper());
    	GestureDetector gestureDetector = new GestureDetector(MainActivity.this,
	        	new GestureDetector.SimpleOnGestureListener(), handler);
        ...
    }
 }).start();
  1. 在子线程中创建GestureDetector-方式3:在子线程中准备Looper,直接使用第一种构造方法来进行创建
new Thread(new Runnable() {
	@Override
	public void run() {
		Looper.prepare();//重点是这里,即在子线程中准备一个Looper
    	GestureDetector gestureDetector = new GestureDetector(MainActivity.this,
	        	new GestureDetector.SimpleOnGestureListener());
        ...
    }
 }).start();

1.2 监听器

既然是手势检测,那么自然会在对应的手势出现时通知调用者,这便需要监听器了。在源码中,GestureDetector有4种监听器:OnGestureListener、OnDoubleTapListener、OnContextClickListener以及继承以上三个接口的空实现类SimpleOnGestureListener
GestureDetector_监听器

  1. OnContextClickListener:用于检测外部设备上的按钮是否按下(例如蓝牙触控笔上的按钮)。
    1. onContextClick(MotionEvent e):若要监听此该事件,则必须在View.onGenericMotionEvent(MotionEvent)中调用GestureDetector.OnGenericMotionEvent(MotionEvent)
  2. OnDoubleTapListener:监听单击和双击事件。
    1. onSingleTapConfirmed(MotionEvent e):确认不是双击(通过单击DOWN后300ms没有下一个DOWN事件确认)而是单击事件发生时会回调;
    2. onDoubleTap(MotionEvent e):确认是一个双击事件后会回调,并且在第二次按下手指时,即便不抬起也会触发 onDoubleTap 和 onDoubleTapEvent 的 DOWN
    3. onDoubleTapEvent(MotionEvent e):在双击事件确定发生时会对第2次按下产生的MotionEvent(DOWN、MOVE、UP)信息进行回调,这个方法用于在双击后进行更细微的控制。
  3. OnGestureListener:监听一些手势:如单击、长按、滑动等动作。
    1. onDown(MotionEvent e):用户按下屏幕时的回调;
    2. onShowPress(MotionEvent e):用户按下按键后100ms还没有松开或者移动就会回调,主要作用是给用户提供一种视觉反馈,可以在监听到该事件时让控件换一种颜色或者产生一些变化,告诉用户它的动作已被识别;
    3. onLongPress(MotionEvent e):用户长按后会触发(默认为100ms+500ms),触发之后不会触发其他回调,直至松开(UP事件);
    4. onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):监听滚动事件,在手指滑动的时候回调——接收到MOVE事件,且位移大于一定距离。其中,e1和e2分别是手指按下时的Event和手指抬起时的Event。distanceX和distanceY就是在 X 轴上划过的距离和在 Y 轴上划过的距离。
    5. onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):直译是——扔、抛、甩,它是在MOVE事件之后手松开(UP事件)那一瞬间在x/y方向上仍有一定的速度,如果达到一定的速度(默认50px/s),就是该操作。最常见的场景是:在ListView或者RecyclerView上快速滑动时手指抬起后列表还会滚动一段事件才会停止,因此有onFling必有onScroll。与上面的面的onScroll类似,只不过onScroll后两个参数是距离,而onFling后两个参数是速度;
    6. onSingleTapUp(MotionEvent e):用户单击抬起时的回调。
      onSingleTapUp和onSingleTapConfirmed有何不同呢?具体看下面两个表格:

单击事件触发:

类型 触发次数 说明
onSingleTapUp 1 单击抬起
onSingleTapConfirmed 1 单击确认
onClick 1 单击事件

双击事件触发:

类型 触发次数 说明
onSingleTapUp 1 在双击的第一次抬起时触发
onSingleTapConfirmed 0 双击发生时不会触发
onClick 2 在双击事件时触发两次

补充:onShowPress和onSingleTapConfirmed类似,也是一种延时回调,延迟时间是180ms,若用户按下后立即抬起或时间立即被拦截,没有超过180ms的话,这条消息挥别remove掉,则不会触发该回调。

  1. SimpleOnGestureListener:是上述3个接口的空实现,一般情况下使用这个比较多,也比较方便。

总结:以上重用的9种回调方法的调用时机如下表所示

回调/输入事件 DOWN事件 MOVE事件 UP事件
onDown
onShowPress
onLongPress
onScroll
onFling
onSingleTapUp
onSingleTapConfirmed
onDoubleTap
onDoubleTapEvent

1.3 常用方法

  • setIsLongpressEnabled:设置是否启用长按,如果在用户按住时启用了长按,您将获得一个长按事件,没有其他事情。如果它被禁用,用户可以按住,然后移动他们的手指,你会得到滚动事件。默认情况下,长按处于启用状态。
  • isLongpressEnabled:判断当前是否允许触发长按事件,true 表示允许,false 表示不允许。
  • onTouchEvent:分析给定的运动事件,如果适用,则触发所提供的GestureDetector.OnGestureListener上的相应回调。如果GestureDetector.OnGestureListener消费了事件则返回true,否则返回false。
  • onGenericMotionEvent:若要监听外部设备上的按钮是否被按下,则需要调用该方法。主要是为OnContextClickListener服务的,暂时不用关注。
  • setContextClickListener:设置 ContextClickListener。
  • setOnDoubleTapListener:设置 OnDoubleTapListener。

1.4 简单使用

手势检测的使用大致分为3个步骤:

  1. 创建一个监听器,并在其中重写需要监听的事件(一般使用SimpleOnGestureListener比较方便);
  2. 创建一个GestureDetector对象(根据在主线程或子线程中创建而使用不同的构造方法);
  3. 给控件设置监听器,并重写onTouch方法。
//1.创建监听回调
GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onDoubleTap(MotionEvent e) {
        Toast.makeText(MainActivity.this, "双击", Toast.LENGTH_SHORT).show();
        return super.onDoubleTap(e);
    }
};

//2.创建一个GestureDetector
final GestureDetector gestureDetector = new GestureDetector(this, listener);

//3.给控件设置监听器,并重写onTouch方法
testButton.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
    	return gestureDetector.onTouchEvent(event);
    }
});

2 ScaleGestureDetector

2.1 构造方法

同GestureDetector类似,ScaleGestureDetector也有2种构造方法:

ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener)
ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener, Handler handler)

2.2 缩放手势监听器及常用方法

  • OnScaleGestureListener:缩放手势监听器
    • onScaleBegin(ScaleGestureDetector detector):缩放手势开始,当两个手指放在屏幕上的时候会调用该方法(只调用一次)。如果返回 false 则表示不使用当前这次缩放手势。
    • onScale(ScaleGestureDetector detector):缩放被触发(会调用0次或者多次)如果返回 true 则表示当前缩放事件已经被处理,检测器会重新积累缩放因子,返回 false 则会继续积累缩放因子。
    • onScaleEnd(ScaleGestureDetector detector):缩放手势结束。
  • SimpleOnScaleGestureListener:缩放手势监听器的空实现

注:有关ScaleGestureDetector的使用,请参见上面的GestureDetector


参考资料:

  1. Android开发艺术探索
  2. 安卓自定义View进阶-手势检测(GestureDetector)
  3. Android手势检测——GestureDetector全面分析
  4. 安卓自定义View进阶-缩放手势检测(ScaleGestureDecetor)

猜你喜欢

转载自blog.csdn.net/chaixingsi/article/details/86320697