android手势监听GestureDetector接口详解
文章由多出组合而成,它们来自:
http://blog.sina.com.cn/s/blog_8edee36301010kv9.html
http://blog.csdn.net/xiezhenxiang/article/details/6659506
http://384444165.iteye.com/blog/1502761
android的手势识别是android更人性化,让我们的操作更简单, 其实我们日常应用中不少地方都使用了手势识别方法 比如 2011年那么火的 愤怒的小鸟 中的 发射武器, uc 游览器 的左右滑动功能,这都是可以使用android的手势识别来实现。如果想要实现提高我们app 的用户体验,更具有可操作性,那这时android的GestureDetector 就派上用场了 , 我们先开看看GestureDetector这个类的api 给我们提供了哪些操作
下面是GestureDetector 这个类的概括情况
根据 MotionEvent
事件检测各种手势. GestureDetector.OnGestureListener
回调函数用于通知用户发生的手势动作。该类仅处理MotionEvent
事件中的触摸事件(不处理轨迹球事件)。 使用该类的方法如下:
1:首先需要在初始化的时候创建GestureDetector对象实例
接口 OnDoubleTapListener 手势监听器 用于双击时发出通知
接口 OnGestureListener 手势监听器 用于发送手势操作时发生通知 如 拖动,长按,按下,弹起 等手势操作
类 SimpleOnGestureListener GestureDetector的扩展类 这个类的作用是当我们只想使用部分手势时, 而非全部的时候 就可以继承这个类
GestureDetector 还有几个公共方法 如下
boolean | isLongpressEnabled() 判定是否允许长点击 |
boolean | onTouchEvent(MotionEventev)
分析给定的动作事件,如果满足条件,就触发
GestureDetector.OnGestureListener 中提供的回调函数。 |
void | setIsLongpressEnabled(boolean isLongpressEnabled)
设置是否允许长按。如果允许长按,当用户按下并保持按下状态时, 将收到一个长按事件,同时不再接收其它事件;如果禁用长按, 当用户按下并保持按下状态然后再移动手指时,将会接收到滚动事件。 长按默认为允许。
|
void | setOnDoubleTapListener(GestureDetector.OnDoubleTapListeneronDoubleTapListener)
设置双击及其相关手势的监听器。
|
OnGestureListener 接口 的公共方法 如果你想在你的app view中实现下面这些手势操作的话 继承该接口 并实现下面这些方法即可
abstract boolean | onDown(MotionEvente)
当轻触手势按下屏幕 时发生的事件.
|
abstract boolean | onFling(MotionEvent e1, MotionEvente2, float velocityX, float velocityY)
快速滑动后抬起事件的通知.
|
abstract void | onLongPress(MotionEvente)
长按时的通知事件
|
abstract boolean | onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 滑动屏幕时通知事件 为了方便提供了X轴和Y轴上的滚动距离.
|
abstract void | onShowPress(MotionEvente)
当用户长点击后抬起时通知事件
|
abstract boolean | onSingleTapUp(MotionEvente)
当轻触手势抬起屏幕时发生的事件.
|
OnDoubleTapListener 接口的公共方法
abstract boolean | onDoubleTap(MotionEvente)
双击发生时的通知。
|
abstract boolean | onDoubleTapEvent(MotionEvente)
双击手势过程中发生的事件,包括按下、移动和抬起事件。
|
abstract boolean | onSingleTapConfirmed(MotionEvente)
发生确定的单击时执行。
与 |
在实际应用中 我们根据我们所需要的功能实现不同的接口 或者 继承自SimpleOnGestureListener 类 只实现部分个人需要的方法 就可以了
直接看代码 GameSurfaceView类
package yxqz.com;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
import android.widget.Toast;
/**
* android 手势识别
* @author mahaile
*
*/
public class GameSurfaceView extends SurfaceView implements Callback,OnGestureListener{
private String TAG="GameView";
boolean flag; //线程标示位 当为false时停止刷新界面
SurfaceHolder surfaceHolder;
GameViewThread gameViewThread;
float x=0,y=0;
int stat=0; //手势识别状态标示
private GestureDetector gd; // 手势
Context context;
int width,height;
Bitmap bitmap_role;
public GameSurfaceView(Context context) {
super(context);
this.context=context;
surfaceHolder=this.getHolder();
surfaceHolder.addCallback(this); //添加回调
//设置焦点 如果不设置焦点的话 在该界面下 点击触摸屏是无效的 默认为false
setFocusableInTouchMode(true);
bitmap_role=BitmapFactory.decodeResource(getResources(), R.drawable.role);
gd=new GestureDetector(this); //创建手势监听对象
}
public void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
//canvas.drawBitmap(bitmap_role, width/2-bitmap_role.getWidth()/2, y, null);
canvas.drawBitmap(bitmap_role, x-bitmap_role.getWidth()/2, y-bitmap_role.getHeight()/2, null);
}
//重写父类中的 onTouchEvent就可以监听到 触摸事件了 记住要设置焦点喔
@Override
public boolean onTouchEvent(MotionEvent event) {
gd.onTouchEvent(event); //通知手势识别方法
return true;
}
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
}
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//获取屏幕的 宽高 只有在 surface创建的时候 才有效 ,才构造方法中获取 宽高是获取不到的
width=this.getWidth();
height=this.getHeight();
//初始化绘图线程
gameViewThread=new GameViewThread();
gameViewThread.flag=true;
gameViewThread.start();
}
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
gameViewThread.flag=false; //销毁线程
}
class GameViewThread extends Thread{
public boolean flag;
public void run(){
while(flag){
Canvas canvas=null;
try{
canvas=surfaceHolder.lockCanvas(); //锁定画布 并获取canvas
onDraw(canvas);//调用onDraw 渲染到屏幕
surfaceHolder.unlockCanvasAndPost(canvas); //此步不要忘记了喔 否则界面上显示不出来的
}catch(Exception e){
e.printStackTrace();
}
try {
Thread.sleep(10);//线程休眠时间 控制帧数
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} //每一秒刷新一次
}
}
}
//**************************下面是手势识别的重写方法*******************************************
//屏幕点下
public boolean onDown(MotionEvent arg0) {
Log.d(TAG, "onDown");
Toast.makeText(this.context, "你点击了屏幕", Toast.LENGTH_SHORT).show();
return false;
}
//屏幕点下
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
Log.d(TAG, "onFling");
Toast.makeText(this.context, "你拖动屏幕后弹起", Toast.LENGTH_SHORT).show();
return false;
}
//屏幕点下 并长按时触发
public void onLongPress(MotionEvent e) {
Log.d(TAG, "onLongPress");
Toast.makeText(this.context, "你长按了屏幕", Toast.LENGTH_SHORT).show();
}
//屏幕拖动
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
this.x=this.x-distanceX;
this.y=this.y-distanceY;
Toast.makeText(this.context, "你拖动了屏幕", Toast.LENGTH_SHORT).show();
return false;
}
//屏幕长按
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
Toast.makeText(this.context, "你长按屏幕后弹起", Toast.LENGTH_SHORT).show();
Log.d(TAG, "onShowPress");
}
//屏幕点击后弹起
public boolean onSingleTapUp(MotionEvent e) {
Log.d(TAG, "onSingleTapUp");
Toast.makeText(this.context, "你弹起屏幕", Toast.LENGTH_SHORT).show();
return false;
}
}
另一个关于手势监听的详细例子:
public class MyGesture extends Activity implements OnTouchListener, OnGestureListener {
private GestureDetector mGestureDetector;
public MyGesture() {
mGestureDetector = new GestureDetector(this);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView) findViewById(R.id.tv);
tv.setOnTouchListener(this);
tv.setFocusable(true);
tv.setClickable(true);
tv.setLongClickable(true);
mGestureDetector.setIsLongpressEnabled(true);
}
/*
* 在onTouch()方法中,我们调用GestureDetector的onTouchEvent()方法,将捕捉到的MotionEvent交给GestureDetector
* 来分析是否有合适的callback函数来处理用户的手势
*/
public boolean onTouch(View v, MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
// 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发
public boolean onDown(MotionEvent arg0) {
Log.i("MyGesture", "onDown");
Toast.makeText(this, "onDown", Toast.LENGTH_SHORT).show();
return true; }
/*
* 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
* 注意和onDown()的区别,强调的是没有松开或者拖动的状态
*/
public void onShowPress(MotionEvent e) {
Log.i("MyGesture", "onShowPress");
Toast.makeText(this, "onShowPress", Toast.LENGTH_SHORT).show();
}
// 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
public boolean onSingleTapUp(MotionEvent e) {
Log.i("MyGesture", "onSingleTapUp");
Toast.makeText(this, "onSingleTapUp", Toast.LENGTH_SHORT).show();
return true;
}
// 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.i("MyGesture", "onFling");
Toast.makeText(this, "onFling", Toast.LENGTH_LONG).show();
return true;
}
// 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.i("MyGesture", "onScroll");
Toast.makeText(this, "onScroll", Toast.LENGTH_LONG).show();
return true;
}
// 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发
public void onLongPress(MotionEvent e) {
Log.i("MyGesture", "onLongPress");
Toast.makeText(this, "onLongPress", Toast.LENGTH_LONG).show();
}
}
public class MyGesture extends Activity implements OnTouchListener, OnGestureListener {
private GestureDetector mGestureDetector;
public MyGesture() {
mGestureDetector = new GestureDetector(this);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView) findViewById(R.id.tv);
tv.setOnTouchListener(this);
tv.setFocusable(true);
tv.setClickable(true);
tv.setLongClickable(true);
mGestureDetector.setIsLongpressEnabled(true);
}
/*
* 在onTouch()方法中,我们调用GestureDetector的onTouchEvent()方法,将捕捉到的MotionEvent交给GestureDetector
* 来分析是否有合适的callback函数来处理用户的手势
*/
public boolean onTouch(View v, MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
// 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发
public boolean onDown(MotionEvent arg0) {
Log.i("MyGesture", "onDown");
Toast.makeText(this, "onDown", Toast.LENGTH_SHORT).show();
return true;
}
/*
* 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
* 注意和onDown()的区别,强调的是没有松开或者拖动的状态
*/
public void onShowPress(MotionEvent e) {
Log.i("MyGesture", "onShowPress");
Toast.makeText(this, "onShowPress", Toast.LENGTH_SHORT).show();
}
// 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
public boolean onSingleTapUp(MotionEvent e) {
Log.i("MyGesture", "onSingleTapUp");
Toast.makeText(this, "onSingleTapUp", Toast.LENGTH_SHORT).show();
return true;
}
// 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.i("MyGesture", "onFling");
Toast.makeText(this, "onFling", Toast.LENGTH_LONG).show();
return true;
}
// 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.i("MyGesture", "onScroll");
Toast.makeText(this, "onScroll", Toast.LENGTH_LONG).show();
return true;
}
// 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发
public void onLongPress(MotionEvent e) {
Log.i("MyGesture", "onLongPress");
Toast.makeText(this, "onLongPress", Toast.LENGTH_LONG).show();
}
}
Fling事件的处理代码:除了第一个触发Fling的ACTION_DOWN和最后一个ACTION_MOVE中包含的坐标等信息外,我们还可以根据用户在X轴或者Y轴上的移动速度作为条件。比如下面的代码中我们就在用户移动超过100个像素,且X轴上每秒的移动速度大于200像素时才进行处理。
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- // 参数解释:
- // e1:第1个ACTION_DOWN MotionEvent
- // e2:最后一个ACTION_MOVE MotionEvent
- // velocityX:X轴上的移动速度,像素/秒
- // velocityY:Y轴上的移动速度,像素/秒
- // 触发条件 :
- // X轴的坐标位移大于FLING_MIN_DISTANCE,且移动速度大于FLING_MIN_VELOCITY个像素/秒
- final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200;
- if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) {
- // Fling left
- Log.i("MyGesture", "Fling left");
- Toast.makeText(this, "Fling Left", Toast.LENGTH_SHORT).show();
- } else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) {
- // Fling right
- Log.i("MyGesture", "Fling right");
- Toast.makeText(this, "Fling Right", Toast.LENGTH_SHORT).show();
- }
- return false;
- }
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 参数解释:
// e1:第1个ACTION_DOWN MotionEvent
// e2:最后一个ACTION_MOVE MotionEvent
// velocityX:X轴上的移动速度,像素/秒
// velocityY:Y轴上的移动速度,像素/秒
// 触发条件 :
// X轴的坐标位移大于FLING_MIN_DISTANCE,且移动速度大于FLING_MIN_VELOCITY个像素/秒
final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200;
if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) {
// Fling left
Log.i("MyGesture", "Fling left");
Toast.makeText(this, "Fling Left", Toast.LENGTH_SHORT).show();
} else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) {
// Fling right
Log.i("MyGesture", "Fling right");
Toast.makeText(this, "Fling Right", Toast.LENGTH_SHORT).show();
}
return false;
}
这个例子中,tv.setLongClickable(
true
)是必须的,因为
只有这样,view才能够处理不同于Tap(轻触)的hold(即ACTION_MOVE,或者多个ACTION_DOWN),我们同样可以通过layout定义中的android:longClickable来做到这一点。
SimpleOnGestureListener
- public class MyGesture extends Activity implements OnTouchListener {
- private GestureDetector mGestureDetector;
- public MyGesture() {
- mGestureDetector = new GestureDetector(new MySimpleGesture());
- }
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- TextView tv = (TextView) findViewById(R.id.tv);
- tv.setOnTouchListener(this);
- tv.setFocusable(true);
- tv.setClickable(true);
- tv.setLongClickable(true);
- }
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_UP) {
- Log.i("MyGesture", "MotionEvent.ACTION_UP");
- }
- return mGestureDetector.onTouchEvent(event);
- }
- // SimpleOnGestureListener implements GestureDetector.OnDoubleTapListener, GestureDetector.OnGestureListener
- private class MySimpleGesture extends SimpleOnGestureListener {
- // 双击的第二下Touch down时触发
- public boolean onDoubleTap(MotionEvent e) {
- Log.i("MyGesture", "onDoubleTap");
- return super.onDoubleTap(e);
- }
- // 双击的第二下Touch down和up都会触发,可用e.getAction()区分
- public boolean onDoubleTapEvent(MotionEvent e) {
- Log.i("MyGesture", "onDoubleTapEvent");
- return super.onDoubleTapEvent(e);
- }
- // Touch down时触发
- public boolean onDown(MotionEvent e) {
- Log.i("MyGesture", "onDown");
- return super.onDown(e);
- }
- // Touch了滑动一点距离后,up时触发
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- Log.i("MyGesture", "onFling");
- return super.onFling(e1, e2, velocityX, velocityY);
- }
- // Touch了不移动一直Touch down时触发
- public void onLongPress(MotionEvent e) {
- Log.i("MyGesture", "onLongPress");
- super.onLongPress(e);
- }
- // Touch了滑动时触发
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- Log.i("MyGesture", "onScroll");
- return super.onScroll(e1, e2, distanceX, distanceY);
- }
- /*
- * Touch了还没有滑动时触发
- * (1)onDown只要Touch Down一定立刻触发
- * (2)Touch Down后过一会没有滑动先触发onShowPress再触发onLongPress
- * So: Touch Down后一直不滑动,onDown -> onShowPress -> onLongPress这个顺序触发。
- */
- public void onShowPress(MotionEvent e) {
- Log.i("MyGesture", "onShowPress");
- super.onShowPress(e);
- }
- /*
- * 两个函数都是在Touch Down后又没有滑动(onScroll),又没有长按(onLongPress),然后Touch Up时触发
- * 点击一下非常快的(不滑动)Touch Up: onDown->onSingleTapUp->onSingleTapConfirmed
- * 点击一下稍微慢点的(不滑动)Touch Up: onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
- */
- public boolean onSingleTapConfirmed(MotionEvent e) {
- Log.i("MyGesture", "onSingleTapConfirmed");
- return super.onSingleTapConfirmed(e);
- }
- public boolean onSingleTapUp(MotionEvent e) {
- Log.i("MyGesture", "onSingleTapUp");
- return super.onSingleTapUp(e);
- }
- }
- }
我们知道,一般的View只能响应点击(Click)和长按(LongPress)事件。这是因为View里只暴露了这些listener给我们使用。而实质上,View是在onTouchEvent(MotionEvent event)里对用户的动作做了一定的分析,从而通知我们是发生了点击还是长按等事件。
View里提供的回调在我描述的场景里,并不能满足要求。因此,GestureDetector出场了。我需要对其啃透才能写出自己的ActionDetector。
GestureDetector类可以帮助我们分析用户的动作,和View的onTouchEvent的处理方式差不多,但分析的动作类型更加细致,以下是它的回调接口:
public interface OnGestureListener {
// Touch down时触发, e为down时的MotionEvent
boolean onDown(MotionEvent e);
// 在Touch down之后一定时间(115ms)触发,e为down时的MotionEvent
void onShowPress(MotionEvent e);
// Touch up时触发,e为up时的MotionEvent
boolean onSingleTapUp(MotionEvent e);
// 滑动时触发,e1为down时的MotionEvent,e2为move时的MotionEvent
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
// 在Touch down之后一定时间(500ms)触发,e为down时的MotionEvent
void onLongPress(MotionEvent e);
// 滑动一段距离,up时触发,e1为down时的MotionEvent,e2为up时的MotionEvent
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}
public interface OnDoubleTapListener {
// 完成一次单击,并确定没有二击事件后触发(300ms),e为down时的MotionEvent
boolean onSingleTapConfirmed(MotionEvent e);
// 第二次单击down时触发,e为第一次down时的MotionEvent
boolean onDoubleTap(MotionEvent e);
// 第二次单击down,move和up时都触发,e为不同时机下的MotionEvent
boolean onDoubleTapEvent(MotionEvent e);
}
其中GestureDetector.SimpleOnGestureListener(Framework帮我们简化了)是实现了上面提到的OnGestureListener和OnDoubleTapListener两个接口的类,我们只需要继承它并重写其中我们关心的回调即可。
后,再提一下双击和三击的识别过程:在第一次单击down时,给Hanlder发送了一个延时300ms的消息,如果300ms里,发生了第二次单击的down事件,那么,就认为是双击事件了,并移除之前发送的延时消息。如果300ms后仍没有第二次的down消息,那么就判定为SingleTapConfirmed事件(当然,此时用户的手指应已完成第一次点击的up过程)。三击的判定和双击的判定类似,只是多了一次发送延时消息的过程.
引用其他。