【转】android 手势识别和VelocityTracker

参考地址:

http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1020/448.html

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1117/574.html

一.Android的手势操作识别

1.在android应用当中每一次手势的交互都都会依照如下顺序去执行的

  •     刚接触到手机屏幕的时候,要触发MotionEvent事件
  •     该事件被OnTouchListener监听,在要复写的onTouch()方法里获得该MotionEvent对象
  •     通过GestureDetector(手势识别器)转发次MotionEvent对象至OnGestureListener。
  •     OnGestureListener获取到对象之后,根据对应的手势做出回应
2.这个顺序可以说就是手势交互的原理,下面一同来了解一下MotionEvent、GestureDetector和OnGestureListener。


MotionEvent: 这个类用于封装手势、触摸笔、轨迹球等等的动作事件。其内部封装了两个重要的属性X和Y,这两个属性分别用于记录横轴和纵轴的坐标。

GestureDetector: 识别各种手势。

OnGestureListener: 这是一个手势交互的监听接口,其中提供了多个抽象方法,并根据GestureDetector的手势识别结果调用相对应的方法。

下面我再通过一个切换图片的代码示例,演示一下手势交互的实现,让大伙对上面的执行顺序,以及各手势动作的区分有一个更加深刻的了解和记忆。

 首先,提供一个只有ImageView的布局文件——main.xml。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3 android:orientation="vertical"
 4 android:layout_width="fill_parent"
 5 android:layout_height="fill_parent">
 6    
 7  <ImageView android:id="@+id/image"
 8 android:layout_width="fill_parent"
 9 android:layout_height="fill_parent"
10 android:layout_gravity="center"/>
11 </LinearLayout>

 

然后,完成我们的Activity,因为要监听触摸屏的触摸事件和手势事件,所以该Activity必须实现OnTouchListener和OnGestureListener两个接口,并重写其中的方法。具体代码如下:


 

 1 public class MainActivity extends Activity implements OnTouchListener, OnGestureListener { 
 2    
 3 //创建一个用于识别收拾的GestureDetector对象waiyuwu.blogcn.com
 4 private GestureDetector detector = new GestureDetector(this);
 5 //定义一个数组,用于放漂亮的女孩
 6 int[] girls = new int[]{R.drawable.girl1, R.drawable.girl2, R.drawable.girl3};
 7 //定义数组下标,以方便观看各个女孩
 8 private int index;
 9 private ImageView image;
10    
11 @Override
12 public void onCreate(Bundle savedInstanceState) {
13 super.onCreate(savedInstanceState);
14 setContentView(R.layout.main);
15 image = (ImageView)findViewById(R.id.image);
16 //设置一个初始显示的girl吧
17 image.setImageResource(girls[index]);
18 //监听这个ImageView组件上的触摸屏时间
19 image.setOnTouchListener(this);
20 //下面两个要记得设哦,不然就没法处理轻触以外的事件了,例如抛掷动作。
21 image.setLongClickable(true);
22 detector.setIsLongpressEnabled(true);
23 }//用于呼喊下一个女孩的方法
24 public void goNext(){
25 index++;
26 index = Math.abs(index % girls.length);
27 image.setImageResource(girls[index]);
28 }
29    
30 //重写OnTouchListener的onTouch方法
31 //此方法在触摸屏被触摸,即发生触摸事件(接触和抚摸两个事件,挺形象)的时候被调用。
32 @Override
33 public boolean onTouch(View v, MotionEvent event) {
34 detector.onTouchEvent(event);
35 return true;
36 }
37    
38 //在按下动作时被调用
39 @Override
40 public boolean onDown(MotionEvent e) {
41 return false;
42 }
43    
44 //在抛掷动作时被调用
45 @Override
46 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
47 float velocityY) {
48 //velocityX表示横向的移动,根据手指移动的方向切换女孩
49 if(velocityX < 0){
50 goNext();
51 }else if(velocityX > 0){
52 goPrevious();
53 }
54 return false;
55 }
56    
57 //用户呼唤上一个女孩的方法
58 public void goPrevious(){
59 index--;
60 index = Math.abs(index % girls.length);
61 image.setImageResource(girls[index]);
62 }
63    
64 //在长按时被调用
65 @Override
66 public void onLongPress(MotionEvent e) {
67 }
68    
69 //在滚动时调用
70 @Override
71 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
72 float distanceY) {
73 return false;
74 }
75    
76 //在按住时被调用
77 @Override
78 public void onShowPress(MotionEvent e) {
79 }
80    
81 //在抬起时被调用
82 @Override
83 public boolean onSingleTapUp(MotionEvent e) {
84 return false;
85 }
86 }

 

