【转】android 手势识别和VelocityTracker
参考地址:
http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1020/448.html
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1117/574.html
1.在android应用当中每一次手势的交互都都会依照如下顺序去执行的
- 刚接触到手机屏幕的时候,要触发MotionEvent事件
- 该事件被OnTouchListener监听,在要复写的onTouch()方法里获得该MotionEvent对象
- 通过GestureDetector(手势识别器)转发次MotionEvent对象至OnGestureListener。
- 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>
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());
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 * 释放VelocityTracker 82 * 83 * @see android.view.VelocityTracker#clear() 84 * @see android.view.VelocityTracker#recycle() 85 */ 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手势处理揭秘