android多点触控的理解

news/2024/11/18 0:41:30/

首先多点触控要使用event.getActionMasked()来获取事件,调用情况如下:

  • case MotionEvent.ACTION_DOWN: //第一根手指按下触发,只会触发一次
  • case MotionEvent.ACTION_MOVE: //所有手指的move事件都会触发这个事件
  • case MotionEvent.ACTION_UP: //只会触发一次,最后一根手指抬起时触发
  • case MotionEvent.ACTION_POINTER_DOWN: //非第一跟手指按下触发
  • case MotionEvent.ACTION_POINTER_UP: //非最后一根手指抬起触发
    接下来看一段代码和效果
public class MultiTouchView extends View {private float offsetX, offsetY;private float lastOffsetX, lastOffsetY;private float downX, downY;Bitmap bitmap;Paint paint;float currentScale;private int currentPointId;。。。省略@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test3);paint = new Paint();if ((float) bitmap.getWidth() / bitmap.getHeight() > (float) getWidth() / getHeight()) {//图片是横向图片currentScale = (float) getWidth() / bitmap.getWidth();} else {currentScale = (float) getHeight() / bitmap.getHeight();}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.translate(offsetX, offsetY);canvas.scale(currentScale, currentScale, getWidth() / 2f, getHeight() / 2f);canvas.drawBitmap(bitmap, (getWidth() - bitmap.getWidth()) / 2f, (getHeight() - bitmap.getHeight()) / 2f, paint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getActionMasked()) { //getAction//只会触发一次case MotionEvent.ACTION_DOWN:downX = event.getX();downY = event.getY();currentPointId=0;break;//所有手指移动都是触发这个事件case MotionEvent.ACTION_MOVE://获取id对应的index值,index是会变化的,id不会变化//int index= event.findPointerIndex(currentPointId);//移动距离:上次偏移值+当前滑动距离offsetX = lastOffsetX + event.getX() - downX;//event.getY() 使用的是pointerIndexoffsetY = lastOffsetY + event.getY() - downY;invalidate();break;//只会触发一次,最后一根手指抬起时触发case MotionEvent.ACTION_UP://抬手记录上次偏移值lastOffsetX = offsetX;lastOffsetY = offsetY;break;//非第一跟手指按下触发case MotionEvent.ACTION_POINTER_DOWN:break;//非最后一根手指抬起触发case MotionEvent.ACTION_POINTER_UP:break;}return true;}
}

自定义了一个view,onDraw绘制了一张图片,在ACTION_MOVE的时候offsetX(Y)达到图片随手指滑动的效果:

我这里的操作是

  1. 先在右上角按下手指1,滑动,图片能跟随手指滑动
  2. 接着在左下角按下手指2并滑动发现手指2没法滑动图片(依旧只会跟随手指1滑动)
  3. 然后松开手指1,这时候图片跳到左下角并能跟随手指2滑动了
  4. 最后再按下手指1,图片跳到了右上角并跟随手指1滑动

为什么第二步按下手指2没法滑动图片呢?来看MotionEvent.ACTION_MOVE中的代码:

offsetX = lastOffsetX + event.getX() - downX

问题就在这个event.getX(),查看MotionEvent 的源码:
在这里插入图片描述
第二个参数pointerIndex用的是默认值0,一番查阅后发现:
每根手指按下后系统都会保存该手指的index和id,移除一根手指后index会重新排序,原id不变。当插入一个手指时会根据id列表进行插入操作,如 下图:
在这里插入图片描述
当依次按下四根手指时四根手指的id和index相同,依次是0123,当移除第二根手指,那么将会重新排序,第三根手指的index变为1,id不变,第四根手指的index变为2,id不变。这时候再按下一个手指它会发现id只有0、2、3,于是生成了id为1的手指,然后重新排序变成跟最开始的状态一样。

回头看之前的操作,依次按下手指1和2时生成了两个:手指1(id=0,index=0),手指2(id=1,index=1)
因此,在上面的操作的第二步中,因为Action_move中event.getX() 始终用的index都是0,所以处理的都是第一根手指,第二根手指自然无法拉动图片;
第三步中,因为移除了第一根手指,进行了重新排序,手指2的index变成了0,所以手指2能拖动图片
第四步,手指1重新按下发现当前只有手指2(id=1),并没有id0,因此创建了一个手指id为0,再重新排序的时候变成了手指1(id=0,index=0),手指2(id=1,index=1),手指1的index为0,因此是手指1能拖动图片。

