Android多点触控揭秘

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文件,如下所示:

  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/imageView"  
  8.             android:layout_width="fill_parent"  
  9.             android:layout_height="fill_parent"  
  10.             android:src="@drawable/img"  
  11.             android:scaleType="matrix" >  
  12. </ImageView>  
  13. </LinearLayout>  
<?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面板上显示,我们可以采用下面的一句话:

  1. ImageView imageView = (ImageView)findViewById(R.id.imageView);  
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,因此在按下时,我们必须标记单点与多点触控的区别。

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

首先,双指拉伸式。

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

主要代码如下:

 

  1. if(mode == ZOOM)  
  2. {  
  3.     float newDistance;  
  4.     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)));  
  5.     if(newDistance > 10f) {  
  6.         matrix.set(savedMatrix);  
  7.         matrix.postScale(newDistance/oldDistance, newDistance/oldDistance, mid.x, mid.y);  
  8.         oldDistance = newDistance;  
  9.         savedMatrix.set(matrix);  
  10.     }  
  11. }  
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的角度问题哦~

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

主要代码如下:

  1. if (mode == MOVE)  
  2. {  
  3.     if(rotate == NONE) {  
  4.         savedMatrix.set(matrix);  
  5.         mid.set(event.getX(), event.getY());  
  6.         rotate = ROTATION;  
  7.     }  
  8.     else {  
  9.         matrix.set(savedMatrix);  
  10.         double a = Math.atan((mid.y-start.y)/(mid.x-start.x));  
  11.         double b = Math.atan((event.getY()-mid.y)/(event.getX()-mid.x));  
  12.         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)) {  
  13.             matrix.postScale((float)0.9, (float)0.9);  
  14.         }  
  15.         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)) {  
  16.         matrix.postScale((float)1.1, (float)1.1);  
  17.         }  
  18.         start.set(event.getX(), event.getY());  
  19.         rotate = NONE;  
  20.     }  
  21. }  
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

 

  1. package example.MultiTouch;  
  2.   
  3. import android.app.Activity;  
  4. import android.graphics.Matrix;  
  5. import android.graphics.PointF;  
  6. import android.os.Bundle;  
  7. import android.view.MotionEvent;  
  8. import android.view.View;  
  9. import android.view.View.OnTouchListener;  
  10. import android.widget.ImageView;  
  11.   
  12. public class TouchActivity extends Activity {  
  13.       
  14.     private static final int NONE = 0;  
  15.     private static final int MOVE = 1;  
  16.     private static final int ZOOM = 2;  
  17.       
  18.     private static final int ROTATION = 1;  
  19.       
  20.     private int mode = NONE;  
  21.     private Matrix matrix = new Matrix();  
  22.     private Matrix savedMatrix = new Matrix();  
  23.     private PointF start = new PointF();  
  24.     private PointF mid = new PointF();  
  25.     private float s = 0;  
  26.     private float oldDistance;  
  27.     private int rotate = NONE;  
  28.     @Override  
  29.     public void onCreate(Bundle savedInstanceState) {  
  30.         super.onCreate(savedInstanceState);  
  31.         setContentView(R.layout.main);  
  32.           
  33.         ImageView imageView = (ImageView)findViewById(R.id.imageView);  
  34.         imageView.setOnTouchListener(new OnTouchListener()  
  35.         {  
  36.   
  37.             @Override  
  38.             public boolean onTouch(View view, MotionEvent event) {  
  39.                 ImageView imageView = (ImageView)view;  
  40.                 switch (event.getAction()&MotionEvent.ACTION_MASK) {  
  41.                 case MotionEvent.ACTION_DOWN:  
  42.                     savedMatrix.set(matrix);  
  43.                     start.set(event.getX(), event.getY());  
  44.                     mode = MOVE;  
  45.                     rotate = NONE;  
  46.                     break;  
  47.                 case MotionEvent.ACTION_UP:  
  48.                 case MotionEvent.ACTION_POINTER_UP:  
  49.                     mode = NONE;  
  50.                     break;  
  51.                 case MotionEvent.ACTION_POINTER_DOWN:  
  52.                     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)));  
  53.                     if (oldDistance > 10f) {  
  54.                         savedMatrix.set(matrix);  
  55.                         mid.set((event.getX(0)+event.getX(1))/2, (event.getY(0)+event.getY(1))/2);  
  56.                         mode = ZOOM;  
  57.                     }  
  58.                 case MotionEvent.ACTION_MOVE:  
  59.                     if (mode == MOVE)  
  60.                     {  
  61.                         if(rotate == NONE) {  
  62.                             savedMatrix.set(matrix);  
  63.                             mid.set(event.getX(), event.getY());  
  64.                             rotate = ROTATION;  
  65.                         }  
  66.                         else {  
  67.                             matrix.set(savedMatrix);  
  68.                             double a = Math.atan((mid.y-start.y)/(mid.x-start.x));  
  69.                             double b = Math.atan((event.getY()-mid.y)/(event.getX()-mid.x));  
  70.                             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)) {  
  71.                                 matrix.postScale((float)0.9, (float)0.9);  
  72.                             }  
  73.                             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)) {  
  74.                                 matrix.postScale((float)1.1, (float)1.1);  
  75.                             }  
  76.                             start.set(event.getX(), event.getY());  
  77.                             rotate = NONE;  
  78.                         }  
  79.                     }  
  80.                     else if(mode == ZOOM)  
  81.                     {  
  82.                         float newDistance;  
  83.                         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)));  
  84.                         if(newDistance > 10f) {  
  85.                             matrix.set(savedMatrix);  
  86.                             matrix.postScale(newDistance/oldDistance, newDistance/oldDistance, mid.x, mid.y);  
  87.                             oldDistance = newDistance;  
  88.                             savedMatrix.set(matrix);  
  89.                         }  
  90.                     }  
  91.                     break;  
  92.                 }  
  93.                 imageView.setImageMatrix(matrix);  
  94.                 return true;  
  95.             }  
  96.               
  97.         });  
  98.     }  
  99. }  
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

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.       package="example.MultiTouch"  
  4.       android:versionCode="1"  
  5.       android:versionName="1.0">  
  6.     <uses-sdk android:minSdkVersion="8" />  
  7.   
  8.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
  9.         <activity android:name=".TouchActivity"  
  10.                   android:label="@string/app_name">  
  11.             <intent-filter>  
  12.                 <action android:name="android.intent.action.MAIN" />  
  13.                 <category android:name="android.intent.category.LAUNCHER" />  
  14.             </intent-filter>  
  15.         </activity>  
  16.   
  17.     </application>  
  18. </manifest>  
引用自:http://blog.csdn.net/cloudzfy1/article/details/6582707

----------------------------------
参考2:

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>  
引用自:http://www.cnblogs.com/devinzhang/archive/2012/02/27/2369942.html

--------------------------------
参考3:

Android 百度地图的缩放功能的实现