GestureDetector.OnGestureListener监听手势的方法有如下这些:

对这些方法做一下解释:
  • 按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。

  • 抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。

  • 长按(onLongPress): 手指按在持续一段时间,并且没有松开。

  • 滚动(onScroll): 手指在触摸屏上滑动。

  • 按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。

  • 抬起(onSingleTapUp):手指离开触摸屏的那一刹那。

除了这些定义之外,鄙人也总结了一点算是经验的经验吧,在这里和大家分享一下。

  • 任何手势动作都会先执行一次按下(onDown)动作。

  • 长按(onLongPress)动作前一定会执行一次按住(onShowPress)动作。

  • 按住(onShowPress)动作和按下(onDown)动作之后都会执行一次抬起(onSingleTapUp)动作。

  • 长按(onLongPress)、滚动(onScroll)和抛掷(onFling)动作之后都不会执行抬起(onSingleTapUp)动作

二.手势事件:滑动动速度跟踪类VelocityTracker介绍

VelocityTracker顾名思义即速度跟踪,在android中主要应用于touchEvent, VelocityTracker通过跟踪一连串事件实时计算出当前的速度,这样的用法在android系统空间中随处可见,比如Gestures中的 Fling, Scrolling等。

 

android.view.VelocityTracker主要用跟踪触摸屏事件 (flinging事件和其他gestures手势事件)的速率。用addMovement(MotionEvent)函数将Motion event加入到VelocityTracker类实例中.你可以使用getXVelocity() 或getXVelocity()获得横向和竖向的速率到速率时,但是使用它们之前请先调用computeCurrentVelocity(int)来初始 化速率的单位 。

主要函数
 
Public Methods
void addMovement(MotionEventevent)
Add a user's movement to the tracker.
void clear()
Reset the velocity tracker back to its initial state.
void computeCurrentVelocity(int units, float maxVelocity)
Compute the current velocity based on the points that have been collected.
intunitis表示速率的基本时间单位。unitis值为1的表示是,一毫秒时间单位内运动了多少个像素, unitis值为1000表示一秒(1000毫秒)时间单位内运动了多少个像素
floatVelocity表示速率的最大值
void computeCurrentVelocity(int units)
Equivalent to invoking computeCurrentVelocity(int, float)with a maximum velocity of Float.MAX_VALUE.
abstract T getNextPoolable()
float getXVelocity()
Retrieve the last computed X velocity.
float getXVelocity(int id)
Retrieve the last computed X velocity.
float getYVelocity(int id)
Retrieve the last computed Y velocity.
float getYVelocity()
Retrieve the last computed Y velocity.
abstract boolean isPooled()
static VelocityTracker obtain()
Retrieve a new VelocityTracker object to watch the velocity of a motion.
void recycle()
Return a VelocityTracker object back to be re-used by others.
abstract void setNextPoolable(T element)
abstract void setPooled(boolean isPooled)
 
示例:
 
 1 private VelocityTracker mVelocityTracker;//生命变量 
 2 //在onTouchEvent(MotionEvent ev)中 
 3 if (mVelocityTracker == null) { 
 4     mVelocityTracker = VelocityTracker.obtain();//获得VelocityTracker类实例 
 5 } 
 6 mVelocityTracker.addMovement(ev);//将事件加入到VelocityTracker类实例中 
 7 //判断当ev事件是MotionEvent.ACTION_UP时:计算速率 
 8 final VelocityTracker velocityTracker = mVelocityTracker; 
 9 // 1000 provides pixels per second 
10 velocityTracker.computeCurrentVelocity(1, (float)0.01);//设置maxVelocity值为0.1时,速率大于0.01时,显示的速率都是0.01,速率小于0.01时,显示正常  
11 Log.i("test","velocityTraker"+velocityTracker.getXVelocity()); 
12 velocityTracker.computeCurrentVelocity(1000); //设置units的值为1000,意思为一秒时间内运动了多少个像素 
13 Log.i("test","velocityTraker"+velocityTracker.getXVelocity());

 

大体的使用是这样的:
 
当你需要跟踪触摸屏事件的速度的时候,使用obtain()方法来获得VelocityTracker类的一个实例对象
 
在onTouchEvent回调函数中,使用addMovement(MotionEvent)函数将当前的移动事件传递给VelocityTracker对象
 