接下来实践一下,将上面的View改为:最后按下的手指拉动图片
思路:记录最后按下手指的id,根据手指的id获取index,ACTION_MOVE中event.getX/Y()传入index:

 case MotionEvent.ACTION_DOWN:downX = event.getX();downY = event.getY();currentPointId=0;break;//所有手指移动都是触发这个事件
case MotionEvent.ACTION_MOVE://获取id对应的index值,index是会变化的,id不会变化int index= event.findPointerIndex(currentPointId);//根据id获取index//移动距离:上次偏移值+当前滑动距离offsetX = lastOffsetX + event.getX(index) - downX;//event.getY() 使用的是pointerIndexoffsetY = lastOffsetY + event.getY(index) - downY;invalidate();break;//只会触发一次,最后一根手指抬起时触发case MotionEvent.ACTION_UP://抬手记录上次偏移值lastOffsetX = offsetX;lastOffsetY = offsetY;break;//非第一跟手指按下触发
case MotionEvent.ACTION_POINTER_DOWN:int pointerIndex= event.getActionIndex();currentPointId=event.getPointerId(pointerIndex);//解决跳动downX=event.getX(pointerIndex);downY=event.getY(pointerIndex);lastOffsetX = offsetX;lastOffsetY = offsetY;break;

在ACTION_DOWN时记录了当前id:currentPointId=0(因为是第一个手指id必然是0),在ACTION_POINTER_DOWN即非第一根手指按下的时候通过getActionIndex、event.getPointerId(pointerIndex)得到按下的id,最后在ACTION_MOVE事件中通过findPointerIndex(currentPointId)找到当前id的index,这样就保证了index为最后按下的手指index。
到此就实现了最后按下的手指拉动图片的效果,但是引入了新的问题:当拖动图片的那根手指抬起来后会出现闪退。
这是因为event.findPointerIndex(currentPointId)数组越界,因为手指抬起后currentPointId被移除了,通过这个id找index自然会报错。在ACTION_POINTER_UP中进行处理:

            //非最后一根手指抬起触发case MotionEvent.ACTION_POINTER_UP:int upIndex = event.getActionIndex();int pointerUpId = event.getPointerId(upIndex);if (pointerUpId == currentPointId) {//如果抬起的是最后一个手指if (upIndex == event.getPointerCount() - 1) {upIndex = event.getPointerCount() - 2;} else {upIndex++;}currentPointId = event.getPointerId(upIndex);//记录位置,解决跳动downX = event.getX(upIndex);downY = event.getY(upIndex);lastOffsetX = offsetX;lastOffsetY = offsetY;}break;

当手指抬起时获取手指id,如果id是正在控制拖动图片的手指id,那么如果该id是最后一个手指则将index回退一个(将拖动交给上一根手指处理),如果该id不是最后一个,那么index++,即交给下一根手指处理。
最后剩下新手指按下图片跳动的问题是因为手指按下时没有记录当前的downX(Y)和lastOffsetX (Y)。
完整代码---------------------------------------------------------------------------------------->

 import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;import androidx.annotation.Nullable;public class MultiTouchView extends View {private float offsetX, offsetY;private float lastOffsetX, lastOffsetY;private float downX, downY;Bitmap bitmap;Paint paint;float currentScale;private int currentPointId;public MultiTouchView(Context context) {this(context, null);}public MultiTouchView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public MultiTouchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test3);paint = new Paint();if ((float) bitmap.getWidth() / bitmap.getHeight() > (float) getWidth() / getHeight()) {//图片是横向图片currentScale = (float) getWidth() / bitmap.getWidth();} else {currentScale = (float) getHeight() / bitmap.getHeight();}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.translate(offsetX, offsetY);canvas.scale(currentScale, currentScale, getWidth() / 2f, getHeight() / 2f);canvas.drawBitmap(bitmap, (getWidth() - bitmap.getWidth()) / 2f, (getHeight() - bitmap.getHeight()) / 2f, paint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getActionMasked()) { //getAction//只会触发一次case MotionEvent.ACTION_DOWN:downX = event.getX();downY = event.getY();currentPointId = 0;break;//所有手指移动都是触发这个事件case MotionEvent.ACTION_MOVE://获取id对应的index值,index是会变化的,id不会变化int index = event.findPointerIndex(currentPointId);//根据id获取index//移动距离:上次偏移值+当前滑动距离offsetX = lastOffsetX + event.getX(index) - downX;//event.getY() 使用的是pointerIndexoffsetY = lastOffsetY + event.getY(index) - downY;invalidate();break;//只会触发一次,最后一根手指抬起时触发case MotionEvent.ACTION_UP://抬手记录上次偏移值lastOffsetX = offsetX;lastOffsetY = offsetY;break;//非第一跟手指按下触发case MotionEvent.ACTION_POINTER_DOWN:int pointerIndex = event.getActionIndex();currentPointId = event.getPointerId(pointerIndex);//记录位置,解决跳动downX = event.getX(pointerIndex);downY = event.getY(pointerIndex);lastOffsetX = offsetX;lastOffsetY = offsetY;break;//非最后一根手指抬起触发case MotionEvent.ACTION_POINTER_UP:int upIndex = event.getActionIndex();int pointerUpId = event.getPointerId(upIndex);if (pointerUpId == currentPointId) {//如果抬起的是最后一个手指if (upIndex == event.getPointerCount() - 1) {upIndex = event.getPointerCount() - 2;} else {upIndex++;}currentPointId = event.getPointerId(upIndex);//记录位置,解决跳动downX = event.getX(upIndex);downY = event.getY(upIndex);lastOffsetX = offsetX;lastOffsetY = offsetY;}break;}return true;}
}

