Android多点触控技术
1 简介
Android多点触控在本质上需要LCD驱动和程序本身设计上支持,目前市面上HTC、Motorola和Samsung等知名厂商只要使用电容屏触控原理的手机均可以支持多点触控Multitouch技术,对于网页缩放、手势操作上有更好的用户体验。 在Android平台上事件均使用了MotionEvent对象方式处理,比如开始触控时会触发ACTION_DOWN,而移动操作时为 ACTION_MOVE,最终放开手指时触发ACTION_UP事件。当然还有用户无规则的操作可能触发ACTION_CANCEL这个动作。
需要注意的是:Android的多点触控功能需要运行在Android 2.0版本以上。
首先Android开发网提醒大家多点触控需要LCD驱动和应用软件两个支持才能实现,所以部分比较老的,比如Android 2.0以前或在北美上市的手机可能无法支持多点触控在固件上,由于Apple专利原因在欧洲和亚太地区的Android 2.0以后的新款机型固件均已经在屏幕驱动中支持,同时模拟器也无法实现多点触控的测试。
2 实现步骤
1)第一种情况是直接重载Activity中的onTouchEvent方法。
对于onTouchEvent方法的参数MotionEvent,我们可以详细处理来实现对多点触控的了解,比如
event.getAction() //获取触控动作比如ACTION_DOWN
event.getPointerCount(); //获取触控点的数量,比如2则可能是两个手指同时按压屏幕
event.getPointerId(nID); //对于每个触控的点的细节,我们可以通过一个循环执行getPointerId方法获取索引
event.getX(nID); //获取第nID个触控点的x位置
event.getY(nID); //获取第nID个点触控的y位置
event.getPressure(nID); //LCD可以感应出用户的手指压力,当然具体的级别由驱动和物理硬件决定的
event.getDownTime() //按下开始时间
event.getEventTime() // 事件结束时间
event.getEventTime()-event.getDownTime()); //总共按下时花费时间
2)第二种情况是实现一个OnTouchListener的方法,来设置View的侦听属性,然后实现onTouch(View view, MotionEvent event)的方法,就可以获取触屏的感应事件了。
在该事件中,有两个参数可以用来获取对触摸的控制,这两个参数分别为:MotionEvent.getAction()和MotionEvent.ACTION_MASK,前者用于对单点触控进行操作,后者用于对多点触控进行操作,对于单点触控,由MotionEvent.getAction()可以得到以下几种事件:ACTION_DOWN、ACTION_UP,而对于多点触控,由MotionEvent.ACTION_MASK,我们可以得到:ACTION_POINTER_DOWN、ACTION_POINTER_UP,都是MotionEvent中的常量,可以直接调用。而有些常量则是单点和多点共用的,如:ACTION_MOVE,因此在按下时,必须标记单点与多点触控的区别。
3)注意:android2.2中onTouchEvent(MotionEvent event) 这里可以用event.getActionMasked()表示用于多点触控检测点。而在1.6和2.1中并没有event.getActionMasked()这个方法,其实他就是把event.getAction()& MotionEvent.ACTION_MASK封装了一下。
3 案例
案例一
public class MultiTouchActivity extends Activity {
<span style="color: rgb(0, 128, 0);"> /** Called when the activity is first created. */</span>
<span style="color: rgb(0, 128, 0);"> @Override</span>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
<span style="color: rgb(0, 128, 0);"> @Override</span>
public boolean onTouchEvent(MotionEvent event){
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_POINTER_1_DOWN:
showMessage("第一个手指按下");
break;
case MotionEvent.ACTION_POINTER_1_UP:
showMessage("第一个手指抬起");
break;
case MotionEvent.ACTION_POINTER_2_DOWN:
showMessage("第二个手指按下");
break;
case MotionEvent.ACTION_POINTER_2_UP:
showMessage("第二个手指抬起");
break;
case MotionEvent.ACTION_POINTER_3_DOWN:
showMessage("第三个手指按下");
break;
case MotionEvent.ACTION_POINTER_3_UP:
showMessage("第三个手指抬起");
break;
}
return true;
}
private void showMessage(String s){
Toast toast = Toast.makeText(getApplicationContext(), s, Toast.LENGTH_SHORT);
toast.show();
}
}
实测效果如下:
情况一:手指1按下没有出现提示;手指1 抬起 也没有出现提示;这是很显然的,因为这时产生的消息是ACTION_DOWN 和 ACTION_UP。 情况二:手指1按下没有提示;手指2按下出现手指2按下的提示;手指2抬起 出现手指2抬起的提示。 情况三:手指1按下没有提示;手指2 按下 出现提示;这时手指1提起出现手指1提起的提示;手指1按下出现手指1按下的提示; 情况四:大家可以放三个手指去尝试下,看看Android 是怎样产生这些消息的。 根据实验的结果,可以得到一句话:当屏幕上有一个手指时可以完美的产生2点触摸的消息;当屏幕上有2个手指时可以完美的产生3点触摸消息,以此类推……。所谓的完美就是指你能正确的得到到底是那个手指进行了操作。
案例二
public class Pointer2DrawActivity extends Activity implements OnTouchListener{
/** Called when the activity is first created. */
ImageView imgView;
Bitmap bitmap;
Canvas canvas;
Paint paint;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imgView = (ImageView)findViewById(R.id.imgView);
Display currentDisplay = getWindowManager().getDefaultDisplay();
float dw = currentDisplay.getWidth();
float dh = currentDisplay.getHeight();
bitmap = Bitmap.createBitmap((int)dw, (int)dh, Config.ARGB_8888);
canvas = new Canvas(bitmap);
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth((float) 10.00);//设置笔刷大小,自己的屏幕太犀利了
imgView.setImageBitmap(bitmap);
imgView.setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
int pointerCount = event.getPointerCount();
int pointerId = 0;
int action = (event.getAction()&MotionEvent.ACTION_MASK) % 5;//统一单点和多点
switch(action){
case MotionEvent.ACTION_DOWN:
if(pointerCount>1){
pointerId = (event.getAction()&MotionEvent.ACTION_POINTER_ID_MASK)>>> MotionEvent.ACTION_POINTER_ID_SHIFT;
}
break;
case MotionEvent.ACTION_MOVE:
if(pointerCount == 2){
float x = event.getX(1);
float y = event.getY(1);
canvas.drawPoint((int)x, (int)y, paint);
imgView.invalidate();
}
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
案例三
public class GameView2X extends GameView implements SurfaceHolder.Callback {
private float oldDist;
private PointF midPoint = new PointF();
private boolean isZoom = false;
public GameView2X(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
super.actionDown(event);
break;
case MotionEvent.ACTION_POINTER_UP:
isZoom = false;
break;
/**
* API原文是 A non-primary pointer has gone down.
* 翻译过来就是:非第一个点按下
*/
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
midPoint(midPoint, event);
isZoom = true;
break;
case MotionEvent.ACTION_MOVE:
if (isZoom) {
float newDist = spacing(event);
/**
* 表示新的距离比两个手指刚触碰的距离大
* ( +10个像素用来延迟一下放大,不然稍微动一点像素,也放大,感觉也太快了。)
*/
if (newDist + 10 > oldDist) {
super.getGameThread().getGameDraw()
.checkXY((int) midPoint.x, (int) midPoint.y);
super.getGameThread().getGameDraw().setIsZoom(true);
}
/**
* 表示新的距离比两个手指刚触碰的距离小
*/
if (newDist + 10 < oldDist) {
super.getGameThread().getGameDraw().setIsZoom(false);
GameDraw.newX = 0;
GameDraw.newY = 0;
}
}
super.actionMove(event);
break;
}
return true;
}
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);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
}
案例四(图片的放大和缩小)
public class TouchActivity extends Activity {
private static final int NONE = 0;
private static final int MOVE = 1;
private static final int ZOOM = 2;
private static final int ROTATION = 1;
private int mode = NONE;
private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
private PointF start = new PointF();
private PointF mid = new PointF();
private float s = 0;
private float oldDistance;
private int rotate = NONE;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView imageView = (ImageView)findViewById(R.id.imageView);
imageView.setOnTouchListener(new OnTouchListener()
{
@Override
public boolean onTouch(View view, MotionEvent event) {
ImageView imageView = (ImageView)view;
switch (event.getAction()&MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = MOVE;
rotate = NONE;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDistance = (float)Math.sqrt((event.getX(0)-event.getX(1))*(event.getX(0)-event.getX(1))+(event.getY(0)-event.getY(1))*(event.getY(0)-event.getY(1)));
if (oldDistance > 10f) {
savedMatrix.set(matrix);
mid.set((event.getX(0)+event.getX(1))/2, (event.getY(0)+event.getY(1))/2);
mode = ZOOM;
}
case MotionEvent.ACTION_MOVE:
if (mode == MOVE)
{
if(rotate == NONE) {
savedMatrix.set(matrix);
mid.set(event.getX(), event.getY());
rotate = ROTATION;
}
else {
matrix.set(savedMatrix);
double a = Math.atan((mid.y-start.y)/(mid.x-start.x));
double b = Math.atan((event.getY()-mid.y)/(event.getX()-mid.x));
if ((b - a < Math.PI/2 && b - a > Math.PI / 18)||((b + Math.PI) % Math.PI - a < Math.PI/2 && (b + Math.PI) % Math.PI - a > Math.PI / 18)) {
matrix.postScale((float)0.9, (float)0.9);
}
else if ((a - b < Math.PI / 2 && a - b > Math.PI / 18)||((a + Math.PI) % Math.PI - b < Math.PI/2 && (a + Math.PI) % Math.PI - b > Math.PI / 18)) {
matrix.postScale((float)1.1, (float)1.1);
}
start.set(event.getX(), event.getY());
rotate = NONE;
}
}
else if(mode == ZOOM)
{
float newDistance;
newDistance = (float)Math.sqrt((event.getX(0)-event.getX(1))*(event.getX(0)-event.getX(1))+(event.getY(0)-event.getY(1))*(event.getY(0)-event.getY(1)));
if(newDistance > 10f) {
matrix.set(savedMatrix);
matrix.postScale(newDistance/oldDistance, newDistance/oldDistance, mid.x, mid.y);
oldDistance = newDistance;
savedMatrix.set(matrix);
}
}
break;
}
imageView.setImageMatrix(matrix);
return true;
}
});
}
}
main.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView android:id="@+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="@drawable/img"
android:scaleType="matrix" >
</ImageView>
</LinearLayout>
Android多点触控技术的更多相关文章
- [yueqian_scut]Android多点触控技术和应用框架
Android多点触控技术跟Linux输入子系统紧密相关.本文将从应用的角度说明Android多点触控技术的接口和应用. 一.多点触控场景分析 网络上有关Android多点触控技术的文章多见于两点拉伸 ...
- Android多点触控技术实战,自由地对图片进行缩放和移动
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11100327 在上一篇文章中我带着大家一起实现了Android瀑布流照片墙的效果, ...
- Android多点触控技术,实现对图片的放大缩小平移,惯性滑动等功能
首先推荐一下鸿洋大大的打造个性的图片预览与多点触控视频教程,这套教程教我们一步一步实现了多点触控实现对图片的平移和缩放的功能.这篇文章我将在鸿洋大大的基础之上做了一些扩展功能: 1.图片的惯性滑动 2 ...
- Android 多点触控与简单手势(一)
现在一般的Android手机都会使用电容触摸屏最少可以支持两点触摸,多的可能是七八个,所以基本上都会支持多点触控, android系统中应用程序可以使用多点触控的事件来完成各种手势和场景需求. And ...
- Android 多点触控错误处理(java.lang.IllegalArgumentException: pointerIndex out of range)
最近做View的多点触控时,每次第一次触控事件完美运行,第二次就直接崩了,错误信息如下: 01-03 00:05:44.220 4377-4410/system_process E/AndroidRu ...
- 关于android多点触控
最近项目需要一个多点触控缩放的功能.然后上网查了下资料 总结一下: 首先android sdk版本很重要,比如你在AndroidManifest.xml中指定android:minSdkVersion ...
- Android多点触控(图片的缩放Demo)
本文主要介绍Android的多点触控,使用了一个图片缩放的实例,来更好的说明其原理.须要实现OnTouchListener接口,重写当中的onTouch方法. 实现效果图: 源码: 布局文 ...
- android 多点触控
多点触控 1.多点触控从字面意思讲就是你用大于等于2根的手指触摸子啊手机屏幕上. Android中监听触摸事件是onTouchEvent方法,它的参数为MotionEvent,下面列举MotionEv ...
- Android多点触控手势基础
处理多点触控手势 多点触控就是同时把一根以上的手指放在屏幕上. 再继续往下以前需要补充一些名词: 触控手势:就是把一根或者几根手指放在屏幕上做各种动作,其中包括保留一根手指的前提下,拿起或者放下其余的 ...
随机推荐
- 下载某资源文件并加载其中的所有Prefab到场景中
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> / ...
- Laravel开发:Laravel核心——Ioc服务容器
服务容器 在说 Ioc 容器之前,我们需要了解什么是 Ioc 容器. Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具. 在理解这句话之前,我们需要先了解一下服务容器的来龙去脉: ...
- sql server 字符串函数大全
平常会用到一些函数处理字符串,用的不算频繁,所以每次用到的时候就忘记了,这次在网上找了一篇文档,担心突然某一天这篇文章找不到了,然后就把文章的内容复制了一份: /* 1,ASCII返回字符表达式中最左 ...
- This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its 错误解决办法
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary log ...
- POJ 1518 A Round Peg in a Ground Hole【计算几何=_=你值得一虐】
链接: http://poj.org/problem?id=1584 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=22013#probl ...
- 九度OJ 1343:城际公路网 (最小生成树)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:445 解决:178 题目描述: 为了加快城市之间的通行和物资流动速度,A国政府决定在其境内的N个大中型城市之间,增加修建K条公路.已知这N个 ...
- 九度OJ 1283:第一个只出现一次的字符 (计数)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1808 解决:997 题目描述: 在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出现一次的字符 ...
- OLTP和OLAP
1 OLTP和OLAP online transaction processing,联机事务处理.业务类系统主要供基层人员使用,进行一线业务操作,通常被称为联机事务处理. online analyti ...
- Surpassing Human-Level Face Verification Performance on LFW with GaussianFace
Face verification remains a challenging problem in very complex conditions with large variations suc ...
- 算法设计 mac 字符串 标识 n维度 2 3维度 字符串 标识值 特征值
基向量