Android事件处理之多点触摸与手势识别
一、Muilti-touch 双指缩放的实现探索:
首先要实现OnTouchListener接口,然后重写方法:
public boolean onTouch(View v, MotionEvent event);
从这个方法中我们就可以获取实现两指缩放功能的全部信息。
View v是触发事件的源,MotionEvent event即一个触摸事件。对屏幕的几乎所有操作都会触发事件,如点击、放开、滑动等。
不同的事件在MotionEvent中有不同的id,我们可以根据event.getAction() & MotionEvent.ACTION_MASK的结果来判断是何种事件。
有如下事件使我们要用到的:
- MotionEvent.ACTION_DOWN:在第一个点被按下时触发
 - MotionEvent.ACTION_UP:当屏幕上唯一的点被放开时触发
 - MotionEvent.ACTION_POINTER_DOWN:当屏幕上已经有一个点被按住,此时再按下其他点时触发。
 - MotionEvent.ACTION_POINTER_UP:当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)。
 - MotionEvent.ACTION_MOVE: 当有点在屏幕上移动时触发。值得注意的是,由于它的灵敏度很高,而我们的手指又不可能完全静止(即使我们感觉不到移动,但其实我们的手指也在不停地抖 动),所以实际的情况是,基本上只要有点在屏幕上,此事件就会一直不停地被触发。
 
举 例子来说:当我们放一个食指到屏幕上时,触发ACTION_DOWN事件;再放一个中指到屏幕上,触发ACTION_POINTER_DOWN事件;此时 再把食指或中指放开,都会触发ACTION_POINTER_UP事件;再放开最后一个手指,触发ACTION_UP事件;而同时在整个过程 中,ACTION_MOVE事件会一直不停地被触发。
event.getX(index)和event.getY(index)可以获取到指定index点的坐标,所以当屏幕上有两个点的时候,我们用如下方法来获取两点间的距离:
private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return FloatMath.sqrt(x * x + y * y);
}  
由以上事件触发的原理,就可以根据被触发的不同事件来判断当前屏幕上的点的个数:
switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mode = 1;
            break;
        case MotionEvent.ACTION_UP:
            mode = 0;
            break;
        case MotionEvent.ACTION_POINTER_UP:
            mode -= 1;
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            mode += 1;
            break;
}  
然后在MotionEvent.ACTION_MOVE事件中,判断点的个数,如果大于等于2,就计算两点间的距离,如果距离增大就把图片放大,距离减少就把图片缩小。
于是代码就成了:
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
    mode = 1;
    break;
case MotionEvent.ACTION_UP:
    mode = 0;
    break;
case MotionEvent.ACTION_POINTER_UP:
    mode -= 1;
    break;
case MotionEvent.ACTION_POINTER_DOWN:
    oldDist = spacing(event);//两点按下时的距离
    mode += 1;
    break;
case MotionEvent.ACTION_MOVE:
    if (mode >= 2) {
        float newDist = spacing(event);
        if (newDist > oldDist) {
            zoomOut();
        }
        if (newDist < oldDist) {
            zoomIn();
        }
        break;
    }  