最终效果:


http://www.ppmy.cn/news/484968.html

相关文章

Android 多点触控

1.多点触控 多点触控 ( Multitouch,也称 Multi-touch ),即同时接受屏幕上多个点的人机交互操作,多点触控是从 Android 2.0 开始引入的功能,在 Android 2.2 时对这一部分进行了重新设计。 多点触控相关问题: 在引入多点…

android多点触控的使用

最近没什么事看了一下多点触控的例子,跟我开始想的实现方法一样,只是一些函数不知道:下面是常用的函数解释(copy过来滴....) event.getAction() //获取触控动作比如ACTION_DOWN event.getPointerCount(); //获取触控点的数量,比如…

单点触控和多点触控

Android 多点触控详解,在前面的几篇文章中我们大致了解了 Android 中的事件处理流程和一些简单的处理方案,本次带大家了解 Android 多点触控相关的一些知识。 多点触控 ( Multitouch,也称 Multi-touch ),即同时接受屏幕上多个点的…

Android多点触控详解

原文地址 Android多点触控详解 内容 Android 多点触控详解,在前面的几篇文章中我们大致了解了 Android 中的事件处理流程和一些简单的处理方案,本次带大家了解 Android 多点触控相关的一些知识。 多点触控 ( Multitouch,也称 Multi-touch…

kafka硬件选择以及如何在生产中优化各个组件的参数

硬件选择: 1.求出一天kafka会产出大概多少的消息,然后平均到每一秒,要多少的消息,然后一条消息大概就是0.5-2k的大小,求出,每秒占用多少内存 2.求购买服务器数量(上边求出的效率 * 副本数/100)1 如果除不…

520探讨一下如何给geek男友选礼物?(超实用)

一眨眼520就到了,又到情侣们互赠礼物秀恩爱的时刻了。女孩们总是嫌弃钢铁直男的蜜汁审美,送出的礼物总能让自己吐槽整整十分钟。事实上,如何给自己的geek男友挑到让他眼前一亮、赞不绝口的礼物,也是个让人头疼的问题。 下面&#…

互联网日报 | 1月10日 星期日 | 小米之家千店同开;蔚来发布首款旗舰轿车ET7;LVMH完成收购Tiffany...

今日看点 ✦ 国家卫健委:公民接种新冠疫苗个人不负担费用,已累计接种900多万剂次 ✦ 360安全中心紧急发布:警惕“新冠疫苗免费接种”虚假信息 ✦ 小米之家首次“千店同开”,刷新行业单日开店新纪录 ✦ 蔚来发布首款电动轿车ET7&am…

数据库小技能:SQL Server 基础

文章目录 引言I SQL Server1.1 布尔类型Bit1.2 多列转行,逗号分隔(字段拼接/字段分割)引言 I SQL Server 1.1 布尔类型Bit SQL Server中,没有布尔类型True和False。 其实还有一个不错的选择,是Bit类型。 alter table MarketActivityadd IsNeedAduit bit default 0 not nu…