这个功能十分简单,希望能对android百度地图入门不久的朋友有所帮助。

编写这个功能需要对其api 较为了解。

 

由于功能简单本人就直接黏贴代码:

 

在布xml 局文件中添加放大缩小控件。

        <ZoomControls
            android:id="@+id/zoomControls1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/bmapsView"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="30dp" />

 

在gen 的R文件中自动生成对应的id。

 

在activity的继承类中onCreate函数里定义变量:

 ZoomControls    zoomControls = (ZoomControls) findViewById(R.id.zoomControls1);

 zoomControls.setIsZoomInEnabled(true);
 zoomControls.setIsZoomOutEnabled(true);

//获取当前的视野级别。当点击放大和缩小的时候这个数是动态变化的。

rank = mapView.getZoomLevel();

 

/**地图放大**/
        zoomControls.setOnZoomInClickListener(new ZoomControls.OnClickListener(){
            public void onClick(View v) {
                zoomControls.setIsZoomOutEnabled(true);
                Log.i(this.toString(),"放大");
                MapController mc = mapView.getController();
                mc.zoomInFixing(250,250);
//                int level = mapView.getZoomLevel();
                rank++;
                Log.i(this.toString(),"放大的视野级别为:"+rank);

//视野级别的取之范围是【3 18】
                if(rank >18){
                 //放大到该值,放大点击不可用
                 zoomControls.setIsZoomInEnabled(false);
                 rank=18;
                } 
                else if(rank >= 15 && rank <=18)
                {
                 Drawable marker = getResources().getDrawable(R.drawable.iconmarka);  //得到需要标在地图上的资源
                 marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());   //为maker定义位置和边界
              overitem = new OverItemT(marker, MContext, 4);
              mapView.getOverlays().add(overitem);
                }
                else
                {
                 mapView.getOverlays().clear();
                }
            }});
    
     

/**地图缩小**/
        zoomControls.setOnZoomOutClickListener(new ZoomControls.OnClickListener(){
            public void onClick(View v) {
                zoomControls.setIsZoomInEnabled(true);
                Log.i(this.toString(),"缩小");
                MapController mc = mapView.getController();
                mc.zoomOutFixing(250,250);
//                int level = mapView.getZoomLevel()-1;
                --rank;
                Log.i(this.toString(),"缩小的视野级别为:"+rank);

                if(rank <3){
                 //缩小到该值后 缩小点击不可用
                 Log.i(this.toString(),"已经最小了!");
                  zoomControls.setIsZoomOutEnabled(false);
                  rank=3;
                 
                }
                 if(rank < 15 && rank >= 3)
                {
                    mapView.getOverlays().clear(); 
                }
                 if(rank >=15 && rank <=18)
                {
                 Drawable marker = getResources().getDrawable(R.drawable.iconmarka);  //得到需要标在地图上的资源
                 marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());   //为maker定义位置和边界
              overitem = new OverItemT(marker, MContext, 4);
              mapView.getOverlays().add(overitem);
                }
            }});

 

 

毕!

引用自:http://hi.baidu.com/pengdavey/item/354cc03eb1a474c41a96964d
------------------------------------
参考4:

Android游戏开发之地图编辑器的使用以及绘制地图 (四)

分类: 【Android 开发系列锦集】 13560人阅读 评论(54) 收藏 举报
雨松MOMO带你走进游戏开发的世界之地图编辑器的使用以及绘制地图






雨松MOMO原创文章如转载,请注明:转载至我的独立域名博客雨松MOMO程序研究院,原文地址:http://www.xuanyusong.com/archives/211










Mappy中文地图编辑器的使用说明

下载地址:http://download.csdn.net/source/3452259

        压缩包中包含 游戏地图编辑器使用指南 与地图资源图片 宫院1.png 一张 mapwin.exe 可执行文件 map.FMP 与map.TXT为使用编辑器生成出来的保存文件与地图数组。





解压后打开地图编辑器 mapwin.exe.exe 创建一张新的地图。




由于我用的Android模拟器宽高是320X480

地图宽的块数 就是 320 / 32 = 10

地图高的块数 就是 480 / 32 = 15




这里扩充一下 实际在工作开发中因为手机的分辨率各式各样 所以是需要尺寸考虑自适应的 有两种方法可以拿到当前手机屏幕的宽高

  1.   Display display = getWindowManager().getDefaultDisplay();  
  2.   Log.i("view" , "height:" +display.getHeight());  
  3.   Log.i("view" , "width:" +display.getWidth());  
  4.   DisplayMetrics displayMetrics = getResources().getDisplayMetrics();  
  5.   Log.i("view" , "height" +displayMetrics.heightPixels);  
  6.   Log.i("view" , "width" +displayMetrics.widthPixels);  
  Display display = getWindowManager().getDefaultDisplay();
  Log.i("view" , "height:" +display.getHeight());
  Log.i("view" , "width:" +display.getWidth());
  DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
  Log.i("view" , "height" +displayMetrics.heightPixels);
  Log.i("view" , "width" +displayMetrics.widthPixels);

弹出框后点击确定


导入地图图块 编辑器下载地址中包含了一张 地图图片 可以选择使用

因为编辑器是须要美术图片配合使用的 比如tile的尺寸 图片的宽高尺寸必需能被整除。




导入地图图块成功 右侧为导入的地图资源 接下来就是自己拖动右侧地图块拼出自己想要的地图了。




接下来我将填充3个图层 最底层 实体层 物理层 我会一一介绍他们的作用

图层0为最底层 绘制地图先绘制这一层





图层1为实物层 这一层主要绘制一些actor 绘制完第一层在绘制这一层




图层2为物理层检测物理碰撞这一层不用绘制但是玩家每移动一次就须要以玩家当前点在地图数组中的角标 和物理层做判断是否碰撞,它和Actor层的位置一样。




拼地图的使用技巧 编辑新图层的时候可以把上一个涂层打开进行对比编辑。

这样子就可以根据0图层的信息来编辑图层1






       地图块拼完后编辑完成后点击保存文件 后在点击保存文本数据  地图数组文件就生成出来了 文件命为map.TXT 里面就存着我们编辑的3个地图层的地图信息。



使用Mappy中文地图编辑器生成的地图信息数组来绘制游戏地图


效果图如下



代码实现

这里我先说一下游戏窗口的全屏实现方法 

第一种
  1. // 全屏显示窗口   
  2. requestWindowFeature(Window.FEATURE_NO_TITLE);  
  3. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);  
	// 全屏显示窗口
	requestWindowFeature(Window.FEATURE_NO_TITLE);
	getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

第二种 AndroidManifest.xml 中加入
  1. <activity android:name=".activity"  
  2.           android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />  
	<activity android:name=".activity"
			  android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />

