Android触摸事件(一)-AbsTouchEventHandle
目录
概述
这是一个 触摸 事件统一处理辅助类;处理的主要是点击事件,其中包括了:
单点触摸事件 多点(两点)触摸事件此类可以处理的事件包括:
单击事件(基于时间与距离的两种单击事件,详见下文) 双击事件 单点触摸移动事件(可用于实现界面拖动) 多点触摸移动事件(可用于实现界面缩放) 所有触摸事件的相应回调(down
/move
/up
事件)
关于单点触摸事件(singleTouch
)
单点触摸事件很好理解,触发的流程一般是:
mouse_down
->mouse_move
->mouse_up
这个过程可能发生的事件有以下几种:
单击事件 单点移动事件 双击事件但是必须注意的点是:
单击事件可能触发
mouse_move
事件,而单点移动必定触发mouse_move
事件
在单击事件中,mouse_move
事件并不是百分百会触发的,触摸的时候先触发的是mouse_down
事件,如果触摸的时间足够长(按住不动时),接下来会触发mouse_move
事件,之后抬起时会触发mouse_up
事件
虽然触发了mouse_move
事件(按住不动),但是这依然是一个单击事件,如果进行调试或者输出移动的距离,可以明显得到距离为 0
单击的两种方式
基于时间的单点触摸事件(
singleTouchByTime
)
以时间来计算单击事件时,这个过程可以不必过多地考虑单击可能触发的mouse_move
事件,因为单击本身就是一个时间足够短的操作,即便存在一定小范围的移动偏差也是允许的,当然这种情况是在 时间足够短
的情况下
我们可以这么处理:
//定义全局变量用于存放按下时的时间点
long downTime=0;
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//触摸时记录当前时间
downTime=System.currentTimeMillis();
break;
case MotionEvent.ACTION_UP:
//抬起时计算与按下时时间的差
long tillTime=System.currentTimeMillis()-downTime;
//时间差在允许范围内时,视为一次单击事件成立
if(tillTime<250){
//处理单击事件
}
//否则不视为一次单击事件
break;
}
通过计算按下时与抬起时的时间差来确定是否是一次单击事件(250ms足够了),这是基于时间的单击事件;
基于距离的单点触摸事件(
singleTouchByDistance
)
从上面我们知道单点触摸时也是可能触发mouse_move
事件的,所以mouse_move
事件并不能作为一个是否单点移动的标识,而且我们已经知道了单击也可以是按住某个位置不动,持续一段时间之后再抬起,此时可能时间上已经达到一个足够长的时间,但其实 点击地方的坐标并没有改变 ,这种情况下我将其也视为单击的一种情况(总会在某些情况下需要处理这种单击方式)
参考 基于时间的单击方式 的处理方法,我们可以得到类似的处理方法:
float downX=0;
float downY=0;
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//触摸时记录当前触摸点的坐标
downX=event.getX();
downY=event.getY();
break;
case MotionEvent.ACTION_UP:
//抬起时计算与按下时坐标的偏移距离
float offsetX=Math.abs(event.getX()-downX);
float offsetY=Math.abs(event.getY()-downY);
//偏移量差在允许范围内时,视为一次单击事件成立
if(offsetX<20 && offsetY<20){
//处理单击事件
}
//否则不视为一次单击事件
break;
}
以上为两种单击方式的处理方式
关于双击事件
双击事件的检测逻辑
由于单击事件存在两种不同情况,所以双击同理衍生出两种方式, 基于时间和基于距离两种双击事件
不管是哪种方式,原理都是一样的,基于对应的单击方式实现第一次单击,两次单击事件就构成了一次双击事件;
同时这里存在一个问题是,双击不管从哪个角度来说,都是指两次时间间隔短暂的单击事件,所以不管是基于时间还是基于距离的双击事件,都是以两次单击时间之前的间隔时间不超过某个范围来确定一次双击事件的.
必须指出的是,基于距离的双击事件其实也是可以不按时间来处理的,只要两次单击事件的距离在一定的偏移值范围内,可认为是一次双击事件(与时间无关);
但此方式存在的问题是,如果是两次连接发生在同一个位置的单击事件,此时就无法正确的区分出到底是一次双击事件还是两次单击事件了.所以并不推荐使用此方式处理,而是按两次单击事件间隔在一定时间差内视为一次双击事件
由上可以看出,其实这里的双击事件就不是处理两种方式了,仅仅是 基于时间的双击事件
,只是构成该双击事件的单击事件可能是 基于时间的或者是基于距离的
单击事件
//用于记录是否已经完成一次单击事件
boolean isSingleClick=false;
switch(event.getAction()){
//忽略ACTION_DOWN逻辑
case MotionEvent.ACTION_UP:
//达成一次单击事件操作时,视为一次单击事件成立
if(singleClickFinish){
//判断是否已经完成了一次单击事件(在允许的双击间隔时间内)
if(isSingleClick){
//若已完成了一次单击事件,此次单击构成了双击事件
//处理双击事件
}else{
//仅为一次单击事件
//处理单击事件
//记录已经完成了一次单击事件
isSingleClick=true;
}
}
//否则不视为一次单击事件
break;
}
同时,这里有一个需要注意的地方是,双击事件本质是两次单击事件构成的,第一次单击事件发生时我们无法确定是否是一个正常的单击事件还是可能会构成一次双击事件,所以必须按正常单击事件响应;
但第二次单击事件发生时,我们已经可以确定构成了一次双击事件,此时不应该再响应单击事件,而应该响应双击事件并结束触摸事件;
实际的处理事件并没有这么简单,以上是简单的处理逻辑,具体的实现请参照下文 双击事件的优化处理
双击事件触发的时机
双击事件触发的时机是比较重要的.因为双击事件是由单击事件触发的.必然先检测单击事件之后再检测双击事件;
但一旦单击事件被触发了,那么接下来需要做的操作有两个选择:
检测双击事件(之后执行双击事件) 执行单击事件
这两个事件的优先性是必须确定的而且会造成不同的影响.
如果先执行单击事件
,则可能会造成在后续双击事件成立的之前,单击事件会被执行一次.这并不合理,也可能存在一些不安全的因素(如果单击操作会影响到双击操作的情况下)
因此应
检测双击事件
,一旦双击事件成立,直接执行双击事件,同时忽略单击事件;(用户触发了双击事件本身包含了不需要执行单击事件的想法,否则直接触发单击事件即可)
这也是为什么事件触发规则会双击事件优先;
关于多点触摸事件(multiTouch
)
多点触摸事件相对比较复杂,此处只讨论 两点触摸
.
多点触摸事件的需要通过额外的方式进行检测并处理事件,无法与单点触摸事件一样直接event.getAction()
得到的就是相关的触摸事件;
//分离触摸事件,使用 MotionEvent.ACTION_MASK
//此方式可以正确分离出多点触摸事件 ACTION_POINTER_X,也可以正常返回单点触摸事件 ACTION_X
switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_X:
//单点触摸事件处理
break;
case MotionEvent.ACTION_POINTER_X:
//多点触摸事件处理
brea;
}
两点触摸中的移动事件
首先,必须注意的一个点是:
多点触摸事件中移动时触发的移动事件也是
ACTION_MOVE
也就是说ACTION_MOVE
事件是移动的通用事件,在单点触摸移动和多点触摸移动中都存在.
两点触摸事件的触发过程
除以上提及的共用ACTION_MOVE
事件之外,多点触摸事件可能存在的过程是这样的:
ACTION_DOWN
->