参考地址:
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>
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) | ||||||||||
abstract T | |||||||||||
float | 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 | 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对象
()函数来获得当前的速度
下面是我写的一个简单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 * 释放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后的减速运动等。实现一些非常棒的效果。