经过检验,这种方法是能够实现缩放效果的。
但是有了另外一个问题:就是由于ACTION_MOVE会因颤抖一直被触发,而每次触发的时候两点间的距离也总会有细小的变化,所以运行之后只要有两点在屏幕上,就总会在放大或缩小字体。
经过一番思考,我想出了一个控制其灵敏度的方法,即在case MotionEvent.ACTION_MOVE时判断只有当距离变化大于一定程度时才会更改字体大小:
if (newDist > oldDist + 1) {//原为:if (newDist > oldDist)
    zoomOut();//放大
}  
另外缩放的方法也改成了按比例缩放,完整的ZoomListenter代码:
1 import android.util.FloatMath;
2 import android.view.MotionEvent;
3 import android.view.View;
4 import android.view.View.OnTouchListener;
5 import android.widget.TextView;
6
7 public class ZoomListenter implements OnTouchListener {
8
9 private int mode = 0;
10 float oldDist;
11 float textSize = 0;
12
13 TextView textView = null;
14
15 @Override
16 public boolean onTouch(View v, MotionEvent event) {
17 textView = (TextView) v;
18 if (textSize == 0) {
19 textSize = textView.getTextSize();
20 }
21 switch (event.getAction() & MotionEvent.ACTION_MASK) {
22 case MotionEvent.ACTION_DOWN:
23 mode = 1;
24 break;
25 case MotionEvent.ACTION_UP:
26 mode = 0;
27 break;
28 case MotionEvent.ACTION_POINTER_UP:
29 mode -= 1;
30 break;
31 case MotionEvent.ACTION_POINTER_DOWN:
32 oldDist = spacing(event);
33 mode += 1;
34 break;
35
36 case MotionEvent.ACTION_MOVE:
37 if (mode >= 2) {
38 float newDist = spacing(event);
39 if (newDist > oldDist + 1) {
40 zoom(newDist / oldDist);
41 oldDist = newDist;
42 }
43 if (newDist < oldDist - 1) {
44 zoom(newDist / oldDist);
45 oldDist = newDist;
46 }
47 }
48 break;
49 }
50 return true;
51 }
52
53 private void zoom(float f) {
54 textView.setTextSize(textSize *= f);
55 }
56
57 private float spacing(MotionEvent event) {
58 float x = event.getX(0) - event.getX(1);
59 float y = event.getY(0) - event.getY(1);
60 return FloatMath.sqrt(x * x + y * y);
61 }
62
63 }
这样,基本算是能达到预期的效果了。
二、Android 屏幕手势滑动
Android系统自带一个Gallery浏览图片的应用,通过手指拖动时能够非常流畅的显示图片,用户交互和体验都很好。
所以本人研究了一下android的源码后,写了一个小demo:

1、基本原理
在 Activity 中实现 OnGestureListener 的接口 onFling() 手势事件,通过自定义的 View 绘制draw() 图片
2、Activity
Activity中,通过onTouchEvent() 注册 myGesture.onTouchEvent(event)
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
flingView.onFling(0); // 手指抬起后,重置滑动距离offsetX = 0
break;
} return myGesture.onTouchEvent(event);
}
接着实现接口OnGestureListener 的 onScroll()方法,给继承自View的 FlingView 的handleScroll()成员方法传递滑动参数,获取滑动的x轴距离
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
flingView.handleScroll(-1 * (int) distanceX);
return true;
}
接着实现接口OnGestureListener 的 OnFling()方法,给继承自View的 FlingView 的onFling()成员方法传递滑动参数,获取手势的速度
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
flingView.onFling((int) - velocityX);
return true;
}
3、FlingView
FlingView中,获取来自Activity中的手势速度
public void onFling(int paramFloat1) {
    if (offsetX > GalleryDemoActivity.deviceScreenWidth / 5) {
        if (fBitmap != null) {
            isFling = true;
            isFlingRight = true;
        }
    } else if (offsetX < -GalleryDemoActivity.deviceScreenWidth / 5) {
        if (nBitmap != null) {
            isFling = true;
            isFlingLeft = true;
        }
    }
    // 开始动画效果
    startAnimation(new MyAnimation());
}  
在滑动过程中,通过实现View的Draw()方法绘制图片,注意:此时需要同时绘制当前图片(获取焦点)和下一张图片(即将获取焦点)共两张图片
@Override
public void draw(Canvas canvas) {
Paint paint = new Paint();
Rect rect = new Rect();
canvas.drawColor(Color.BLACK); // 绘制当前图片
if (bitmap != null) {
int left = offsetX;
int top = offsetY;
int right = offsetX + GalleryDemoActivity.deviceScreenWidth;
int bottom = offsetY + GalleryDemoActivity.deviceScreenHeight;
rect.set(left, top, right, bottom);
canvas.drawBitmap(bitmap, null, rect, paint);
} // 绘制下一张图片
if (offsetX < 0) { // 向左滑动
if (nBitmap != null) {
int left = GalleryDemoActivity.deviceScreenWidth + 15 + offsetX;
int top = 0;
int right = left + GalleryDemoActivity.deviceScreenWidth;
int bottom = GalleryDemoActivity.deviceScreenHeight;
rect.set(left, top, right, bottom);
canvas.drawBitmap(nBitmap, null, rect, paint);
}
} else if (offsetX > 0) { // 向右滑动
if (fBitmap != null) {
int left = -GalleryDemoActivity.deviceScreenWidth - 15 + offsetX;
int top = 0;
int right = left + GalleryDemoActivity.deviceScreenWidth;
int bottom = GalleryDemoActivity.deviceScreenHeight;
rect.set(left, top, right, bottom);
canvas.drawBitmap(fBitmap, null, rect, paint);
}
}
}
在滑动图片结束后,需要做滑动动画后的处理,重新设置当前图片和当前图片的上一张和下一张的状态,为下次滑动做准备
@Override
protected void onAnimationEnd() {
if (isFlingRight) { // 向右滑动,position减1
nBitmap = bitmap;
bitmap = fBitmap;
fBitmap = null;
postion = postion - 1;
} else if (isFlingLeft) { // 向左滑动,position加1
fBitmap = bitmap;
bitmap = nBitmap;
nBitmap = null;
postion = postion + 1;
} isFlingRight = false;
isFlingLeft = false;
isFling = false;
offsetX = 0;
if (fBitmap == null && offsetX == 0) { // 如果前一张图片为空(向右滑),则重置前一张图片(position - 1)
if (postion > 0) {
fBitmap = getBitmap(postion - 1);
} } else if (nBitmap == null && offsetX == 0) { // 如果后一张图片为空(向左滑),则重置后一张图片(position + 1)
if (postion < bitmaps.length - 1) {
nBitmap = getBitmap(postion + 1);
}
}
clearAnimation();
}
4、手势坐标介绍
本示例中,用到了OnGestureListener接口的onScroll()和OnFling()方法,涉及到了Android系统坐标及触摸MotionEvent e1和e2、速度velocityX、velocityY等值
Android屏幕坐标系如下图(左)