这里我详细说一下编辑器生成出来的数组怎么用?就拿生成出来的ID 137为例  假设 tile的宽高为32 137表示从图片的左上角从左到右从上到下 数到第137个tile 就是我们须要绘制的tile  
绘制方面利用 clipRect方法来剪裁图片 实现绘制 下一章我讲游戏中的摄像头机制 会详细介绍这一点。


  1. public class mapAcitvity extends Activity {  
  2.   
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.     // 全屏显示窗口   
  7.     requestWindowFeature(Window.FEATURE_NO_TITLE);  
  8.     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);  
  9.     //显示自定义的游戏View   
  10.     setContentView(new MapView(this));  
  11.     }  
  12.       
  13.       
  14.     public class MapView extends View{  
  15.   
  16.         //tile块的宽高   
  17.     public final static int TILE_WIDTH = 32;  
  18.     public final static int TILE_HEIGHT = 32;  
  19.       
  20.         //tile块的宽高的数量   
  21.     public final static int TILE_WIDTH_COUNT = 10;  
  22.     public final static int TILE_HEIGHT_COUNT = 15;  
  23.       
  24.     //数组元素为0则什么都不画   
  25.     public final static int TILE_NULL = 0;  
  26.     //第一层游戏View地图数组   
  27.     public int [][]mMapView = {  
  28.         { 1111137137137111 },  
  29.         { 1111137137137111 },  
  30.         { 1111137137137111 },  
  31.         { 137137137137137137137137137137 },  
  32.         { 137137137137137137137137137137 },  
  33.         { 11111111137137 },  
  34.         { 11111111137137 },  
  35.         { 11111111137137 },  
  36.         { 11111111137137 },  
  37.         { 11111111137137 },  
  38.         { 11111111137137 },  
  39.         { 137137137137137137137137137137 },  
  40.         { 137137137137137137137137137137 },  
  41.         { 1111113713713711 },  
  42.         { 1111113713713711 }  
  43.         };  
  44.   
  45.     //第二层游戏实体actor数组   
  46.     public int [][]mMapAcotor  = {  
  47.         { 102103103104000165166167 },  
  48.         { 110111111112000173174175 },  
  49.         { 126127127128000181182183 },  
  50.         { 0000000000 },  
  51.         { 0000000000 },  
  52.         { 41424344000000 },  
  53.         { 49505152000000 },  
  54.         { 5758596022923023123200 },  
  55.         { 6566676823723823924000 },  
  56.         { 000024524624724800 },  
  57.         { 00000254255000 },  
  58.         { 0000000000 },  
  59.         { 0000000000 },  
  60.         { 102103103103104000143144 },  
  61.         { 110111111111112000143144 }  
  62.         };  
  63.       
  64.     //第三层游戏碰撞物理层数组    
  65.     //下一章介绍   
  66.     //....................   
  67.       
  68.     //游戏地图资源   
  69.     Bitmap mBitmap = null;  
  70.       
  71.     //资源文件   
  72.     Resources mResources = null;  
  73.       
  74.     //游戏画笔   
  75.     Paint mPaint = null;  
  76.       
  77.     //横向纵向tile块的数量   
  78.     int mWidthTileCount = 0;  
  79.     int mHeightTileCount = 0;  
  80.   
  81.     //横向纵向tile块的数量   
  82.     int mBitMapWidth = 0;  
  83.     int mBitMapHeight = 0;  
  84.       
  85.     /** 
  86.      * 构造方法 
  87.      * @param context 
  88.      */  
  89.     public MapView(Context context) {  
  90.         super(context);  
  91.       
  92.         mPaint = new Paint();  
  93.         mBitmap = ReadBitMap(context, R.drawable.map);  
  94.         mBitMapWidth = mBitmap.getWidth();  
  95.         mBitMapHeight = mBitmap.getHeight();  
  96.         mWidthTileCount = mBitMapWidth / TILE_WIDTH;  
  97.         mHeightTileCount = mBitMapHeight / TILE_HEIGHT;  
  98.     }  
  99.       
  100.         @Override  
  101.         protected void onDraw(Canvas canvas) {  
  102.             DrawMap(canvas,mPaint,mBitmap);  
  103.             super.onDraw(canvas);  
  104.              
  105.         }  
  106.           
  107.         private void DrawMap(Canvas canvas,Paint paint ,Bitmap bitmap) {  
  108.             int i,j;  
  109.             for(i = 0; i< TILE_HEIGHT_COUNT; i++) {  
  110.             for(j = 0; j<TILE_WIDTH_COUNT;j++) {  
  111.                 int ViewID =  mMapView[i][j];  
  112.                 int ActorID = mMapAcotor[i][j];  
  113.                 //绘制地图第一层   
  114.                 if(ViewID > TILE_NULL) {  
  115.                  DrawMapTile(ViewID,canvas,paint,bitmap, j * TILE_WIDTH , i * TILE_HEIGHT);  
  116.                 }  
  117.                  
  118.                 //绘制地图第二层   
  119.                 if(ActorID > TILE_NULL) {  
  120.                 DrawMapTile(ActorID,canvas,paint,bitmap, j * TILE_WIDTH , i * TILE_HEIGHT);  
  121.                 }  
  122.             }  
  123.             }  
  124.         }  
  125.           
  126.           
  127.           
  128.         /** 
  129.          * 根据ID绘制一个tile块 
  130.          * @param id 
  131.          * @param canvas 
  132.          * @param paint 
  133.          * @param bitmap 
  134.          */  
  135.         private void DrawMapTile(int id,Canvas canvas,Paint paint ,Bitmap bitmap,int x, int y) {  
  136.             //根据数组中的ID算出在地图资源中的XY 坐标   
  137.             //因为编辑器默认0 所以第一张tile的ID不是0而是1 所以这里 -1  
  138.             id--;  
  139.             int count = id /mWidthTileCount;  
  140.             int bitmapX = (id - (count * mWidthTileCount)) * TILE_WIDTH;  
  141.             int bitmapY = count * TILE_HEIGHT;  
  142.             DrawClipImage(canvas,paint,bitmap,x,y,bitmapX,bitmapY,TILE_WIDTH,TILE_HEIGHT);  
  143.         }  
  144.           
  145.         /**  
  146.          * 读取本地资源的图片  
  147.          * @param context  
  148.          * @param resId  
  149.          * @return  
  150.          */    
  151.         public Bitmap ReadBitMap(Context context, int resId){    
  152.             BitmapFactory.Options opt = new BitmapFactory.Options();    
  153.             opt.inPreferredConfig = Bitmap.Config.RGB_565;     
  154.             opt.inPurgeable = true;    
  155.             opt.inInputShareable = true;    
  156.             //获取资源图片     
  157.             InputStream is = context.getResources().openRawResource(resId);    
  158.                 return BitmapFactory.decodeStream(is,null,opt);    
  159.         }    
  160.       
  161.         /** 
  162.      * 绘制图片中的一部分图片 
  163.      * @param canvas 
  164.      * @param paint 
  165.      * @param bitmap 
  166.      * @param x 
  167.      * @param y 
  168.      * @param src_x 
  169.      * @param src_y 
  170.      * @param src_width 
  171.      * @param src_Height 
  172.      */  
  173.     private void DrawClipImage(Canvas canvas,Paint paint ,Bitmap bitmap, int x, int y, int src_x, int src_y, int src_xp, int src_yp) {  
  174.         canvas.save();  
  175.         canvas.clipRect(x, y, x + src_xp, y + src_yp);  
  176.         canvas.drawBitmap(bitmap, x - src_x, y - src_y,paint);  
  177.         canvas.restore();  
  178.     }  
  179.     }  
  180. }  
