这是一个 触摸 事件统一处理辅助类;处理的主要是点击事件,当中包含了:java
此类可以处理的事件包含:git
down
/move
/up
事件)此类实现View.onTouchListener
,经过此监听方法实现对触摸事件的操做.在Activity中也可以使用,调用时onTouch(View,MotionEvent)
传递的參数中将view设置为null就能够.
整个触摸事件处理中并不会涉及不论什么跟view有关的操做,仅分析及处理MotionEventgithub
修正部分逻辑,加入了双击是否可用的设置.改动了文章中一些相应的部分及说明.安全
将AbsTouchEventHandle
抽象类改动为TouchEventHelper
的辅助类,将抽象方法抽出到接口OnToucheEventListener
中,经过为helper设置相应的事件处理接口就能够直接处理事件.
改动为helper的辅助类以后,就需要经过调用其方法来处理触摸事件.TouchEventHelper
实现了View.onTouchListnener
接口,因此对事件的处理需要经过下面的方式调用.markdown
//參数为事件处理接口 onTouchEventListener
TouchEventHelper helper=new TouchEventHelper(this);
//直接在需要处理触摸事件的地方调用onTouch方法,如:
//onTouchEvent(MotionEvent event),在view中
//dispatchTouchEvent(MotionEvent event),在view,activity或者viewGroup中
helper.onTouch(null,event);
//或者直接使用view.setOnTouchListener(helper)
//activity中没法使用setOnTouchListener(),因此仅仅能在相应的方法中进行调用
singleTouch
)单点触摸事件很是好理解,触发的流程一般是: ide
mouse_down
->mouse_move
->mouse_up
post
这个过程可能发生的事件有下面几种:优化
但是必须注意的点是:this
单击事件可能触发
mouse_move
事件,而单点移动必然触发mouse_move
事件spa
在单击事件中,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<150){
//处理单击事件
}
//不然不视为一次单击事件
break;
}
经过计算按下时与抬起时的时间差来肯定是不是一次单击事件(150ms足够了),这是基于时间的单击事件;
singleTouchByDistance
)从上面咱们知道单点触摸时也是可能触发mouse_move
事件的,因此
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` -> `ACTION_POINTER_DOWN` -> `ACTION_MOVE` -> `ACTION_POINTER_UP` -> `ACTION_UP`
这样的状况是在两点触摸时,两个手指恰好 同一时候按上 -> 移动 -> 同一时候抬起
很是明显,既然存在同一时候触摸,也确定存在非同一时候触摸了.当非同一时候触摸时的过程是这样的:
`ACTION_DOWN` -> `ACTION_MOVE` -> `ACTION_POINTER_DOWN` -> `ACTION_MOVE` -> `ACTION_POINTER_UP` -> `ACTION_MOVE` -> `ACTION_UP`
这样的状况是先单点触摸,触发了ACTION_DOWN
事件,而后第二个触摸点按下时,触发ACTION_POINTER_DOWN
,而后当触摸点抬起时,触发ACTION_POINTER_UP
(多个触摸点的状况下会屡次触发ACTOIN_PONTER_DOWN
与ACTION_POINTER_UP
),以后单点触摸抬起,触发ACTION_UP
;
至于第一个ACTION_MOVE
事件是否会触发取决于第一个触摸点与第二个触摸点之间的时间间距(假设第二次按下的时间与第一次按下时间间隔足够短,则不会触发);
同理第二个ACTION_MOVE
取决于多点触摸的按下与抬起的时间差,类似于单击,多点触摸按住时,ACTION_MOVE
依旧是正常触发,但距离值仍是 0.
而第三个ACTION_MOVE
是所有多点触摸抬起后(仅仅剩下单点触摸时),若还保持单点触摸(不管有没有移动)就会触发第三轮的ACTION_MOVE
事件
以上过程可以明白获得:
不管是同一时候多点触摸仍是间接多点触摸,ACTION_DOWN
与ACTION_UP
两个事件是必然会触发并永远在第一项和最后一项事件
因此,在处理多点触摸的事件时,必须当心处理ACTION_MOVE
与ACTION_UP
事件,因为这两个事件并不是单点触摸
专属的事件,而是所有的触摸事件都会触发的
在两点触摸时,通常咱们不考虑两点”单击”(ACTION_POINTER_DOWN
)事件,主要是针对两点触摸时触发的移动事件进行处理;每每这样的状况需要处理的是类似放大/缩小的功能
对于两点触摸事件,这个很是好推断;当ACTION_POINTER_DOWN
触发时,说明触发了两点触摸事件;当ACTION_POINTER_UP
触发时,说明两点触摸事件结束; 两点触摸事件主要是基于这两个事件之间,难点在于:
怎样区分单点触摸的移动事件和两点触摸的移动事件
依据以上咱们肯定两点触摸时,会触发ACTION_PONTER_DOWN
事件,以后才会触发两点触摸事件的ACTION_MOVE
,所以可以经过此事件肯定当前的ACTION_MOVE
事件是否属于两点触摸的仍是单点触摸的事件
boolean isMultiDown=false;
switch(even.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_POINTER_DOWN:
//记录多点触摸事件触发
isMultiDown=true;
break;
case MotionEvent.ACTION_MOVE:
//检測是否已经触发了多点触摸事件
if(isMultiDown){
//多点触摸移动事件
}else{
//单点触摸移动事件
}
break;
}
第二个可能的难点在于:
怎样在
ACTION_UP
事件中区分并处理多点触摸事件及单点触摸事件
通常来讲,处理多点触摸事件时仅仅关注多点触摸事件;处理单点触摸事件时仅仅关注单点触摸事件;而二者都存在的ACTION_UP
事件而且都在最后,就可能形成一个没必要要的麻烦:
可能在多点触摸事件结束后,触发的ACTION_UP
事件处理了一次单点触摸事件
而这可能会致使某些咱们不想要的状况发生.因此关于ACTION_UP
事件,咱们需要当心处理.固然,两点触摸的抬起事件是很是明白的ACTION_POINTER_UP
,假设仅仅关心两点触摸事件时,就全然不需要再考虑ACTION_UP
事件了.(只是在此辅助类中需要在up事件中回调单击事件,因此这部分的处理仍是需要的.)
依据以上的说明,大体的一个触摸事件流程和需要响应的事件也已经肯定下来了.下面是整个触摸事件及流程的一个实现思路,包含:
因为触摸事件各类状况相对复杂,先肯定需要处理的事件包含例如如下事件:
下面事件为触摸事件,说起触摸事件特指下面五种系统反馈的触摸事件,单击/双击等非系统提早定义事件称为本身定义事件
- ACTION_DOWN
- ACTION_POINTER_DOWN
- ACTION_POINTER_UP
- ACTION_UP
- ACTION_MOVE
需要的变量包含:
//是否单点触摸按下,用于单击事件的检測
boolean isSingleDown;
//是否多点触摸按下,用于区分处理事件
boolean isMultDown;
//多点触摸按下的次数,相应多点触摸的个数
//虽然咱们仅仅处理两点触摸,但实际可能多达N点触摸
int multiTouchCount;
//是否进行了移动,不区分多点移动仍是单点移动
boolean isInMotionMove;
//是否进行了单点触摸移动(优化处理单点触摸与多点触摸的切换)
boolean isSingleMove;
//是否完毕一次单击事件
boolean isSingleClikEvent;
以上为基本的需要变量,用于记录各类不一样的状态以区分多点触摸与单点触摸及之间的单击/双击/移动事件
首先需要知道的一个事情是:
关于单击/双击事件中时间间隔的计时咱们经过Handler
来处理
因为Hanlder
可以发送Delay
的消息,咱们可以经过指定发送延时的消息交给Handler
去取消事件或者消费事件;例如如下样例:
Hanlder mHandler=new Handler{
@Override
public void handleMessage(Message msg){
if(msg.what==CANCLE_EVENT){
//处理相应的消息取消操做
}
}
}
//250ms后发送取消事件消息
mHandler.sendEmptyMessageDelayed(CANCLE_EVENT,250);
经过此方法,咱们可以在ACTION_DOWN
事件触发后设置一个按下的标识,而后发送一个延迟的取消按下事件消息,在ACTION_UP
中直接检測按下事件标识是否有效,有效则达成一次单击事件,无效则说明已经超时,单击事件没法触摸;
必须注意:不管
ACTION_UP
事件中按下标识是否有效,已经发生了ACTION_UP
必然发生过ACTION_DOWN
,按下识别是做为单击事件的检測标识而不是ACTION_DOWN
的触发标识
对于以上说起的不一样的触摸事件中,不一样的事件可能会触发不一样的本身定义事件
ACTION_DOWN
/ACTION_UP
事件触发ACTION_DOWN
/ACTION_MOVE
/ACTION_UP
事件触发ACTION_DOWN
/ACTION_UP
事件中被触发
- 不论何时触发双击事件再也不响应其余事件(单击或者
UP
等其余事件)- 不论何时触发多点触摸事件则再也不响应单点触摸的
MOVE
事件
对于每一个触摸事件,除非被其余事件消费或者拦截(如双击事件会拦截其余兴许事件),不然都会进行一次回调提供给子类进行处理,当中
ACTION_DOWN
/ACTION_UP
回调事件:onSingleTouchEventHandle(MotionEvent,int)
ACTION_POINTER_DOWN
/ACTION_POINTER_UP
回调事件:onMultiTouchEventHandle(MotionEvent,int)
ACTION_MOVE
比較特殊,存在两个回调可能
统一回调的方法分为单点触摸事件回调,多点触摸事件回调;回调的时机是每一个相应的MotionEvent
触发时,在处理所有事件(单击双击等)以后都会回调相应的事件以通知子类本身定义处理.
onSingleTouchEventHandle(MotionEvent, int)
//单点触摸事件回调onMultiTouchEventHandle(MotionEvent, int)
//多点触摸事件回调各事件的回调相应例如如下:
//省略參数
switch(event.getAction() & MotionEvent.MASK){
case MotionEvent.ACTION_DOWN:
onSingleTouchEventHandle();
break;
case MotionEvent.ACTION_POINTER_DOWN:
onMultiTouchEventHandle();
break;
case MotionEvent.ACTION_UP:
onSingleTouchEventHandle();
break;
case MotionEvent.ACTION_POINTER_UP:
onMultiTouchEventHandle();
break;
case MotionEvent.ACTION_MOVE:
//move事件是共用的,因此需要区分回调事件的类型
onSingleTouchEventHandle() || onMultiTouchEventHandle();
break;
}
參数意义:
触摸事件
建议处理的触摸事件类型
參数1很是好理解,仅仅是传送了系统分发的触摸事件变量而已,包含了触摸点的坐标,触摸状态等;
參数2是一个比較关键的參数;其存在是的意义是,建议以某个事件去处理当前的事件而不是直接按触发的事件处理当前的事件.其使用的场景例如如下
因为不论何时触发了多点触摸事件则再也不处理单点触摸事件的MOVE事件(触发规则)
因此当多点触摸事件ACTION_POINTER_DOWN
发生以后,所有的ACTION_MOVE
转为多点触摸的移动事件;
所以假设以前存在单点触摸的ACTION_MOVE
事件时,将结束该事件回调onSingleTouchEventHandle()
并再也不处理;
所以,此时回调该事件时,将通知子类处理事件时建议处理为ACTION_UP
事件(因为从这个时候開始整个单点触摸事件已经结束了,以后也不会再响应不论什么单点触摸事件),视为一次单点触摸事件的结束
这里回调时仅仅是建议而不会改动MotionEvent
的事件參数,子类可以选择忽略.
另外建议处理为ACTION_UP
的缘由是,单点触摸事件结束的标志是ACTION_UP
,很是可能子类需要在这个时候处理某些数据或者保存工做.
因为切换为多点触摸以后再也不响应单点触摸事件,而终于事件结束时的ACTION_UP
事件中的參数也很是可能与此时的參数不一致(最基本的就是触摸点的坐标了),所以此回建议处理为ACTION_UP
事件.
举例: 如进行一次单点移动操做时,再按下一个触摸点就变成了两点触摸事件.此时所有的事件将转变成两点触摸事件,所以单点触摸事件就结束了,回调action_up(建议的操做)通知相关的保存工做. 若不这样处理,两点触摸中不论什么的事件均可能影响到界面的变化,而且移动事件也变得不清晰了,因此在整个触摸事件结束时才回调action_up的话,很是有可能会让单击操做的很是多參数出现故障.
下面为ACTION_MOVE
事件中区分单点触摸及多点触摸事件的操做.
case ACTION_MOVE:
//若已经触发了多点触摸事件且保持在多点触摸状态
//当 multiTouchCount=0 时说明已经退了多点触摸状态,恢复到单点触摸状态
//但以后依旧不会响应单点触摸的 MOVE 事件
if (mIsMultiDown && mMultiTouchCount > 0) {
//若此前是单点触摸的移动状态时
if (mIsSingleMove) {
//按单点触摸的结束状态处理并再也不响应单点触摸移动状态
showMsg("单击 move 结束");
//结束单点触摸事件,并建议处理为 UP 事件
this.onSingleTouchEventHandle(event, MotionEvent.ACTION_UP);
mIsSingleMove = false;
}
//正常直接多点移动操做
showMsg("多点触控 move");
this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING);
}
break;
此处的实际应用场景在于:当界面被拖动移动时(依赖于ACTION_MOVE
事件),切换到多点触摸状态时可以保证界面的正常(此时依赖于ACTION_UP
事件保存移动后的位置)而不会在触摸事件结束时再保存移动后位置,可能会发生忽然转到移动到某个位置的状况
下面为触摸事件处理的完整流程(还有其余的处理逻辑,但属于辅助性的逻辑)
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//进入单点单击处理
showMsg("单点触摸 down ");
mIsSingleDown = true;
//发送延迟取消按下标识消息
mHandle.sendEmptyMessageDelayed(HANDLE_SINGLE_DOWN, SINGLE_CLICK_INTERVAL);
//记录按下坐标
mDownX = event.getX();
mDownY = event.getY();
this.onSingleTouchEventHandle(event, MOTION_EVENT_NOTHING);
break;
case MotionEvent.ACTION_POINTER_DOWN:
//開始多点单击事件
showMsg("多点触控 down");
mIsMultiDown = true;
//每发生一次多点触摸此事件会触发一次
//经过此事件可以记录多点触摸的次数及推断是否已经退出多点触摸状态(当变量为0时)
mMultiTouchCount += 1;
this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING);
break;
case MotionEvent.ACTION_UP:
showMsg("单点触摸 up");
//不论什么一种事件中,仅仅要触发了双击事件,则结束事件
//TODO: 双击事件检測并处理,触发 break;不然运行单点触摸抬起事件
//在处理单击事件up中,不论何时仅仅要在结束up以前产生不论什么的多点触控,都不将这次的事件处理为单点触摸up
//因为这时候单点触摸事件已经不完整了,混合了其余的事件且多点触摸可能致使本来的单点触摸事件的坐标数据不正常,因此再也不处理单点触摸事件
if (!mIsMultiDown && mMultiTouchCount <= 0) {
//此处分为两种状况
//一种是未进行不论什么多点触摸状态的,那么必然为单,事件必须响应
//在事件响应处两个推断条件是:1.用户高速单击,没有move事件,此时 isInMotionMove=false;
if (!mIsInMotionMove
//2. 用户慢速单产生了move事件但仍没有形成多点触摸事件,此时 isInMotionMove=true 且 isSingleMove=true;
|| (mIsInMotionMove && mIsSingleMove)) {
showMsg("单击 up");
this.onSingleTouchEventHandle(evenMOTION_EVENT_NOTHING);
} else {
//一种是进行了多点触摸,且在多点触摸结束以后保持单点触摸的状态,此时以多点触摸按下的时刻处理触摸事件(即在move中已经按up处理掉事件了)
//则在完毕所有事件以后的up中将再也不处理该事事件,即下面的"不处理"
showMsg("单击 up 不处理");
}
//处理触摸结束事件,重置变量
this.finishTouchEvent();
break;
case MotionEvent.ACTION_POINTER_UP:
//当确认进入多点单击状态,则运行多点单击抬起事件
if (mMultiTouchCount > 0) {
showMsg("多点触控 up");
this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING);
}
//每次多点触摸抬起触发一次,多点触摸次数-1(直到仅仅剩单点触摸为止再也不触发此事件,此时变量值为0)
mMultiTouchCount -= 1;
break;
case MotionEvent.ACTION_MOVE:
//进入移动状态
mIsInMotionMove = true;
//当前不是多点单击状态,则进行移动操做
//若触发了多点触摸事件,则结束单点移动事件,进入多点触摸移动事件
//结束单点移动操做后在触摸事件结束以前都不会再运行单点移动操做
//这样的状况是为了不有可能实用户单击移动以后再进行多点触控,这样的状况没法处理为用户需要移动仍是需要缩放
//而且引发的坐标变化可能致使一些错乱
if (!mIsMultiDown && mMultiTouchCount <= 0) {
showMsg("单点触摸 move");
this.onSingleTouchEventHandle(event, MOTION_EVENT_NOTHING);
mIsSingleMove = true;
//多点触摸事件触发了,进入多点触摸移动事件
} else if (mIsMultiDown && mMultiTouchCount > 0) {
//若此前是单点触摸的移动状态时
if (mIsSingleMove) {
//按单点触摸的结束状态处理并再也不响应单点触摸移动状态
showMsg("单击 move 结束");
this.onSingleTouchEventHandle(event, MotionEvent.ACTION_UP);
mIsSingleMove = false;
}
//正常直接多点移动操做
showMsg("多点触控 move");
this.onMultiTouchEventHandle(event, MOTION_EVENT_NOTHING);
}
break;
}
前面提到,关于双击事件的处理逻辑是这样的:完毕一次单击事件记录单击事件标识,第二次触发单击事件时,依据此前是否存在第一次单击事件来肯定是否触发双击事件
在这里需要注意的一个点是,单击事件存在两种方式,而每一种方式的完毕都是一次单击事件.
咱们定义的双击事件是:
两次必定时间间隔内连续发生的单击事件即为一次双击事件,这里单击事件的触发方式是
随意的
而因为单击事件在ACTION_UP
事件中检測并触发,两种方式的单击事件都需要检測及处理,因此这个过程可能致使单击事件会被触发两次
这样的状况下就需要用不一样的变量来识别单击事件的触发了.需要处理的包含:
因为两种方式的单击事件都需要检測一次,因此可能存在一种状况:(不管哪一种方式优先检測)
条件:假设单击事件成立标识为true
状况: -----
1.假设已经进行了一次单击,最后一次单击标识为true.
2.首先检測第一种单击方式(先检測是否触发双击事件),当第一种方式单击成立以后,本次触摸事件单击事件成功,标识置为true;
3.当另一种方式检測时(先检測是否触发双击事件),最后一次单击标识为true,且本次触摸事件已经为true(上一个方式已经肯定),则会触发一次双击事件 -----
结果:在一次单击事件检測中,因为两种单击方式都需要检測就可能致使一次单击事件就会触发双击事件了.
这个过程明显是存在BUG
所以需要设置不一样标识在不一样方式的单击事件中使用;
固然,现在已经加入了两种检測方式仅仅能触发一种的处理,因此这个问题easy处理很是多.
//是否触发基于时间的单击事件
boolean isFireTimeClickEvent=false;
//是否触发基于距离的单击事件
boolean isFireDistanceClickEvent=false;
//上一次单击事件是否被触发了
boolean isFireLastClickEvent=false;
双击事件是基于单击事件的,两次连续触发的单击事件才构成一次双击事件;所以双击事件的检測(这里需要注意事件的检測与事件的响应是分开的,检測到某个事件不必定需要响应相应的事件):
不论什么一次单击事件检測完毕以后都需要检測双击事件,再运行单击事件;单击事件需要检測两次(因为有两种触发方式),所以双击事件是必须检測两次的.
双击事件被触发时,其余事件ACTON_UP
事件是不会触发的
//检測是否触发双击事件
//检測本次触摸事件中是否触发了(不论什么一种)单击事件
if((isFireTimeClickEvent || isFireDistanceClickEvent)
//检測上一次单击事件是否触发了
&& isFireLastClickEvent){
//条件成立,触发双击事件
//同一时候重置所有相关变量
//因为双击事件已经触发,保留变量状态会影响下一次推断
isFireTimeClickEvent=false;
isFireDistanceClickEvent=false;
isFireLastClickEvent=false;
//处理了双击事件则再也不响应不论什么其余事件
break;//或者return;
}
//若处理了时间单击事件,相应标识置为true
isFireTimeClickEvent=true;
//同上检測双击事件
//若处理了距离单击事件,相应标识置为true
isFireDistanceClickEvent=true;
//保存这次单击状态
isFireLastClickEvent = isFireTimeClickEvent || isFireDistanceClickEvent;
在以上的触摸处理事件中,咱们提到单击分为两中方式:
基于时间的单击事件处理已经在ACTION_DOWN
事件中操做了,经过延时发送取消按下标识,再从ACTION_UP
事件中进行推断是否处理为一次基于时间的单击事件
基于距离的单击事件在ACTION_DOWN
中没有不论什么处理,因为距离自己跟时间没有不论什么关系.
ACTION_UP
事件中抬起的坐标与按下坐标值距离在赞成范围内即为一次基于距离的单击事件因为存在这样的特殊的单击方式,因此基于距离的单击事件仅仅跟ACTION_DOWN
/ACTION_UP
有关;但这是不无缺的.
存在一种可能,**在单点触摸中按下以后,触摸点进行了移动`ACTION_MOVE`事件,而后再移动回到按下的位置**
即`ACTION_DOWN`的位置坐标,此时在`ACTION_UP`事件中,触摸点的坐标没有变化,`ACTION_MOVE`中所有的操做对`ACTION_UP`是透明无效的,这可能会违背了咱们需要处理的单击事件
因此需要修正这样的可能存在的错误;修正方式也很是easy,即然是在ACTION_MOVE
中产生的问题,在ACTION_MOVE
修正;
修正方式例如如下,针对距离单击事件
//事先定义用于标识触摸点是否产生超过单击的赞成范围的移动事件(下面称为非法事件)
//**针对距离单击事件**
booelan mIsClickDistanceMove = false;
case MotionEvent.ACTION_MOVE:
//当前移动过程当中,触摸点未产生非法移动事件
if (!mIsClickDistanceMove) {
//进行检測
float moveDistanceX = event.getX() - mUpX;
float moveDistanceY = event.getY() - mUpY;
//此处为移动赞成的偏移量范围,因为手指easy抖动,增大此值可以增大容错率
int offsetDistance = SINGLE_CLICK_OFFSET_DISTANCE;
//触摸点移动超过单击赞成范围的偏移量
if (Math.abs(moveDistanceX) > offsetDistance
|| Math.abs(moveDistanceY) > offsetDistance) {
//产生非法移动事件
//一旦产生了非法移动事件,则不需要再次检測了
mIsClickDistanceMove = true;
}
}
以上为所有的触摸事件的处理方案
直接实例化TouchEventHelper
,实现其接口,重写所有抽象方法就能够.
TouchEventHelper
实现了View.onTouchListnener
接口,经过此监听方法实现对触摸事件的操做.
在Activity中也可以使用,调用时onTouch(View,MotionEvent)
传递的參数中将view设置为null就能够.
整个触摸事件处理中并不会涉及不论什么跟view有关的操做,仅分析及处理MotionEvent
//參数为事件处理接口 onTouchEventListener
TouchEventHelper helper=new TouchEventHelper(this);
//直接在需要处理触摸事件的地方调用onTouch方法,如:
//onTouchEvent(MotionEvent event),在view中
//dispatchTouchEvent(MotionEvent event),在view,activity或者viewGroup中
helper.onTouch(null,event);
//或者直接使用view.setOnTouchListener(helper)
//activity中没法使用setOnTouchListener(),因此仅仅能在相应的方法中进行调用
源代码请移步Github查看;