(1)MotionEvent中 e1是手指第一次按上屏幕的起点,e2是抬起手指离开屏幕的终点,根据上图Android屏幕坐标系可知:
手指向右滑动,终点(e2)在起点(e1)的右侧,有e2.getX() - e1.getX() 大于0
手指向左滑动,终点(e2)在起点(e1)的左侧,有e2.getX() - e1.getX() 小于0
手指向下滑动,终点(e2)在起点(e1)的下侧,有e2.getY() - e1.getY() 大于0
手指向上滑动,终点(e2)在起点(e1)的上侧,有e2.getY() - e1.getY() 小于0
(2)onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
distanceX,是前后两次call的X距离,不是e2与e1的水平距离
distanceY,是前后两次call的Y距离,不是e2与e1的垂直距离
具体数值的方向,请详见上图(中)
(3)onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
velocityX,是X轴的每秒速度
velocityY,是Y轴的每秒速度
具体数值的方向,请详见上图(右)
仔细观察可以发现:velocityX、velocityY的方向与distanceX、distanceY方向正好相反
Android事件处理之多点触摸与手势识别的更多相关文章
- ios开发——实用技术篇Swift篇&多点触摸与手势识别
		
多点触摸与手势识别 //点击事件 var atap = UITapGestureRecognizer(target: self, action: "tapDo:") self.vi ...
 - Android学习之多点触摸并不神秘
		
最近研究了一下多点触摸,写了个利用多点触摸来控制图片大小和单点触摸控制图片移动的程序,和大家分享分享. Android中监听触摸事件是onTouchEvent方法,它的参数为MotionEvent,下 ...
 - Android多点触摸 与 手势识别
		
1. 事件类型 MotionEvent.ACTION_DOWN MotionEvent.ACTION_MOVE MotionEvent.ACTION_UP 2. 事件传递 public boolean ...
 - Linux Android 多点触摸协议 原文出自【比特网】,转载请保留原文链接:http://soft.chinabyte.com/os/71/12306571.shtml
		
