1.GestureDetecor
用户触摸屏幕时会产生许多手势,一般通过重写View类的onTouch()方法可以处理一些触摸事件,但是这个方法太过简单,如果需要处理一些复杂的手势,用这个接口就会很麻烦,需要自己根据用户触摸的轨迹去判断是什么手势。
因此,Android sdk提供了GestureDetector类来监听手势发生,通过它的onTouchEvent(event)方法完成不同手势的识别。识别出手势后,不同的手势要怎么处理,再由开发者自己来实现。
GestureDetector类内部定义了三个接口和一个类,重写这些方法,就能实现传入触摸事件之后做出相应的回调:
①OnGestureListener接口
监听一些手势,如单击、滑动、长按等操作。
②OnDoubleTapListener接口
监听双击和单击事件。
③OnContextClickListener接口
当鼠标/触摸板,右键点击时候的回调。
④SimpleOnGestureListener 类
实现了上面三个接口的类,拥有上面三个的所有回调方法。
这些回调方法的返回值都是boolean类型,和View的事件传递机制一样,返回true表示消耗了事件,flase表示没有消耗。
其中,SimpleOnGestureListener是一个静态内部类,它实现了OnGestureListener、OnDoubleTapListener、OnContextClickListener三个接口。SimpleOnGestureListener类内重写了接口中的所有方法,但都是空实现,返回的布尔值都是false。这个类的主要作用就是方便继承这个类有选择的复写回调方法,而不是实现接口去重写所有的方法。
下面具体说说这几个接口:
①GesturetDetector.OnGestureListener 接口
public interface OnGestureListener {
boolean onDown(MotionEvent e); // 由1个down触发,每按一下屏幕立即触发
void onShowPress(MotionEvent e); //按下屏幕并且没有移动或松开,由1个down触发。注意和onDown()的区别,强调的是没有松开或者拖动的状态,而onDown()没有任何限制。主要是提供给用户一个可视化的反馈,告诉用户按下操作已经被捕捉到了。如果按下的速度很快只会调用onDown(),按下的速度稍慢一点会先调用onDown()再调用onShowPress()
boolean onSingleTapUp(MotionEvent e); //一次单纯的轻击抬手动作时触发,由一个up触发。如果除了Down以外还有其他操作,比如触发了长按,这时候手抬起时,这个方法不执行
boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX,float distanceY); // 屏幕拖动事件。手指按下并在屏幕上滑动时触发。这个事件由1个down,多个move触发。手指在屏幕上滑动时,会不断触发该方法。如果按下的时间过长,调用了onLongPress,再拖动屏幕不会触发onScroll。e1:开始拖动的第一次按下down操作,也就是第一个ACTION_DOWN;e2:触发当前onScroll方法的ACTION_MOVE,即当前滑动位置点;distanceX:当前x坐标与最后一次触发scroll方法的x坐标的差值,不是e1点到e2点的x轴距离;diastancY:当前y坐标与最后一次触发scroll方法的y坐标的差值,不是e1点到e2点的y轴距离
void onLongPress(MotionEvent e); //长按。在down操作之后,过一个特定的时间触发
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY); //按下屏幕,在屏幕上快速滑动后松开,由一个down、多个move、一个up触发。当滑动较慢时,只会触发onScroll,而不会触发onFling。e1:开始快速滑动的第一次按下down操作,也就是第一个ACTION_DOWN,即拖动事件的起点;e2:触发当前onFling方法的move操作,也就是最后一个ACTION_MOVE,即onFling()调用时手指的位置;velocityX:X轴上的移动速度,像素/秒;velocityY:Y轴上的移动速度,像素/秒
}
快按屏幕抬起:onDown —> onSingleTapUp —> onSingleTapConfirmed
慢按屏幕抬起:onDown –> onShowPress —> onSingleTapUp —> onSingleTapConfirmed
按下屏幕等待一段时间:onDown –> onShowPress –> onLongPress
拖动屏幕:onDown –> onShowPress –> onScroll(多个) –> onFling
快速滑动:onDown–>onScroll(多个)–>onFling
注意:当拖动速率velocityX或velocityY超过ViewConfiguration.getMinimumFlingVelocity()最小拖动速率时,才会调用onFling(),也就是说如果只拖动一点,或者慢慢的拖动,是不会触发该方法的。
②GesttureDetector.OnDoubleTapListener 接口
public interface OnDoubleTapListener {
boolean onSingleTapConfirmed(MotionEvent e); //单击事件。用来判定该次点击是单纯的SingleTap而不是DoubleTap。如果连续点击两次就是DoubleTap手势,如果只点击一次,系统等待一段时间后没有收到第二次点击则判定该次点击为SingleTap而不是DoubleTap,然后触发SingleTapConfirmed事件。触发顺序是:OnDown->OnSingleTapUp->OnSingleTapConfirmed。关于onSingleTapConfirmed和onSingleTapUp的区别:onSingleTapUp只要手抬起就会执行,而对于onSingleTapConfirmed来说,如果双击的话,则onSingleTapConfirmed不会执行。
boolean onDoubleTap(MotionEvent e); //双击事件。在第二次点击的down时触发。参数表示双击发生时,第一次按下时的down事件
boolean onDoubleTapEvent( MotionEvent e); //双击间隔中发生的动作。指触发onDoubleTap以后,在双击之间发生的其它动作,包含down、up和move事件
}
下图是双击一下的Log输出:
从图中可以看出,在第二下点击时,先触发OnDoubleTap,然后再触发OnDown(第二次点击)。在触发OnDoubleTap以后,就开始触发onDoubleTapEvent了,onDoubleTapEvent后面的数字代表了当前的事件,0指ACTION_DOWN,1指ACTION_UP,2 指ACTION_MOVE。
双击顺序:onDown –> onSingleTapUp –> onDoubleTap –> onDoubleTapEvent –> onDown–> onDoubleTapEvent
③GesttureDetector.OnContextClickListener接口
public interface On context click Listener {
boolean onContextClick(MotionEvent e);
}
④GestureDetector.SimpleOnGestureListener类
它与前面三个不同,这是一个类,并且实现了前三个接口,在它基础上新建类的话,要用extends派生而不是用implements继承。OnGestureListener和OnDoubleTapListener接口里的函数都是必须重写的,即使用不到也要重写一个空函数,但SimpleOnGestureListener类的实例或派生类中可以根据情况,用到哪个函数就重写哪个函数,因为SimpleOnGestureListener类本身已经实现了这两个接口的所有函数,只是里面全是空的而已。
2.GestureDetector使用方法
①第一步:根据需求创建onGestureListener、onDoubleTapListener或simpleOnGestureListener这三个类中的一个对象。比如只需要监听单击的话,那只需要得到实现了onGestureListener接口的对象;如果是双击,就只需要得到实现了onDoubleTapListener接口的对象;如果两种情况都需要考虑,就可以用SimpleOnGestureListener对象或者一个实现了上面两个接口的对象。
②第二步:创建一个GestureDetector对象,参数传入第一步的对象。
GestureDetector的构造函数如下:
public GestureDetector(Context context, OnGestureListener listener) {
this(context, listener, null);
}
public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
}
public GestureDetector(Context context, OnGestureListener listener, Handler handler,
boolean unused) {
this(context, listener, handler);
}
mGestureDetector = new GestureDetector( mContext, new MyGestureListener());
③第三步: 给view设置OnTouchListener监听,重写onTouch()方法,在return语句中写上mGestureDetector.onTouchEvent(event),将控制权交给GestureDector。
④第四步:绑定控件,给view设置setClickable(true)、setFocusable(true)、setLongClickable(true)。这一步非常重要,不然出不来效果。
注意:GestureDetector的构造方法中,有的构造函数需要多传递一个Handler作为参数,这个Handler主要是为了给GestureDetector提供一个Looper。带有handler参数,表示要在与该handler相关联的线程上,监听手势并处理延时事件。
通常情况下不需要这个Handler,因为它会在内部自动创建一个Handler用于处理数据。如果在主线程中创建GestureDetector,那么它内部创建的Handler会自动获得主线程的Looper;但是如果在一个没有创建Looper的子线程中创建 GestureDetector ,则需要传递一个带有Looper的Handler给它,否则就会因为无法获取到Looper导致创建失败。
下面是两种在子线程中创建GestureDetector的方法:
// 方式一:在主线程创建Handler
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final GestureDetector detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() , handler);
// ......
}
}).start();
// 方式二:在子线程创建Handler,并指定Looper
new Thread(new Runnable() {
@Override
public void run() {
final Handler handler = new Handler(Looper.getMainLooper());
final GestureDetector detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() , handler);
// ......
}
}).start();
使用其它创建Handler的方式也可以,重点是传递的Handler一定要有Looper。强调一下:重点是Handler中的Looper。假如子线程准备了Looper,则可以直接使用第 1 种构造函数进行创建:
new Thread(new Runnable() {
@Override public void run() {
Looper.prepare(); // <- 重点在这里
final GestureDetector detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener());
// ... 省略其它代码 ...
}
}).start();
GestureDetector使用 完整代码:
public class MyActivity extends Activity implements View.OnTouchListener, GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener{
private GestureDetector mGestureDetector;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGestureDetector = new GestureDetect