public class mapAcitvity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
	// 全屏显示窗口
	requestWindowFeature(Window.FEATURE_NO_TITLE);
	getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
	//显示自定义的游戏View
	setContentView(new MapView(this));
    }
    
    
    public class MapView extends View{

        //tile块的宽高
	public final static int TILE_WIDTH = 32;
	public final static int TILE_HEIGHT = 32;
	
        //tile块的宽高的数量
	public final static int TILE_WIDTH_COUNT = 10;
	public final static int TILE_HEIGHT_COUNT = 15;
	
	//数组元素为0则什么都不画
	public final static int TILE_NULL = 0;
	//第一层游戏View地图数组
	public int [][]mMapView = {
		{ 1, 1, 1, 1, 137, 137, 137, 1, 1, 1 },
		{ 1, 1, 1, 1, 137, 137, 137, 1, 1, 1 },
		{ 1, 1, 1, 1, 137, 137, 137, 1, 1, 1 },
		{ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 },
		{ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 },
		{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
		{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
		{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
		{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
		{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
		{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
		{ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 },
		{ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 },
		{ 1, 1, 1, 1, 1, 137, 137, 137, 1, 1 },
		{ 1, 1, 1, 1, 1, 137, 137, 137, 1, 1 }
		};

	//第二层游戏实体actor数组
	public int [][]mMapAcotor  = {
		{ 102, 103, 103, 104, 0, 0, 0, 165, 166, 167 },
		{ 110, 111, 111, 112, 0, 0, 0, 173, 174, 175 },
		{ 126, 127, 127, 128, 0, 0, 0, 181, 182, 183 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 41, 42, 43, 44, 0, 0, 0, 0, 0, 0 },
		{ 49, 50, 51, 52, 0, 0, 0, 0, 0, 0 },
		{ 57, 58, 59, 60, 229, 230, 231, 232, 0, 0 },
		{ 65, 66, 67, 68, 237, 238, 239, 240, 0, 0 },
		{ 0, 0, 0, 0, 245, 246, 247, 248, 0, 0 },
		{ 0, 0, 0, 0, 0, 254, 255, 0, 0, 0 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
		{ 102, 103, 103, 103, 104, 0, 0, 0, 143, 144 },
		{ 110, 111, 111, 111, 112, 0, 0, 0, 143, 144 }
		};
	
	//第三层游戏碰撞物理层数组 
	//下一章介绍
	//....................
	
	//游戏地图资源
	Bitmap mBitmap = null;
	
	//资源文件
	Resources mResources = null;
	
	//游戏画笔
	Paint mPaint = null;
	
	//横向纵向tile块的数量
	int mWidthTileCount = 0;
	int mHeightTileCount = 0;

	//横向纵向tile块的数量
	int mBitMapWidth = 0;
	int mBitMapHeight = 0;
	
	/**
	 * 构造方法
	 * @param context
	 */
	public MapView(Context context) {
	    super(context);
	
	    mPaint = new Paint();
	    mBitmap = ReadBitMap(context, R.drawable.map);
	    mBitMapWidth = mBitmap.getWidth();
	    mBitMapHeight = mBitmap.getHeight();
	    mWidthTileCount = mBitMapWidth / TILE_WIDTH;
	    mHeightTileCount = mBitMapHeight / TILE_HEIGHT;
	}
	
        @Override
        protected void onDraw(Canvas canvas) {
            DrawMap(canvas,mPaint,mBitmap);
            super.onDraw(canvas);
           
        }
        
        private void DrawMap(Canvas canvas,Paint paint ,Bitmap bitmap) {
            int i,j;
            for(i = 0; i< TILE_HEIGHT_COUNT; i++) {
        	for(j = 0; j<TILE_WIDTH_COUNT;j++) {
        	    int ViewID =  mMapView[i][j];
        	    int ActorID = mMapAcotor[i][j];
        	    //绘制地图第一层
        	    if(ViewID > TILE_NULL) {
        		 DrawMapTile(ViewID,canvas,paint,bitmap, j * TILE_WIDTH , i * TILE_HEIGHT);
        	    }
        	   
        	    //绘制地图第二层
        	    if(ActorID > TILE_NULL) {
        		DrawMapTile(ActorID,canvas,paint,bitmap, j * TILE_WIDTH , i * TILE_HEIGHT);
        	    }
        	}
            }
        }
        
        
        
        /**
         * 根据ID绘制一个tile块
         * @param id
         * @param canvas
         * @param paint
         * @param bitmap
         */
        private void DrawMapTile(int id,Canvas canvas,Paint paint ,Bitmap bitmap,int x, int y) {
            //根据数组中的ID算出在地图资源中的XY 坐标
            //因为编辑器默认0 所以第一张tile的ID不是0而是1 所以这里 -1
            id--;
            int count = id /mWidthTileCount;
            int bitmapX = (id - (count * mWidthTileCount)) * TILE_WIDTH;
            int bitmapY = count * TILE_HEIGHT;
            DrawClipImage(canvas,paint,bitmap,x,y,bitmapX,bitmapY,TILE_WIDTH,TILE_HEIGHT);
        }
        
        /** 
         * 读取本地资源的图片 
         * @param context 
         * @param resId 
         * @return 
         */  
        public Bitmap ReadBitMap(Context context, int resId){  
            BitmapFactory.Options opt = new BitmapFactory.Options();  
            opt.inPreferredConfig = Bitmap.Config.RGB_565;   
            opt.inPurgeable = true;  
            opt.inInputShareable = true;  
            //获取资源图片  
            InputStream is = context.getResources().openRawResource(resId);  
                return BitmapFactory.decodeStream(is,null,opt);  
        }  
	
        /**
	 * 绘制图片中的一部分图片
	 * @param canvas
	 * @param paint
	 * @param bitmap
	 * @param x
	 * @param y
	 * @param src_x
	 * @param src_y
	 * @param src_width
	 * @param src_Height
	 */
	private void DrawClipImage(Canvas canvas,Paint paint ,Bitmap bitmap, int x, int y, int src_x, int src_y, int src_xp, int src_yp) {
	    canvas.save();
	    canvas.clipRect(x, y, x + src_xp, y + src_yp);
	    canvas.drawBitmap(bitmap, x - src_x, y - src_y,paint);
	    canvas.restore();
	}
    }
}

最后如果你还是觉得我写的不够详细 看的不够爽 不要紧我把源代码的下载地址贴出来 欢迎大家一起讨论学习
雨松MOMO希望可以和大家一起进步。

下载地址:http://www.xuanyusong.com/archives/211

引用自:http://blog.csdn.net/xys289187120/article/details/6615532
------------------------------------
参考:
Android--多点触控的实现
[日期:2011-08-28] 来源:Linux社区  作者:shaojie519 [字体:大 中 小]  
 Android多点触控在本质上需要LCD驱动和程序本身设计上支持,目前市面上只要使用电容屏触控原理的手机均可以支持多点触控Multitouch技术,对于网页缩放、手势操作上有更好的用户体验。 在Android平台上事件均使用了MotionEvent对象方式处理,比如开始触控时会触发ACTION_DOWN而移动操作时为 ACTION_MOVE最终放开手指时触发ACTION_UP事件。当然还有用户无规则的操作可能触发ACTION_CANCEL这个动作.


对于常规的控件触控操作在内部为View的setOnTouchListener()接口实现的 onTouchEvent()方法来处理。




下面用ImageView做演示:








                           




main.xml:


<?xml version="1.0" encoding="utf-8"?>   
<FrameLayout   
xmlns:android="http://schemas.android.com/apk/res/android"   
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/pic"   
android:scaleType="matrix" >   
</ImageView>   
</FrameLayout>  




要注意的是 android:scaleType 的属性必须设置为matrix,否则图片无法改变大小。 
java文件:


package com.shao.muti;   
   
import android.app.Activity;   
import android.graphics.Matrix;   
import android.graphics.PointF;   
import android.os.Bundle;   
import android.util.Log;   
import android.view.MotionEvent;   
import android.view.View;   
import android.view.View.OnTouchListener;   
import android.widget.ImageView;   
   
public class MutiTouchActivity extends Activity implements OnTouchListener{   
    /** Called when the activity is first created. */   
    private static final String TAG="Touch";   
    private static final int NONE= 0 ;   
    private static final int DRAG = 1;   
    private static final int ZOOM =2;   
    int mode  = NONE;   
    Matrix matrix = new Matrix();   
    Matrix savedMatrix =  new Matrix();   
       
    PointF start = new PointF();   
    PointF mid = new PointF();   
    float oldDist = 1f;   
       
    @Override   
    public void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);   
        setContentView(R.layout.main);   
        ImageView view = (ImageView) findViewById(R.id.imageView);   
        view.setOnTouchListener(this);   
    }   
   
    @Override   
    public boolean onTouch(View v, MotionEvent event) {   
        // TODO Auto-generated method stub    
        ImageView view = (ImageView) v;   
        printEventinfo(event);//测试输出信息    
        switch(event.getAction()&MotionEvent.ACTION_MASK)   
        {   
        case MotionEvent.ACTION_DOWN:   
            savedMatrix.set(matrix);   
               //設置初始點位置    
            start.set(event.getX(),event.getY());   
            Log.d(TAG,"mode=DRAG");   
            mode  =DRAG;   
            break;   
        case MotionEvent.ACTION_POINTER_1_DOWN:   
            oldDist= spacing(event);   
            Log.d(TAG,"lodDist="+oldDist);   
            if(oldDist>10f){   
                savedMatrix.set(matrix);   
                midPoint(mid,event);   
                mode = ZOOM;   
                Log.d(TAG,"mode=ZOOM");   
                   
            }   
            break;   
        case MotionEvent.ACTION_UP:   
        case MotionEvent.ACTION_POINTER_1_UP:   
            mode  =NONE;   
            Log.d(TAG,"mode=NONE");   
            break;   
        case MotionEvent.ACTION_MOVE:   
            if(mode==DRAG){   
                matrix.set(savedMatrix);   
                matrix.postTranslate(event.getX()-start.x, event.getY()-start.y);   
            }   
            else if(mode == ZOOM){   
                 float newDist = spacing(event);   
                    Log.d(TAG, "newDist=" + newDist);   
                    if (newDist > 10f) {   
                       matrix.set(savedMatrix);   
                       float scale = newDist / oldDist;   
                       matrix.postScale(scale, scale, mid.x, mid.y);   
                    }   
                 }   
            break;   
        }   
        view.setImageMatrix(matrix);   
        return true;   
           
    }   
    private void printEventinfo(MotionEvent event){   
        int num =0;   
        System.out.println("触控动作---->"+event.getAction()); //获取触控动作比如ACTION_DOWN    
        System.out.println("取触控点的数量--->"+event.getPointerCount()); //获取触控点的数量,比如2则可能是两个手指同时按压屏幕    
        num = event.getPointerCount();   
        for(int i=0;i<num;i++){   
            System.out.println("索引-->"+event.getPointerId(i)); //对于每个触控的点的细节,我们可以通过一个循环执行getPointerId方法获取索引    
            System.out.println("第i个触控点的x位置-->"+event.getX(i)); //获取第i个触控点的x位置    
            System.out.println("第i个触控点的y位置-->"+event.getY(i)); //获取第i个点触控的y位置    
            System.out.println("手指压力-->"+event.getPressure(i)); //LCD可以感应出用户的手指压力,当然具体的级别由驱动和物理硬件决定的    
        }   
        System.out.println("开始时间"+event.getDownTime()); //按下开始时间    
        System.out.println(" 事件结束时间"+event.getEventTime()); // 事件结束时间    
        System.out.println("总共按下时花费时间"+(event.getEventTime()-event.getDownTime())); //总共按下时花费时间    
   
    }   
      /** Determine the space between the first two fingers */   
    private float spacing(MotionEvent event){   
        float x = event.getX(0)-event.getY(1);   
        float y = event.getY(0)-event.getY(1);   
        return (float)Math.sqrt(x*x+y*y);   
    }   
    /** Calculate the mid point of the first two fingers */   
       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);   
       }   
}  
本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2011-08/41776.htm
引用自:http://www.linuxidc.com/Linux/2011-08/41776.htm
------------------------------------------
参考:
Android开发:多点触控测试代码 PointerLocation
[日期:2011-10-02] 来源:Linux社区  作者:victoryckl [字体:大 中 小]  
 PointerLocation这个多点触控测试程序,在Android的源码中有,只包括下面的两个文件,


android\development\apps\Development\src\com\android\development\PointerLocation.java




android\frameworks\base\core\java\com\android\internal\widget\PointerLocationView.java




新建个工程,只要将这两个文件拷贝出来,修改下包名,就可以运行起来,真好用。


本文工程源码下载地址:


免费下载地址在 http://linux.linuxidc.com/


用户名与密码都是www.linuxidc.com


具体下载目录在 /pub/Android源码集锦/2011年/10月/Android开发:多点触控测试代码 PointerLocation/


 


PointerLocation.java


package com.ckl.PointerLocation;   
   
import android.app.Activity;   
import android.os.Bundle;   
import android.view.WindowManager;   
   
/**  
 * Demonstrates wrapping a layout in a ScrollView.  
 *  
 */   
public class PointerLocation extends Activity {   
    @Override   
    protected void onCreate(Bundle icicle) {   
        super.onCreate(icicle);   
        setContentView(new PointerLocationView(this));   
           
        // Make the screen full bright for this activity.    
        WindowManager.LayoutParams lp = getWindow().getAttributes();   
        lp.screenBrightness = 1.0f;   
        getWindow().setAttributes(lp);   
    }   
}  
PointerLocationView.java 
/*  
 * Copyright (C) 2010 The Android Open Source Project  
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");  
 * you may not use this file except in compliance with the License.  
 * You may obtain a copy of the License at  
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0  
 *  
 * Unless required by applicable law or agreed to in writing, software  
 * distributed under the License is distributed on an "AS IS" BASIS,  
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
 * See the License for the specific language governing permissions and  
 * limitations under the License.  
 */   
   
package com.ckl.PointerLocation;   
   
import android.content.Context;   
import android.graphics.Canvas;   
import android.graphics.Paint;   
import android.graphics.Paint.FontMetricsInt;   
import android.util.Log;   
import android.view.MotionEvent;   
import android.view.VelocityTracker;   
import android.view.View;   
import android.view.ViewConfiguration;   
   
import java.util.ArrayList;   
   
public class PointerLocationView extends View {   
    public static class PointerState {   
        private final ArrayList<Float> mXs = new ArrayList<Float>();   
        private final ArrayList<Float> mYs = new ArrayList<Float>();   
        private boolean mCurDown;   
        private int mCurX;   
        private int mCurY;   
        private float mCurPressure;   
        private float mCurSize;   
        private int mCurWidth;   
        private VelocityTracker mVelocity;   
    }   
   
    private final ViewConfiguration mVC;   
    private final Paint mTextPaint;   
    private final Paint mTextBackgroundPaint;   
    private final Paint mTextLevelPaint;   
    private final Paint mPaint;   
    private final Paint mTargetPaint;   
    private final Paint mPathPaint;   
    private final FontMetricsInt mTextMetrics = new FontMetricsInt();   
    private int mHeaderBottom;   
    private boolean mCurDown;   
    private int mCurNumPointers;   
    private int mMaxNumPointers;   
    private final ArrayList<PointerState> mPointers   
             = new ArrayList<PointerState>();   
       
    private boolean mPrintCoords = true;   
       
    public PointerLocationView(Context c) {   
        super(c);   
        setFocusable(true);   
        mVC = ViewConfiguration.get(c);   
        mTextPaint = new Paint();   
        mTextPaint.setAntiAlias(true);   
        mTextPaint.setTextSize(10   
                * getResources().getDisplayMetrics().density);   
        mTextPaint.setARGB(255, 0, 0, 0);   
        mTextBackgroundPaint = new Paint();   
        mTextBackgroundPaint.setAntiAlias(false);   
        mTextBackgroundPaint.setARGB(128, 255, 255, 255);   
        mTextLevelPaint = new Paint();   
        mTextLevelPaint.setAntiAlias(false);   
        mTextLevelPaint.setARGB(192, 255, 0, 0);   
        mPaint = new Paint();   
        mPaint.setAntiAlias(true);   
        mPaint.setARGB(255, 255, 255, 255);   
        mPaint.setStyle(Paint.Style.STROKE);   
        mPaint.setStrokeWidth(2);   
        mTargetPaint = new Paint();   
        mTargetPaint.setAntiAlias(false);   
        mTargetPaint.setARGB(255, 0, 0, 192);   
        mPathPaint = new Paint();   
        mPathPaint.setAntiAlias(false);   
        mPathPaint.setARGB(255, 0, 96, 255);   
        mPaint.setStyle(Paint.Style.STROKE);   
        mPaint.setStrokeWidth(1);   
           
        PointerState ps = new PointerState();   
        ps.mVelocity = VelocityTracker.obtain();   
        mPointers.add(ps);   
    }   
   
    public void setPrintCoords(boolean state) {   
        mPrintCoords = state;   
    }   
       
    @Override   
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {   
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);   
        mTextPaint.getFontMetricsInt(mTextMetrics);   
        mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2;   
        if (false) {   
            Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent   
                    + " descent=" + mTextMetrics.descent   
                    + " leading=" + mTextMetrics.leading   
                    + " top=" + mTextMetrics.top   
                    + " bottom=" + mTextMetrics.bottom);   
        }   
    }   
   
    @Override   
    protected void onDraw(Canvas canvas) {   
        synchronized (mPointers) {   
            final int w = getWidth();   
            final int itemW = w/7;   
            final int base = -mTextMetrics.ascent+1;   
            final int bottom = mHeaderBottom;   
               
            final int NP = mPointers.size();   
               
            if (NP > 0) {   
                final PointerState ps = mPointers.get(0);   
                canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);   
                canvas.drawText("P: " + mCurNumPointers + " / " + mMaxNumPointers,   
                        1, base, mTextPaint);   
                   
                final int N = ps.mXs.size();   
                if ((mCurDown && ps.mCurDown) || N == 0) {   
                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);   
                    canvas.drawText("X: " + ps.mCurX, 1 + itemW, base, mTextPaint);   
                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint);   
                    canvas.drawText("Y: " + ps.mCurY, 1 + itemW * 2, base, mTextPaint);   
                } else {   
                    float dx = ps.mXs.get(N-1) - ps.mXs.get(0);   
                    float dy = ps.mYs.get(N-1) - ps.mYs.get(0);   
                    canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom,   
                            Math.abs(dx) < mVC.getScaledTouchSlop()   
                            ? mTextBackgroundPaint : mTextLevelPaint);   
                    canvas.drawText("dX: " + String.format("%.1f", dx), 1 + itemW, base, mTextPaint);   
                    canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom,   
                            Math.abs(dy) < mVC.getScaledTouchSlop()   
                            ? mTextBackgroundPaint : mTextLevelPaint);   
                    canvas.drawText("dY: " + String.format("%.1f", dy), 1 + itemW * 2, base, mTextPaint);   
                }   
                   
                canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint);   
                int velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getXVelocity() * 1000);   
                canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint);   
                   
                canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint);   
                velocity = ps.mVelocity == null ? 0 : (int) (ps.mVelocity.getYVelocity() * 1000);   
                canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint);   
                   
                canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint);   
                canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCurPressure * itemW) - 1,   
                        bottom, mTextLevelPaint);   
                canvas.drawText("Prs: " + String.format("%.2f", ps.mCurPressure), 1 + itemW * 5,   
                        base, mTextPaint);   
                   
                canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint);   
                canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCurSize * itemW) - 1,   
                        bottom, mTextLevelPaint);   
                canvas.drawText("Size: " + String.format("%.2f", ps.mCurSize), 1 + itemW * 6,   
                        base, mTextPaint);   
            }   
               
            for (int p=0; p<NP; p++) {   
                final PointerState ps = mPointers.get(p);   
                   
                if (mCurDown && ps.mCurDown) {   
                    canvas.drawLine(0, (int)ps.mCurY, getWidth(), (int)ps.mCurY, mTargetPaint);   
                    canvas.drawLine((int)ps.mCurX, 0, (int)ps.mCurX, getHeight(), mTargetPaint);   
                    int pressureLevel = (int)(ps.mCurPressure*255);   
                    mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel);   
                    canvas.drawPoint(ps.mCurX, ps.mCurY, mPaint);   
                    canvas.drawCircle(ps.mCurX, ps.mCurY, ps.mCurWidth, mPaint);   
                }   
            }   
               
            for (int p=0; p<NP; p++) {   
                final PointerState ps = mPointers.get(p);   
                   
                final int N = ps.mXs.size();   
                float lastX=0, lastY=0;   
                boolean haveLast = false;   
                boolean drawn = false;   
                mPaint.setARGB(255, 128, 255, 255);   
                for (int i=0; i<N; i++) {   
                    float x = ps.mXs.get(i);   
                    float y = ps.mYs.get(i);   
                    if (Float.isNaN(x)) {   
                        haveLast = false;   
                        continue;   
                    }   
                    if (haveLast) {   
                        canvas.drawLine(lastX, lastY, x, y, mPathPaint);   
                        canvas.drawPoint(lastX, lastY, mPaint);   
                        drawn = true;   
                    }   
                    lastX = x;   
                    lastY = y;   
                    haveLast = true;   
                }   
                   
                if (drawn) {   
                    if (ps.mVelocity != null) {   
                        mPaint.setARGB(255, 255, 64, 128);   
                        float xVel = ps.mVelocity.getXVelocity() * (1000/60);   
                        float yVel = ps.mVelocity.getYVelocity() * (1000/60);   
                        canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint);   
                    } else {   
                        canvas.drawPoint(lastX, lastY, mPaint);   
                    }   
                }   
            }   
        }   
    }   
   
    public void addTouchEvent(MotionEvent event) {   
        synchronized (mPointers) {   
            int action = event.getAction();   
               
            //Log.i("Pointer", "Motion: action=0x" + Integer.toHexString(action)    
            //        + " pointers=" + event.getPointerCount());    
               
            int NP = mPointers.size();   
               
            //mRect.set(0, 0, getWidth(), mHeaderBottom+1);    
            //invalidate(mRect);    
            //if (mCurDown) {    
            //    mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3,    
            //            mCurX+mCurWidth+3, mCurY+mCurWidth+3);    
            //} else {    
            //    mRect.setEmpty();    
            //}    
            if (action == MotionEvent.ACTION_DOWN) {   
                for (int p=0; p<NP; p++) {   
                    final PointerState ps = mPointers.get(p);   
                    ps.mXs.clear();   
                    ps.mYs.clear();   
                    ps.mVelocity = VelocityTracker.obtain();   
                    ps.mCurDown = false;   
                }   
                mPointers.get(0).mCurDown = true;   
                mMaxNumPointers = 0;   
                if (mPrintCoords) {   
                    Log.i("Pointer", "Pointer 1: DOWN");   
                }   
            }   
               
            if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {   
                final int index = (action&MotionEvent.ACTION_POINTER_INDEX_MASK)   
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;   
                final int id = event.getPointerId(index);   
                while (NP <= id) {   
                    PointerState ps = new PointerState();   
                    ps.mVelocity = VelocityTracker.obtain();   
                    mPointers.add(ps);   
                    NP++;   
                }   
                final PointerState ps = mPointers.get(id);   
                ps.mVelocity = VelocityTracker.obtain();   
                ps.mCurDown = true;   
                if (mPrintCoords) {   
                    Log.i("Pointer", "Pointer " + (id+1) + ": DOWN");   
                }   
            }   
               
            final int NI = event.getPointerCount();   
               
            mCurDown = action != MotionEvent.ACTION_UP   
                    && action != MotionEvent.ACTION_CANCEL;   
            mCurNumPointers = mCurDown ? NI : 0;   
            if (mMaxNumPointers < mCurNumPointers) {   
                mMaxNumPointers = mCurNumPointers;   
            }   
               
            for (int i=0; i<NI; i++) {   
                final int id = event.getPointerId(i);   
                final PointerState ps = mPointers.get(id);   
                ps.mVelocity.addMovement(event);   
                ps.mVelocity.computeCurrentVelocity(1);   
                final int N = event.getHistorySize();   
                for (int j=0; j<N; j++) {   
                    if (mPrintCoords) {   
                        Log.i("Pointer", "Pointer " + (id+1) + ": ("   
                                + event.getHistoricalX(i, j)   
                                + ", " + event.getHistoricalY(i, j) + ")"   
                                + " Prs=" + event.getHistoricalPressure(i, j)   
                                + " Size=" + event.getHistoricalSize(i, j));   
                    }   
                    ps.mXs.add(event.getHistoricalX(i, j));   
                    ps.mYs.add(event.getHistoricalY(i, j));   
                }   
                if (mPrintCoords) {   
                    Log.i("Pointer", "Pointer " + (id+1) + ": ("   
                            + event.getX(i) + ", " + event.getY(i) + ")"   
                            + " Prs=" + event.getPressure(i)   
                            + " Size=" + event.getSize(i));   
                }   
                ps.mXs.add(event.getX(i));   
                ps.mYs.add(event.getY(i));   
                ps.mCurX = (int)event.getX(i);   
                ps.mCurY = (int)event.getY(i);   
                //Log.i("Pointer", "Pointer #" + p + ": (" + ps.mCurX    
                //        + "," + ps.mCurY + ")");    
                ps.mCurPressure = event.getPressure(i);   
                ps.mCurSize = event.getSize(i);   
                ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3));   
            }   
               
            if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {   
                final int index = (action&MotionEvent.ACTION_POINTER_INDEX_MASK)   
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;   
                final int id = event.getPointerId(index);   
                final PointerState ps = mPointers.get(id);   
                ps.mXs.add(Float.NaN);   
                ps.mYs.add(Float.NaN);   
                ps.mCurDown = false;   
                if (mPrintCoords) {   
                    Log.i("Pointer", "Pointer " + (id+1) + ": UP");   
                }   
            }   
               
            if (action == MotionEvent.ACTION_UP) {   
                for (int i=0; i<NI; i++) {   
                    final int id = event.getPointerId(i);   
                    final PointerState ps = mPointers.get(id);   
                    if (ps.mCurDown) {   
                        ps.mCurDown = false;   
                        if (mPrintCoords) {   
                            Log.i("Pointer", "Pointer " + (id+1) + ": UP");   
                        }   
                    }   
                }   
            }   
               
            //if (mCurDown) {    
            //    mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3,    
            //            mCurX+mCurWidth+3, mCurY+mCurWidth+3);    
            //}    
            //invalidate(mRect);    
            postInvalidate();   
        }   
    }   
       
    @Override   
    public boolean onTouchEvent(MotionEvent event) {   
        addTouchEvent(event);   
        return true;   
    }   
   
    @Override   
    public boolean onTrackballEvent(MotionEvent event) {   
        Log.i("Pointer", "Trackball: " + event);   
        return super.onTrackballEvent(event);   
    }   
       
}  