使用computeCurrentVelocity (int units)函数来计算当前的速度,使用getXVelocity ()、 getYVelocity ()函数来获得当前的速度
下面是我写的一个简单Demo:
 
  1 package com.bxwu.demo.component.activity;
  2 import android.app.Activity;
  3 import android.graphics.Color;
  4 import android.os.Bundle;
  5 import android.view.MotionEvent;
  6 import android.view.VelocityTracker;
  7 import android.view.ViewConfiguration;
  8 import android.view.ViewGroup.LayoutParams;
  9 import android.widget.TextView;
 10    
 11 public class VelocityTrackerTest extends Activity {
 12     private TextView mInfo;
 13    
 14     private VelocityTracker mVelocityTracker;
 15     private int mMaxVelocity;
 16    
 17     private int mPointerId;
 18    
 19     @Override
 20     protected void onCreate(Bundle savedInstanceState) {
 21         super.onCreate(savedInstanceState);
 22    
 23         mInfo = new TextView(this);
 24         mInfo.setLines(4);
 25         mInfo.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); 
 26         mInfo.setTextColor(Color.WHITE);
 27    
 28         setContentView(mInfo);
 29    
 30         mMaxVelocity = ViewConfiguration.get(this).getMaximumFlingVelocity();
 31     }
 32    
 33     @Override
 34     public boolean onTouchEvent(MotionEvent event) {
 35         final int action = event.getAction();
 36         acquireVelocityTracker(event);
 37         final VelocityTracker verTracker = mVelocityTracker;
 38         switch (action) {
 39             case MotionEvent.ACTION_DOWN:
 40                 //求第一个触点的id, 此时可能有多个触点,但至少一个
 41                 mPointerId = event.getPointerId(0);
 42                 break;
 43    
 44             case MotionEvent.ACTION_MOVE:
 45                 //求伪瞬时速度
 46                 verTracker.computeCurrentVelocity(1000, mMaxVelocity);
 47                 final float velocityX = verTracker.getXVelocity(mPointerId);
 48                 final float velocityY = verTracker.getYVelocity(mPointerId);
 49                 recodeInfo(velocityX, velocityY);
 50                 break;
 51    
 52             case MotionEvent.ACTION_UP:
 53                 releaseVelocityTracker();
 54                 break;
 55    
 56             case MotionEvent.ACTION_CANCEL:
 57                 releaseVelocityTracker();
 58                 break;
 59    
 60             default:
 61                 break;
 62         }
 63         return super.onTouchEvent(event);
 64     }
 65    
 66     /**
 67      *
 68      * @param event 向VelocityTracker添加MotionEvent
 69      *
 70      * @see android.view.VelocityTracker#obtain()
 71      * @see android.view.VelocityTracker#addMovement(MotionEvent)
 72      */
 73     private void acquireVelocityTracker(final MotionEvent event) {
 74         if(null == mVelocityTracker) {
 75             mVelocityTracker = VelocityTracker.obtain();
 76         }
 77         mVelocityTracker.addMovement(event);
 78     }
 79    
 80     /**
 81 &nbsp; &nbsp; &nbsp;* 释放VelocityTracker
 82 &nbsp; &nbsp; &nbsp;*
 83 &nbsp; &nbsp; &nbsp;* @see android.view.VelocityTracker#clear()
 84 &nbsp; &nbsp; &nbsp;* @see android.view.VelocityTracker#recycle()
 85 &nbsp; &nbsp; &nbsp;*/
 86     private void releaseVelocityTracker() {
 87         if(null != mVelocityTracker) {
 88             mVelocityTracker.clear();
 89             mVelocityTracker.recycle();
 90             mVelocityTracker = null;
 91         }
 92     }
 93    
 94     private static final String sFormatStr = "velocityX=%f\nvelocityY=%f";
 95    
 96     /**
 97      * 记录当前速度
 98      *
 99      * @param velocityX x轴速度
100      * @param velocityY y轴速度
101      */
102     private void recodeInfo(final float velocityX, final float velocityY) {
103         final String info = String.format(sFormatStr, velocityX, velocityY);
104         mInfo.setText(info);
105     }
106 }

 

 

代码很简单,我们可以求出move过程中的伪瞬时速度, 这样在做很多控件的时候都是可以用到的,比如系统Launcher的分页,

ScrollView滑动等, 可根据此时的速度来计算ACTION_UP后的减速运动等。实现一些非常棒的效果。

 

  android手势处理揭秘

 

posted @ 2014-12-17 21:32  perfect亮  阅读(763)  评论(0编辑  收藏  举报