背景
看了很多关于android事件传递的技术博客,写得特别官方,不接地气,让人看了以后不是特别深刻。特别在遇到一些滑动冲突问题上面,不知道如何去解决,大概知道有dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三个方法可以解决问题,然后尝试着一个一个return ture or false解决问题,虽然运气好解决了问题,但为何是那样?却一知半解。
目的
通俗易懂地帮助大家去理解android事件传递机制,能举一反三地解决事件传递相关的疑难杂点。
主题
今日笔者带领大家从代码的角度去分析android事件传递机制,解开大家心里的疑惑。大家从各种技术渠道都大致了解了事件传递机制有两大主角,分别是ViewGroup、View。然后会有童鞋会问,那activity呢?其实activity可归类ViewGroup。在事件传递机制中,了解过事件传递的童鞋应该知道ViewGroup有三个方法分别对应:分发事件(dispatchTouchEvent)、拦截事件(onInterceptTouchEvent)、消费事件(onTouchEvent);View只有两个方法,跟ViewGroup区别,就少了一个拦截事件(onInterceptTouchEvent)。
分发事件(dispatchTouchEvent)
- 将事件分发下去
- 类似领导分发工作
拦截事件(onInterceptTouchEvent)
- 事件分发后是否需要拦截?
- 类似领导分发工作之后的一次确认,是否工作真的需要分发下去?
消费事件(onTouchEvent)
- 事件是否消费?
- 类似分发的工作是否完成?如果完成了,表示这个工作已经完结,不需要再继续分配了。
接下来我们开始从代码的角度分析android事件传递机制,首先我们创建一个自定义ViewGroup和一个自定义View,分别为CustomLayout、CustomView。
CustomLayout可用对应代码如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
System.out.println("dddddddddddddddddd CustomLayout dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
System.out.println("dddddddddddddddddd CustomLayout onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// CustomButton onTouchEvent
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
System.out.println("ddddddddddddddddddddd CustomLayout onTouchEvent ACTION_DOWN");
return true;
case MotionEvent.ACTION_UP:
System.out.println("ddddddddddddddddddddd CustomLayout onTouchEvent ACTION_UP");
break;
}
System.out.println("ddddddddddddddddddddd CustomLayout onTouchEvent(event) = " + super.onTouchEvent(event));
return super.onTouchEvent(event);
}
CustomView可用对应代码如下:
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
System.out.println("dddddddddddddddddd CustomView dispatchTouchEvent");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
System.out.println("ddddddddddddddddddddd CustomView onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
System.out.println("ddddddddddddddddddddd CustomView onTouchEvent ACTION_UP");
break;
}
System.out.println("ddddddddddddddddddddd CustomView onTouchEvent(event) = " + super.onTouchEvent(event));
return super.onTouchEvent(event);
}
然后app run起来,结果如下:
09-09 10:45:42.411 3011-3011/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomLayout dispatchTouchEvent
09-09 10:45:42.411 3011-3011/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomLayout onInterceptTouchEvent
09-09 10:45:42.411 3011-3011/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomView dispatchTouchEvent
09-09 10:45:42.411 3011-3011/cn.jianke.eventdistributiondemo I/System.out: ddddddddddddddddddddd CustomView onTouchEvent ACTION_DOWN
09-09 10:45:42.411 3011-3011/cn.jianke.eventdistributiondemo I/System.out: ddddddddddddddddddddd CustomView onTouchEvent(event) = false
09-09 10:45:42.411 3011-3011/cn.jianke.eventdistributiondemo I/System.out: ddddddddddddddddddddd CustomLayout onTouchEvent ACTION_DOWN
09-09 10:45:42.458 3011-3011/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomLayout dispatchTouchEvent
09-09 10:45:42.458 3011-3011/cn.jianke.eventdistributiondemo I/System.out: ddddddddddddddddddddd CustomLayout onTouchEvent ACTION_UP
09-09 10:45:42.458 3011-3011/cn.jianke.eventdistributiondemo I/System.out: ddddddddddddddddddddd CustomLayout onTouchEvent(event) = false
我们发现最先执行的是ViewGroup里面的dispatchTouchEvent(事件分发)方法,然后onInterceptTouchEvent(事件拦截),ViewGroup未拦截,就将事件传递给了View,执行了View的dispatchTouchEvent(事件分发)方法,接着是View的onTouchEvent事件,而View的onTouchEvent事件中最先执行的是ACTION_DOWN方法,然后发现View的onTouchEvent事件并没有消费事件(类似没有完成任务),就将该事件又传回给了ViewGroup,此时执行 ViewGroup的onTouchEvent事件,然后ViewGroup重新分发事件,这时没有把事件给View,而是直接执行了ViewGroup的onTouchEvent事件,然而自己也未消费该事件,该事件会继续往上传,依次类推。
此时如果我们将View中ACTION_DOWN事件给返回true,结果会如何呢?请看下面:
09-09 10:59:16.228 10636-10636/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomLayout dispatchTouchEvent
09-09 10:59:16.228 10636-10636/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomLayout onInterceptTouchEvent
09-09 10:59:16.228 10636-10636/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomView dispatchTouchEvent
09-09 10:59:16.228 10636-10636/cn.jianke.eventdistributiondemo I/System.out: ddddddddddddddddddddd CustomView onTouchEvent ACTION_DOWN
09-09 10:59:16.265 10636-10636/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomLayout dispatchTouchEvent
09-09 10:59:16.265 10636-10636/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomLayout onInterceptTouchEvent
09-09 10:59:16.265 10636-10636/cn.jianke.eventdistributiondemo I/System.out: dddddddddddddddddd CustomView dispatchTouchEvent
09-09 10:59:16.265 10636-10636/cn.jianke.eventdistributiondemo I/System.out: ddddddddddddddddddddd CustomView onTouchEvent ACTION_UP
09-09 10:59:16.265 10636-10636/cn.jianke.eventdistributiondemo I/System.out: ddddddddddddddddddddd CustomView onTouchEvent(event) = false
从上面结果可知,当在View中onTouch ACTION_DOWN动作消费了onTouch事件,当执行ACTION_UP动作,事件依然会将事件分发给View处理。
根据上面结果做个总结,ViewGroup类似领导,View类似下属,事件类似工作任务,领导给下属分配工作任务,如果下属能按时完成工作任务,那么领导下次安排工作任务时,就会将工作任务继续分发给该下属,否者就领导看自己能否处理该工作任务,不能处理就直接把工作任务反馈给他的直接上司,依次类推。
扩展
这里进行一下扩展,onTouch跟onClick有啥关系?也许有童鞋会思考这个问题。当onTouch action为ACTION_DOWN时,如果返回ture(直接消费了该事件,类似下属处理了动作为ACTION_DOWN的工作,则不会再触发onClick事件和onLongClick事件)。
看了这篇文章,童鞋们是不是对android事件传递有了彻底的理解呢?
如有疑问,请留言。
关于作者
- QQ:872721111
- email:leibing1989@126.com
网友评论