本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2011-10/44305.htm
------------------------------------
参考:
解析Android开发中多点触摸的实现方法-
发布:jingxian 字体:[增加 减小] 类型:转载 


多点触摸(MultiTouch),指的是允许计算机用户同时通过多个手指来控制图形界面的一种技术。与多点触摸技术相对应的就是单点触摸,单点触摸的设备已经有很多年了,小尺寸的有触摸式的手机,大尺寸的最常见的就是银行里的ATM机和排队查询机等等
-
多点触摸技术在实际开发过程中,用的最多的就是放大缩小功能。比如有一些图片浏览器,就可以用多个手指在屏幕上操作,对图片进行放大或者缩小。再比如一些浏览器,也可以通过多点触摸放大或者缩小字体。其实放大缩小也只是多点触摸的实际应用样例之一,有了多点触摸技术,在一定程度上就可以创新出更多的操作方式来,实现更酷的人机交互。


理论上,Android系统本身可以处理多达256个手指的触摸,这主要取决于手机硬件的支持。当然,支持多点触摸的手机,也不会支持这么多点,一般是支持2个点或者4个点。对于开发者来说,编写多点触摸的代码与编写单点触摸的代码,并没有很大的差异。这是因为,Android SDK中的MotionEvent类不仅封装了单点触摸的消息,也封装了多点触摸的消息,对于单点触摸和多点触摸的处理方式几乎是一样的。


