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: #008000;" >   /** Called when the activity is first created. */ </span> <span style= "color: #008000;" >   @Override </span>    public void onCreate(Bundle savedInstanceState) {      super .onCreate(savedInstanceState);     setContentView(R.layout.main);   }
?
<span style= "color: #008000;" >  @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>  

更多相关文章

  1. [android]实现拖动效果
  2. Android用户界面 UI组件--自动提示输入框 AutoCompleteTextView
  3. Android实现弹出Toast提示
  4. android提示:Gradle DSL method not found: 'android()'
  5. AutoCompleteTextView
  6. android图片缩放平移
  7. Android输入框自动提示
  8. 2.6.1 使用toast显示提示信息框
  9. Android中Toast的自定义

随机推荐

  1. android中的数据库操作
  2. SDK下载地址
  3. Android(安卓)异步从网络上下载图片
  4. Android(安卓)SDK更新失败及其解决办法
  5. Android(安卓)创建与解析XML(六)—— 比较
  6. android页面跳转
  7. android studio 修改生成的apk 包名
  8. 深入解读Linux与Android的相互关系& Andr
  9. [android] android framework中的 PhoneN
  10. Android解析Xml字符串例子