Android多点触控揭秘

本文详细介绍了在Android中实现多点触控功能,包括图片的放大缩小和旋转。通过OnTouchListener监听触控事件,ACTION_DOWN、ACTION_UP、ACTION_POINTER_DOWN、ACTION_POINTER_UP等常量区分单点和多点触控。示例代码展示了如何处理双指拉伸和单指旋转手势,以实现图片的缩放和旋转效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文原创,转载请注明:http://blog.csdn.net/cloudzfy1/article/details/6582707

Google 暑期大学生博客分享大赛 - 2011 Android 成长篇

如果您喜欢本文,请您投一下票: http://www.google.com/intl/zh-CN/daxue/blog/vote.html#tab2
谢谢。

最近需要做个Android的项目,需要使用多点触控的功能,上网找了很久,中文方面的资料相当少,英文方面也不多,经过自己的研究,加上对已知的一些资料的整理,下面为大家介绍一下Android多点触控程序的编写。

首先,我们准备好一张图片:

img.png

在Eclipse下新建一个Android工程(和一般Android工程一样),需要注意的是:Android的多点触控功能需要运行在Android 2.0版本以上。

工程

完成后,我们需要将原先准备好的图片放进res/drawable文件夹下(注意,如果是Android2.2,请放在三个drawable文件夹下),如图所示:

该图片是为了给该多点触控的实例提供运行环境,实现图片的放大和缩小。

下面,我们来修改一下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面板上显示,我们可以采用下面的一句话:

ImageView imageView = (ImageView)findViewById(R.id.imageView);

 

通过设置Matrix,我们可以获得对ImageView的一些基本操作。

OK,啰嗦这么多,下面进入主题:

我们需要实现一个OnTouchListener的方法,来设置ImageView的侦听属性,该接口位于android.view.View.OnTouchListener。

实现onTouch(View view, MotionEvent event)的方法,就可以获取触屏的感应事件了。

在该事件中,有两个参数可以用来获取对触摸的控制,这两个参数分别为:MotionEvent.getAction()和MotionEvent.ACTION_MASK,前者用于对单点触控进行操作,后者用于对多点触控进行操作,相应地,我们可以通过Android Developers’ Reference看到,对于单点触控,我们由MotionEvent.getAction()可以得到以下几种事件:ACTION_DOWN、ACTION_UP,而对于多点触控,由MotionEvent.ACTION_MASK,我们可以得到:ACTION_POINTER_DOWN、ACTION_POINTER_UP,都是MotionEvent中的常量,可以直接调用。而有些常量则是单点和多点共用的,如:ACTION_MOVE,因此在按下时,我们必须标记单点与多点触控的区别。

下面我们来介绍一下缩放功能的实现,对于缩放,我们定义有两种手势,一种是双指拉伸式,一种是单指旋转式。

首先,双指拉伸式。

这是一种比较常规的图片缩放方式,实现起来也比较方便,我们可以很容易想到,在处理多点触控事件时,如果没有别的手势干扰,我们只需检测两指按下时和移动之后的位置关系即可,如果距离变大,则是放大图片;反之则是缩小图片。

主要代码如下:

 

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);
	}
}


然后,单指旋转式。

这是一种单指操作中比较流行的方式,然而实现起来并非特别方便。具体说来,我们可以定义顺时针转动为图片放大,逆时针转动为图片缩小。在没有其他干扰项的时候,我们可以通过捕获三次连续移动来得知手势顺时针还是逆时针。如下图所示,我们把前两次的位置作一个向量A,后两次位置作一个向量B,如果向量B比向量A大,则是逆时针;向量B比向量A小则是顺时针。当然,我们这里就要用到反三角函数,同时要注意2pi的角度问题哦~

注意:在处理同为单指操作或者同为多指操作的时候,要考虑不同行为之间的区别。

主要代码如下:

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;
	}
}


下面是实现的结果截图:

 初始画面:

通过鼠标顺时针转动后的效果图:

通过鼠标逆时针转动后的效果图:

下面是代码的具体解析:

TouchActivity.java

 

package example.MultiTouch;

import android.app.Activity;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

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;
			}
        	
        });
    }
}


AndroidManifest.xml

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="example.MultiTouch"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".TouchActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>


 

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值