在处理单点触摸中,我们一般会用到MotionEvent.ACTION_DOWN、ACTION_UP、ACTION_MOVE,然后可以用一个Switch语句来分别进行处理。ACTION_DOWN和ACTION_UP就是单点触摸屏幕,按下去和放开的操作,ACTION_MOVE就是手指在屏幕上移动的操作。


在处理多点触摸的过程中,我们还需要用到MotionEvent.ACTION_MASK。一般使用switch(event.getAction() & MotionEvent.ACTION_MASK)就可以处理处理多点触摸的ACTION_POINTER_DOWN和ACTION_POINTER_UP事件。代码调用这个“与”操作以后,当第二个手指按下或者放开,就会触发ACTION_POINTER_DOWN或者ACTION_POINTER_UP事件。


下面我们以一个实际的例子来说明如何在代码中实现多点触摸功能。在这里我们载入一个图片,载入图片后,可以通过一个手指对图片进行拖动,也可以通过两个手指的滑动实现图片的放大缩小功能。




复制代码 代码如下:
// import略   
public class ImageViewerActivity extends Activity implements OnTouchListener {  


    private ImageView mImageView;  


    private Matrix matrix = new Matrix();  
    private Matrix savedMatrix = new Matrix();  


    private static final int NONE = 0;  
    private static final int DRAG = 1;  
    private static final int ZOOM = 2;  
    private int mode = NONE;  