为了使用功能强大的多点触控设备,就需要一种方案去上报用户层所需的详细的手指触摸数据.这个文档所描述的多点触控协议可以让内核驱动程序向用户层上报任意多指的数据信息. 使用说明 单点触摸信息是以ABS承载 ...
 - Linux与Android 多点触摸协议【转】
		
本文转载自:http://blog.csdn.net/xubin341719/article/details/7833277 一.Linux与Android 多点触摸协议 为了使用功能强大的多点触控设 ...
 - 10.14 android输入系统_多点触摸驱动测试及Reader线程、InputStage分析
		
21. 多点触摸_电容屏驱动程序_实践_tiny4412 tiny4412触摸屏: 分辨率为800 x 480http://wiki.friendlyarm.com/wiki/index.php/LC ...
 - 毫无保留开源我写的:IOS Android Ipad 多点触摸通用js 库
		
毫无保留开源我写的:IOS Android Ipad 多点触摸通用js 库 在线演示地址: http://m.yunxunmi.com/ 支持 IOS Android Ipad 等不同操作系统的手持或 ...
 - Android 中多点触摸协议
		
http://blog.csdn.net/zuosifengli/article/details/7398661 Android 中多点触摸协议: 参考: http://www.kernel.org/ ...
 - Android 手势滑动,多点触摸放大缩小图片
		
效果展示: 基本思路: <1>首先写一个图片控制类ImageControl,实现对图片控制的的基本操作,我们的图片控制类ImageControl是继承自ImageView自定义的视图: & ...
 
随机推荐
- php正则表达式总结
			
<?php echo 'wj'; echo '<br>'; $file = '<td>移动150卡</td><!--<td></td& ...
 - DWZ简介及其使用
			
来源:http://blog.csdn.net/t123012009065/article/details/8286826 DWZ简介: DWZ富客户端框架(jQuery RIA framewor ...
 - 【C++学习之路】派生类的构造函数(三)
			
三.多层继承的派生类 1.多层继承的派生类只需在构造函数的初始化列表中写出直接基类的构造函数即可 class student { public: student(int n, string nam) ...
 - 学习OpenSeadragon之二 (界面缩放与平移规则设置)
			
OpenSeadragon入门了解请看第一篇:http://www.cnblogs.com/yingjiehit/p/4362377.html OpenSeadragon给我们提供了很多的可选界面元素 ...
 - Android的消息处理机制(Looper,Handler,Message)(转)
			
Handler Handler的定义: 主要接收子线程发送的数据,并用此数据配合主线程更新UI. 当应用程序启动时,Android首先会开启一个主线程(也就是UI线程),主线程为管理界面中的UI空间进 ...
 - flowplayer+flashhls使用过程中发现的一些小问题
			
flashls里边有好几套代码,主要看生成路径,其中flowplayer用了flashls.swc,flashls.swc使用的代码在这里:/src/org/mangui/hls,所以要注意,当搜索代 ...
 - DIRECTORY_SEPARATOR
			
定义 php的内置变量DIRECTORY_SEPARATOR是一个显示系统分隔符的命令,DIRECTORY_SEPARATOR是php的内部常量,不需要任何定义与包含即可直接使用. 2说明 路径分 ...
 - phpEXCEL操作全解
			
phpExcel中文帮助手册,列举了各种属性,以及常用的操作方法,难得是每一个都用实例加以说明,希望对大家有所帮助. phpExcel中文帮助手册,不可多得的好文章,供大家学习参考. 1.设置exce ...
 - 魔法方法:构造和析构 - 零基础入门学习Python041
			
魔法方法:构造和析构 让编程改变世界 Change the world by program 构造和析构 什么是魔法方法呢?我们来系统总结下: - 魔法方法总是被双下划线包围,例如__init__ - ...
 - EF学习
			
一.EF介绍 实体框架 Entity Framework 是ADO.NET 中的一组支持开发面向数据的软件应用程序的技术.在 EF 中的实体数据模型(EDM)由以下三种模型和具有相应文件扩展名的映射文 ...