在Android系统中,手势的识别是通过 GestureDetector.OnGestureListener接口来实现的,不过William翻遍了Android的官方文档也没有找到一个相关的例子,API Demo中的TouchPaint也仅仅是提到了onTouch事件的处理,没有涉及到手势。Android Developer讨论组里也有不少人有和我类似的问题,结合他们提到的方法和我所做的实验,我将给大家简单讲述一下Android中手势识别的实现。
我们先来明确一些概念,首先,Android的事件处理机制是基于Listener(监听器)来实现的,比我们今天所说的触摸屏相关的事件,就是通过 onTouchListener。其次,所有View的子类都可以通过setOnTouchListener()、setOnKeyListener() 等方法来添加对某一类事件的监听器。第三,Listener一般会以Interface(接口)的方式来提供,其中包含一个或多个abstract(抽象)方法,我们需要实现这些方法来完成onTouch()、onKey()等等的操作。这样,当我们给某个view设置了事件Listener,并实现了其中的抽象方法以后,程序便可以在特定的事件被dispatch到该view的时候,通过callbakc函数给予适当的响应。
看一个简单的例子,就用最简单的TextView来说明(事实上和ADT中生成的skeleton没有什么区别)。
01.public class GestureTest extends Activity implements OnTouchListener{
02.
03.@Override
04.protected void onCreate(Bundle savedInstanceState) {
05.super.onCreate(savedInstanceState);
06.setContentView(R.layout.main);
07.
08.// init TextView
09.TextView tv = (TextView) findViewById(R.id.page);
10.// set OnTouchListener on TextView
11.tv.setOnTouchListener(this);
12.// show some text
13.tv.setText(R.string.text);
14.}
15.
16.@Override
17.public boolean onTouch(View v, MotionEvent event) {
18.Toast.makeText(this, "onTouch", Toast.LENGTH_SHORT).show();
19.return false;
20.}
我们给TextView的实例tv设定了一个onTouchListener,因为GestureTest类实现了OnTouchListener接口,所以简单的给一个this作为参数即可。onTouch方法则是实现了OnTouchListener中的抽象方法,我们只要在这里添加逻辑代码即可在用户触摸屏幕时做出响应,就像我们这里所做的——打出一个提示信息。
这里,我们可以通过MotionEvent的getAction()方法来获取Touch事件的类型,包括 ACTION_DOWN, ACTION_MOVE, ACTION_UP, 和ACTION_CANCEL。ACTION_DOWN是指按下触摸屏,ACTION_MOVE是指按下触摸屏后移动受力点,ACTION_UP则是指松开触摸屏,ACTION_CANCEL不会由用户直接触发(所以不在今天的讨论范围,请参考 ViewGroup.onInterceptTouchEvent(MotionEvent))。借助对于用户不同操作的判断,结合getRawX()、 getRawY()、getX()和getY()等方法来获取坐标后,我们可以实现诸如拖动某一个按钮,拖动滚动条等功能。待机可以看看 MotionEvent类的文档,另外也可以看考TouchPaint例子。
回到今天所要说的重点,当我们捕捉到Touch操作的时候,如何识别出用户的Gesture?这里我们需要GestureDetector.OnGestureListener接口的帮助,于是我们的GestureTest类就变成了这个样子。
1.public class GestureTest extends Activity implements OnTouchListener,
2.OnGestureListener {
3....
4.}
随后,在onTouch()方法中,我们调用GestureDetector的onTouchEvent()方法,将捕捉到的MotionEvent交给 GestureDetector 来分析是否有合适的callback函数来处理用户的手势。
1.@Override
2.public boolean onTouch(View v, MotionEvent event) {
3.// OnGestureListener will analyzes the given motion event
4.return mGestureDetector.onTouchEvent(event);
5.}
接下来,我们实现了以下6个抽象方法,其中最有用的当然是onFling()、onScroll()和onLongPress()了。我已经把每一个方法代表的手势的意思写在了注释里,大家看一下就明白了。
01.// 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发
02.@Override
03.public boolean onDown(MotionEvent e) {
04.// TODO Auto-generated method stub
05.Toast.makeText(this, "onDown", Toast.LENGTH_SHORT).show();
06.return false;
07.}
08.
09.// 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
10.// 注意和onDown()的区别,强调的是没有松开或者拖动的状态
11.@Override
12.public void onShowPress(MotionEvent e) {
13.// TODO Auto-generated method stub
14.}
15.
16.// 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
17.@Override
18.public boolean onSingleTapUp(MotionEvent e) {
19.// TODO Auto-generated method stub
20.return false;
21.}
22.
23.// 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发
24.@Override
25.public boolean onFling(MotionEvent e1, MotionEvent e2, floatvelocityX,
26.float velocityY) {
27.// TODO Auto-generated method stub
28.return false;
29.}
30.
31.// 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发
32.@Override
33.public void onLongPress(MotionEvent e) {
34.// TODO Auto-generated method stub
35.
36.}
37.
38.// 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发
39.@Override
40.public boolean onScroll(MotionEvent e1, MotionEvent e2, floatdistanceX,
41.float distanceY) {
42.// TODO Auto-generated method stub
43.return false;
44.}
我们来试着做一个onFling()事件的处理吧,onFling()方法中每一个参数的意义我写在注释中了,需要注意的是Fling事件的处理代码中,除了第一个触发Fling的ACTION_DOWN和最后一个ACTION_MOVE中包含的坐标等信息外,我们还可以根据用户在X轴或者Y轴上的移动速度作为条件。比如下面的代码中我们就在用户移动超过100个像素,且X轴上每秒的移动速度大于200像素时才进行处理。
--------------------------------------------------
有关Android平台的游戏开发中我们需要涉及到控制,在开始的Android游戏开发之旅中我们提到了按键和轨迹球的控制方式,从今天开始 Android123开始给出大家游戏中其他的一些控制方式,比如今天的手势操作和未来重力感应。
很多网友发现Android中手势识别提供了两个类,由于Android 1.6以下的版本比如cupcake中无法使用android.view.GestureDetector,而 android.gesture.Gesture是Android 1.6才开始支持的,我们考虑到仍然有很多Android 1.5固件的网友,就来看下兼容性更强的android.view.GestureDetector。在 android.view.GestureDetector类中有很多种重载版本,下面我们仅提到能够自定义在View中的两种方法,分别为 GestureDetector(Context context, GestureDetector.OnGestureListener listener) 和 GestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler) 和。我们可以看到第一个参数为Context,所以我们想附着到某View时,最简单的方法就是直接从超类派生传递Context,实现 GestureDetector里中提供一些接口。
下面我们就以实现手势识别的onFling动作,在CwjView中我们从View类继承,当然大家可以从TextView等更高层的界面中实现触控。
1. class CwjView extends View {
2.
3. private GestureDetector mGD;
4. public CwjView(Context context, AttributeSet attrs) {
5. super(context, attrs);
6.
7. mGD = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
8. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
9. int dx = (int) (e2.getX() - e1.getX()); //计算滑动的距离
10.
11. if (Math.abs(dx) > MAJOR_MOVE && Math.abs(velocityX) > Math.abs(velocityY)) { //降噪处理,必须有较大的动作才识别
12. if (velocityX > 0) {
13. //向右边
14.
15. } else {
16. //向左边
17.
18. }
19. return true;
20. } else {
21. return false; //当然可以处理velocityY处理向上和向下的动作
22. }
23. }
24. });
25. }
复制代码
在上面Android123提示大家仅仅探测了Fling动作仅仅实现了onFling方法,这里相关的还有以下几种方法来实现具体的可以参考我们以前的文章有详细的解释:
1. boolean onDoubleTap(MotionEvent e)
2. boolean onDoubleTapEvent(MotionEvent e)
3. boolean onDown(MotionEvent e)
4. void onLongPress(MotionEvent e)
5. boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
6. void onShowPress(MotionEvent e)
7. boolean onSingleTapConfirmed(MotionEvent e)
8. boolean onSingleTapUp(MotionEvent e)
复制代码
接下来是重点,让我们的View接受触控,需要使用下面两个方法让GestureDetector类去处理onTouchEvent和 onInterceptTouchEvent方法。
1. @Override
2. public boolean onTouchEvent(MotionEvent event) {
3. mGD.onTouchEvent(event);
4. return true;
5. }
6.
7. @Override
8. public boolean onInterceptTouchEvent(MotionEvent event) {
9. return mGD.onTouchEvent(event);
10. }
11.
12. }
我们先来明确一些概念,首先,Android的事件处理机制是基于Listener(监听器)来实现的,比我们今天所说的触摸屏相关的事件,就是通过 onTouchListener。其次,所有View的子类都可以通过setOnTouchListener()、setOnKeyListener() 等方法来添加对某一类事件的监听器。第三,Listener一般会以Interface(接口)的方式来提供,其中包含一个或多个abstract(抽象)方法,我们需要实现这些方法来完成onTouch()、onKey()等等的操作。这样,当我们给某个view设置了事件Listener,并实现了其中的抽象方法以后,程序便可以在特定的事件被dispatch到该view的时候,通过callbakc函数给予适当的响应。
看一个简单的例子,就用最简单的TextView来说明(事实上和ADT中生成的skeleton没有什么区别)。
01.public class GestureTest extends Activity implements OnTouchListener{
02.
03.@Override
04.protected void onCreate(Bundle savedInstanceState) {
05.super.onCreate(savedInstanceState);
06.setContentView(R.layout.main);
07.
08.// init TextView
09.TextView tv = (TextView) findViewById(R.id.page);
10.// set OnTouchListener on TextView
11.tv.setOnTouchListener(this);
12.// show some text
13.tv.setText(R.string.text);
14.}
15.
16.@Override
17.public boolean onTouch(View v, MotionEvent event) {
18.Toast.makeText(this, "onTouch", Toast.LENGTH_SHORT).show();
19.return false;
20.}
我们给TextView的实例tv设定了一个onTouchListener,因为GestureTest类实现了OnTouchListener接口,所以简单的给一个this作为参数即可。onTouch方法则是实现了OnTouchListener中的抽象方法,我们只要在这里添加逻辑代码即可在用户触摸屏幕时做出响应,就像我们这里所做的——打出一个提示信息。
这里,我们可以通过MotionEvent的getAction()方法来获取Touch事件的类型,包括 ACTION_DOWN, ACTION_MOVE, ACTION_UP, 和ACTION_CANCEL。ACTION_DOWN是指按下触摸屏,ACTION_MOVE是指按下触摸屏后移动受力点,ACTION_UP则是指松开触摸屏,ACTION_CANCEL不会由用户直接触发(所以不在今天的讨论范围,请参考 ViewGroup.onInterceptTouchEvent(MotionEvent))。借助对于用户不同操作的判断,结合getRawX()、 getRawY()、getX()和getY()等方法来获取坐标后,我们可以实现诸如拖动某一个按钮,拖动滚动条等功能。待机可以看看 MotionEvent类的文档,另外也可以看考TouchPaint例子。
回到今天所要说的重点,当我们捕捉到Touch操作的时候,如何识别出用户的Gesture?这里我们需要GestureDetector.OnGestureListener接口的帮助,于是我们的GestureTest类就变成了这个样子。
1.public class GestureTest extends Activity implements OnTouchListener,
2.OnGestureListener {
3....
4.}
随后,在onTouch()方法中,我们调用GestureDetector的onTouchEvent()方法,将捕捉到的MotionEvent交给 GestureDetector 来分析是否有合适的callback函数来处理用户的手势。
1.@Override
2.public boolean onTouch(View v, MotionEvent event) {
3.// OnGestureListener will analyzes the given motion event
4.return mGestureDetector.onTouchEvent(event);
5.}
接下来,我们实现了以下6个抽象方法,其中最有用的当然是onFling()、onScroll()和onLongPress()了。我已经把每一个方法代表的手势的意思写在了注释里,大家看一下就明白了。
01.// 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发
02.@Override
03.public boolean onDown(MotionEvent e) {
04.// TODO Auto-generated method stub
05.Toast.makeText(this, "onDown", Toast.LENGTH_SHORT).show();
06.return false;
07.}
08.
09.// 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
10.// 注意和onDown()的区别,强调的是没有松开或者拖动的状态
11.@Override
12.public void onShowPress(MotionEvent e) {
13.// TODO Auto-generated method stub
14.}
15.
16.// 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
17.@Override
18.public boolean onSingleTapUp(MotionEvent e) {
19.// TODO Auto-generated method stub
20.return false;
21.}
22.
23.// 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发
24.@Override
25.public boolean onFling(MotionEvent e1, MotionEvent e2, floatvelocityX,
26.float velocityY) {
27.// TODO Auto-generated method stub
28.return false;
29.}
30.
31.// 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发
32.@Override
33.public void onLongPress(MotionEvent e) {
34.// TODO Auto-generated method stub
35.
36.}
37.
38.// 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发
39.@Override
40.public boolean onScroll(MotionEvent e1, MotionEvent e2, floatdistanceX,
41.float distanceY) {
42.// TODO Auto-generated method stub
43.return false;
44.}
我们来试着做一个onFling()事件的处理吧,onFling()方法中每一个参数的意义我写在注释中了,需要注意的是Fling事件的处理代码中,除了第一个触发Fling的ACTION_DOWN和最后一个ACTION_MOVE中包含的坐标等信息外,我们还可以根据用户在X轴或者Y轴上的移动速度作为条件。比如下面的代码中我们就在用户移动超过100个像素,且X轴上每秒的移动速度大于200像素时才进行处理。
--------------------------------------------------
有关Android平台的游戏开发中我们需要涉及到控制,在开始的Android游戏开发之旅中我们提到了按键和轨迹球的控制方式,从今天开始 Android123开始给出大家游戏中其他的一些控制方式,比如今天的手势操作和未来重力感应。
很多网友发现Android中手势识别提供了两个类,由于Android 1.6以下的版本比如cupcake中无法使用android.view.GestureDetector,而 android.gesture.Gesture是Android 1.6才开始支持的,我们考虑到仍然有很多Android 1.5固件的网友,就来看下兼容性更强的android.view.GestureDetector。在 android.view.GestureDetector类中有很多种重载版本,下面我们仅提到能够自定义在View中的两种方法,分别为 GestureDetector(Context context, GestureDetector.OnGestureListener listener) 和 GestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler) 和。我们可以看到第一个参数为Context,所以我们想附着到某View时,最简单的方法就是直接从超类派生传递Context,实现 GestureDetector里中提供一些接口。
下面我们就以实现手势识别的onFling动作,在CwjView中我们从View类继承,当然大家可以从TextView等更高层的界面中实现触控。
1. class CwjView extends View {
2.
3. private GestureDetector mGD;
4. public CwjView(Context context, AttributeSet attrs) {
5. super(context, attrs);
6.
7. mGD = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
8. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
9. int dx = (int) (e2.getX() - e1.getX()); //计算滑动的距离
10.
11. if (Math.abs(dx) > MAJOR_MOVE && Math.abs(velocityX) > Math.abs(velocityY)) { //降噪处理,必须有较大的动作才识别
12. if (velocityX > 0) {
13. //向右边
14.
15. } else {
16. //向左边
17.
18. }
19. return true;
20. } else {
21. return false; //当然可以处理velocityY处理向上和向下的动作
22. }
23. }
24. });
25. }
复制代码
在上面Android123提示大家仅仅探测了Fling动作仅仅实现了onFling方法,这里相关的还有以下几种方法来实现具体的可以参考我们以前的文章有详细的解释:
1. boolean onDoubleTap(MotionEvent e)
2. boolean onDoubleTapEvent(MotionEvent e)
3. boolean onDown(MotionEvent e)
4. void onLongPress(MotionEvent e)
5. boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
6. void onShowPress(MotionEvent e)
7. boolean onSingleTapConfirmed(MotionEvent e)
8. boolean onSingleTapUp(MotionEvent e)
复制代码
接下来是重点,让我们的View接受触控,需要使用下面两个方法让GestureDetector类去处理onTouchEvent和 onInterceptTouchEvent方法。
1. @Override
2. public boolean onTouchEvent(MotionEvent event) {
3. mGD.onTouchEvent(event);
4. return true;
5. }
6.
7. @Override
8. public boolean onInterceptTouchEvent(MotionEvent event) {
9. return mGD.onTouchEvent(event);
10. }
11.
12. }