    // 第一个按下的手指的点   
    private PointF startPoint = new PointF();  
    // 两个按下的手指的触摸点的中点   
    private PointF midPoint = new PointF();  
    // 初始的两个手指按下的触摸点的距离   
    private float oriDis = 1f;  


    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        this.setContentView(R.layout.imageviewer);  
        mImageView = (ImageView) this.findViewById(R.id.imageView);  
        mImageView.setOnTouchListener(this);  
    }  


    @Override  
    public boolean onTouch(View v, MotionEvent event) {  
        ImageView view = (ImageView) v;  


        // 进行与操作是为了判断多点触摸   
        switch (event.getAction() & MotionEvent.ACTION_MASK) {  
        case MotionEvent.ACTION_DOWN:  
            // 第一个手指按下事件   
            matrix.set(view.getImageMatrix());  
            savedMatrix.set(matrix);  
            startPoint.set(event.getX(), event.getY());  
            mode = DRAG;  
            break;  
        case MotionEvent.ACTION_POINTER_DOWN:  
            // 第二个手指按下事件   
            oriDis = distance(event);  
            if (oriDis > 10f) {  
                savedMatrix.set(matrix);  
                midPoint = middle(event);  
                mode = ZOOM;  
            }  
            break;  
        case MotionEvent.ACTION_UP:  
        case MotionEvent.ACTION_POINTER_UP:  
            // 手指放开事件   
            mode = NONE;  
            break;  
        case MotionEvent.ACTION_MOVE:  
            // 手指滑动事件   
            if (mode == DRAG) {  
                // 是一个手指拖动   
                matrix.set(savedMatrix);  
                matrix.postTranslate(event.getX() - startPoint.x, event.getY()  
                        - startPoint.y);  
            } else if (mode == ZOOM) {  
                // 两个手指滑动   
                float newDist = distance(event);  
                if (newDist > 10f) {  
                    matrix.set(savedMatrix);  
                    float scale = newDist / oriDis;  
                    matrix.postScale(scale, scale, midPoint.x, midPoint.y);  
                }  
            }  
            break;  
        }  


        // 设置ImageView的Matrix   
        view.setImageMatrix(matrix);  
        return true;  
    }  


    // 计算两个触摸点之间的距离   
    private float distance(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 PointF middle(MotionEvent event) {  
        float x = event.getX(0) + event.getX(1);  
        float y = event.getY(0) + event.getY(1);  
        return new PointF(x / 2, y / 2);  
    }  


}  


以下是布局文件。
复制代码 代码如下:
<?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout  
  xmlns:android="http://schemas.android.com/apk/res/android"  
  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/example"  
        android:scaleType="matrix" >  
    </ImageView>  
</RelativeLayout>  




在这段代码中,我们通过手指的操作来计算Matrix的值,然后设置图片的Matrix,实现图片的移动和缩放。


需要注意的是,在资源文件中,需要设置ImageView的scaleType为“matrix”。


经验分享:


一般来讲,手机的屏幕较小,处理2个手指就已经够用,放上3个及以上手指操作,就有点困难了。所以一般设计的过程中,实现2个手指就已经够用了。


很多手机并不支持多点触摸,所以一定要有其它方法实现需要的功能。比如上面的图片缩放的例子,在实际的产品开发中,一定要设计常规的方式实现图片的缩放,比如用按钮,而不能完全依赖多点触摸。




详细出处参考:http://www.jb51.net/article/36617.htm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值