参考文章:
理解Android的手势识别
Android实战之手势与多点触控探究
Android开发中实现多点触摸
Android学习指南之三十八:Android手势操作编程
Android GestureDetector手势识别类
Android 自定义View可拖动移动位置及边缘拉伸放大缩小
一、最原始的单点拖拽和两点缩放
原理:对于常规的控件触控操作,在setOnTouchListener()接口中,实现 onTouchEvent()方法来处理。
代码清单:

 package com.example.multitouch;  import android.os.Bundle;  import android.annotation.SuppressLint;  import android.annotation.TargetApi;  import android.app.Activity;  import android.graphics.Matrix;  import android.graphics.PointF;  import android.view.GestureDetector;  import android.view.Menu;  import android.view.MotionEvent;  import android.view.View;  import android.view.View.OnTouchListener;  import android.widget.ImageView;  import android.widget.Toast;  import android.view.GestureDetector.OnGestureListener;   public class MainActivity extends Activity implements OnTouchListener{      public ImageView myImageView;      private static final int NONE = 0;        private static final int DRAG = 1;        private static final int ZOOM = 2;        private int mode = NONE;        private Matrix tmpMatrix=new Matrix();;      private Matrix savedMatrix = new Matrix();        private PointF startPoint = new PointF();        private PointF endPoint=new PointF();      private PointF midPoint = new PointF();        private float oldDistance;        @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          myImageView=(ImageView)findViewById(R.id.myImageView);          myImageView.setOnTouchListener(this);      }      @Override      public boolean onCreateOptionsMenu(Menu menu) {          getMenuInflater().inflate(R.menu.activity_main, menu);          return true;      }      @Override      public boolean onTouch(View v, MotionEvent event) {          //获取触控的点数           int pointCount = event.getPointerCount();           switch(event.getAction() & MotionEvent.ACTION_MASK){            //单手指按下            case MotionEvent.ACTION_DOWN:                //将当前的坐标保存为起始点                 startPoint.set(event.getX(), event.getY());                tmpMatrix.set(myImageView.getImageMatrix());               savedMatrix.set(tmpMatrix);                mode = DRAG;               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(tmpMatrix);                   midPoint.set((event.getX(0) + event.getX(1))/2, (event.getY(0) + event.getY(1))/2);                  mode = ZOOM;               }               break;               //指点杆保持按下,并且进行位移            case MotionEvent.ACTION_MOVE:               //拖拽模式               if (mode == DRAG) {                   tmpMatrix.set(savedMatrix);                   tmpMatrix.postTranslate(event.getX() - startPoint.x, event.getY()                           - startPoint.y);               }               //缩放模式               else if (mode == ZOOM)               {                   float newDist =  (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 (newDist > 10f)                  {                       tmpMatrix.set(savedMatrix);                       float scale = newDist / oldDistance;                       tmpMatrix.postScale(scale, scale, midPoint.x, midPoint.y);                   }               }               break;            //有手指抬起,将模式设为NONE           case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_POINTER_UP:                mode = NONE;                break;            default:          }                    myImageView.setImageMatrix(tmpMatrix);           return true;        }  } 

代码解释:MainActivity实现OnTouchLietener的接口,将ImageView的触控 监听器设置为this,在重载函数OnTouch中实现对触控事件的处理。

这里的图像的位置和大小的变化都用到了矩阵运算,不太清楚的话可以先补充一下线性代数的知识。

拖拽的实现就是用矩阵记录手指移动的距离;缩放的时候,首先要记录两只手指最开始的距离,然后当手指移动的时候,实时计算出手指的距离,与之前的距离相除得到缩放的比例,然后用矩阵的scale方法存储。

二、手势识别
上面的例子虽然实现了基本的触控功能,而且低版本的系统也能很好的支持,但如果遇到了高级的触控事件,比如双击,长按之类,实现起来就非常麻烦了!
好在后续版本的api提供了更加棒的接口,我们可以很简单地来实现想要的效果。
这里要用到的是Android给我们提供的手势识别工具GestureDetector,需要2.2及以上的系统版本。下面的例子实现的效果是:单点拖拽,滑动切换imageView的内容,两点缩放,双击图像改变图像显示状态。

函数的最后调用 setImageMatrix()来实现对TextView的缩放或移动。

package com.example.gesture;import java.util.Random;  import android.os.Bundle;  import android.app.Activity;  import android.graphics.Matrix;  import android.graphics.PointF;  import android.view.GestureDetector;  import android.view.GestureDetector.SimpleOnGestureListener;  import android.view.Menu;  import android.view.MotionEvent;  import android.view.ScaleGestureDetector;  import android.view.ScaleGestureDetector.OnScaleGestureListener;  import android.view.View;  import android.widget.ImageView;  import android.widget.Toast;  public class MainActivity extends Activity {      private GestureDetector myDetector;      private Matrix matrix;       private ImageView myImageView;      private Random random;      private ScaleGestureDetector mScaleGestureDetector;      @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          myDetector=new GestureDetector(this,new MyGestureListener());           mScaleGestureDetector=new ScaleGestureDetector(this,new MyScaleGestureListener());          matrix=new Matrix();           myImageView=(ImageView)findViewById(R.id.myImageView);          random=new Random();      }      @Override        public boolean onTouchEvent(MotionEvent event) {           int pointCount = event.getPointerCount();           if(pointCount==1)              return myDetector.onTouchEvent(event);            else               return mScaleGestureDetector.onTouchEvent(event);      }       private class MyGestureListener extends SimpleOnGestureListener      {          Matrix mMatrix=new Matrix();            PointF startPoint=new PointF();          @Override            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,                    float distanceY) {                // TODO Auto-generated method stub                mMatrix.set(myImageView.getImageMatrix());              System.out.println("distanceX:"+distanceX+"distanceY:"+distanceY);                startPoint.set(e1.getRawX(), e1.getRawY());              mMatrix.postTranslate(-distanceX,-distanceY);               myImageView.setImageMatrix(mMatrix);              return false;            }            @Override            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)          {              final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200;                if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) {                    // Fling left                     myImageView.setImageResource(R.drawable.pic0);                                Toast.makeText(getApplicationContext(), "Fling Left", Toast.LENGTH_SHORT).show();                } else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) {                    // Fling right                     switch(random.nextInt(5))                  {                  case 0:                      myImageView.setImageResource(R.drawable.pic2);                      break;                  case 1:                      myImageView.setImageResource(R.drawable.pic3);                      break;                  case 2:                      myImageView.setImageResource(R.drawable.pic7);                      break;                  case 3:                      myImageView.setImageResource(R.drawable.pic5);                      break;                  case 4:                      myImageView.setImageResource(R.drawable.pic6);                      break;                  default:                  }                  Toast.makeText(getApplicationContext(), "Fling Right", Toast.LENGTH_SHORT).show();                }                return false;          }            // 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发             public boolean onDown(MotionEvent arg0) {                Toast.makeText(getApplicationContext(), "onDown", Toast.LENGTH_SHORT).show();               return true;            }            @Override          public boolean onDoubleTap(MotionEvent e)          {              if(myImageView.isShown())                  myImageView.setVisibility(View.INVISIBLE);              else myImageView.setVisibility(View.VISIBLE);              return false;                 }      }      private class MyScaleGestureListener implements OnScaleGestureListener      {          private float oldDist;          private float newDist;          Matrix mMatrix = new Matrix();            @Override          public boolean onScale(ScaleGestureDetector detector) {              // TODO Auto-generated method stub               newDist=detector.getCurrentSpan();              mMatrix.set(myImageView.getImageMatrix());               //缩放比例               //float scale = detector.getScaleFactor()/3;               float scale=newDist/oldDist;              System.out.println("scale:"+scale);              //mMatrix.setScale(scale, scale,detector.getFocusX(),detector.getFocusY());               mMatrix.postScale(scale, scale,detector.getFocusX(),detector.getFocusY());              myImageView.setImageMatrix(mMatrix);               oldDist=newDist;              return false;          }          @Override          public boolean onScaleBegin(ScaleGestureDetector detector) {              // TODO Auto-generated method stub               oldDist=detector.getCurrentSpan();              newDist=detector.getCurrentSpan();              return true;          }          @Override          public void onScaleEnd(ScaleGestureDetector detector) {              // TODO Auto-generated method stub           }      }      @Override      public boolean onCreateOptionsMenu(Menu menu) {          getMenuInflater().inflate(R.menu.activity_main, menu);          return true;      }  } 

代码解释:

这里我定义了两个GestrueListener,一个专门用于处理缩放的ScaleOnGestrueListener一个SimpleOnGestrueListener,当触控的点数为2的时候调用前者来处理,一般常用的手势用后者来处理。

原理和前面的差不多,只是调用不同的接口和不同的方法来实现,但是更加方便也更加清晰.

三、一点后记

学习Andorid中的某个类的时候,其实最好的方法是去看官方的API,有时候网上虽然有现成的代码给你,但实际运用的时候还是会有各种各样的问题,很多文章大都有雷同,甚至代码本身就有bug还往上粘,唉.....所以,最好还是自己踏踏实实研究。

四、陷阱

对于自定义View,使用手势识别有两处陷阱可能会浪费你的不少时间。

1:View必须设置longClickable为true,否则手势识别无法正确工作,只会返回Down, Show, Long三种手势
2:必须在View的onTouchListener中调用手势识别,而不能像Activity一样重载onTouchEvent,否则同样手势识别无法正确工作

更多相关文章

  1. CAMERA(12)---[Android相机]光线传感器识别环境光亮强度
  2. Android(安卓)imageView图片按比例缩放
  3. Android(安卓)Webview适配屏幕宽度
  4. Android(安卓): SeekBar 实现图片旋转缩放
  5. Android手势滑动(左滑和右滑)
  6. Android平台开发-Android(安卓)keypad map-Android按键识别及映
  7. Android(安卓)Makefile中是 如何识别 TARGET_PRODUCT 的
  8. Android(安卓)Makefile中是 如何识别 TARGET_PRODUCT 的
  9. Android之Gridview

随机推荐

  1. XML—XML解析之DOM4J
  2. XML标记的语义
  3. xml学习(4) 创建xml 文件
  4. XML—DOM解析案例
  5. XML的解析
  6. xml学习(3) html显示xml
  7. XML包导入和处理XML数据格式|R包
  8. XML—XML解析之SAX
  9. xml学习(2)xml文档树结构图
  10. R语言XML格式数据导入与处理 - ShangFR