Android MotionEvent 多点触碰
在前面的章节我们学习了基于监听的 TouchLisenter
和基于回调的 onTouchEvent()
,它们都有一个参数 MotionEvent event
,这个参数支持多点触碰
所谓的多点触碰就是多个手指在屏幕上进行操作,常见的就是微信图片上的放大缩小功能
MotionEvent
理论上 Android 系统本身可以处理多达 256 个手指的触摸,当然这取决于手机硬件的支持
支持多点触摸的手机一般支持 2-4 个点,当然有些更多
MotionEvent 代表的是一个触摸事件,可以通过以下方法来判断到底是哪种操作
event.getAction() & MotionEvent.ACTION_MASK
event.getAction()
的返回值常见的有
值 | 说明 |
---|---|
MotionEvent.ACTION_DOWN | 按下事件 |
MotionEvent.ACTION_MOVE | 移动事件 |
MotionEvent.ACTION_UP | 弹起事件 |
MotionEvent.ACTION_POINTER_DOWN | 当屏幕上已经有一个点被按住,此时再按下其它点时触发 |
MotionEvent.ACTION_POINTER_UP | 当屏幕上有多个点被按住,松开其中一个点时触发 ( 即非最后一个点被放开时) |
这些事件的触发顺序是
-
当我们一个手指触摸屏幕 ——> 触发
ACTION_DOWN
事件 -
接着有另一个手指也触摸屏幕 ——> 触发
ACTION_POINTER_DOWN
事件,如果还有其它手指触摸,继续触发 -
有一个手指离开屏幕 ——> 触发
ACTION_POINTER_UP
事件,继续有手指离开,继续触发 -
当最后一个手指离开屏幕 ——> 触发
ACTION_UP
事件 -
而且在整个过程中,
ACTION_MOVE
事件会一直不停地被触发
在这整个过程中,我们可以在调用 MotionEvent 对象的 event.getPointerCount()
方法判断当前有多少个手指在触摸
然后可以通过参数 event.getX(int)
或者 event.getY(int)
来获得不同触摸点的位置
比如 event.getX(0)
可以获得第一个接触点的 X 坐标,event.getX(1)
获得第二个接触点的 X 左边
范例
我们写一个范例来演示多点触碰吧: 单指拖动图片,双指缩放图片
-
创建一个 空的 Android 项目
cn.twle.android.MultiTouch
-
下载 /static/i/meimei_160x360.jpg 并放到
res/drawable
目录,重命名为meimei.jpg
-
修改
activity_main.xml
添加一个ImageView
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" > <ImageView android:id="@+id/meimei" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="matrix" android:src="@drawable/meimei" /> </RelativeLayout>
-
修改
MainActivity.java
添加多点触碰处理机制package cn.twle.android.multitouch; import java.lang.Math; 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 MainActivity extends Activity implements OnTouchListener { private ImageView meimei; // 縮放控制 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); setContentView(R.layout.activity_main); meimei = (ImageView) this.findViewById(R.id.meimei); meimei.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 (float)Math.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); } }