前言
很久之前看了些长篇大论的文章之后,只是去死记硬背的记住一些结论,后面不常用也是忘得一干二净;最近闲研究了一番,记录下当时是以什么样的姿势(思路)去了解的。这里不是从源码的角度上进行分析,而是从现象分析猜其分发机制的逻辑,并得出的结论,得到结论之后有时间再去分析源码相对会容易些。这篇文章分享的是如何去测试分发机制的文章,如果你想要自己去测试一番事件分发机制但又没有思路测试,或许这篇文章会对你有一些帮助。
事件分发常识
<li>事件流:用户从按下到松开屏幕之间所产生的所有事件组成称为一个事件流。</li>
<li>事件的基本传递方向Activity——>ViewGroup——>View。</li>
<li>用户在Activity产生的所有事件,系统首先调用<b>Activity</b>的dispatchTouchEvent方法进行分发。</li>
<li>dispatchTouchEvent: Activity和ViewGroup通过调用自身的该方法完成事件分发。返回布尔值。</li>
<li>onInterceptTouchEvent: ViewGroup分发逻辑上会调用这个方然来判断是否拦截事件。返回布尔值。</li>
<li>onTouchEvent: 事件的消费方法。返回布尔值。</li>
<li>View (例如:TextView)如果传递到该层,那么就不再进行事件分发,而是自己消费,作为事件传递的最终载体。</li><br />
方便测试,这些常识最好熟记。
提出相关问题
dispatchTouchEvent:事件分发方法
onInterceptTouchEvent:事件拦截方法
onTouchEvent:事件消费方法
以下不写方法名称,只写翻译的中文意思
<pre>
1.系统调用Activity的事件分发方法进行事件分发后,这个方法的返回值对于系统意味着什么?<pre>测试结果:这个返回值对于系统没有任何影响,无论返回什么值,系统对用户产生的r任何事件都会调用该方法进行分发</pre>2.ViewGroup的事件分发方法的返回值是否影响了Activity对该事件的进一步处理?<pre>测试结果:是!如果ViewGroup分发结果返回true,则Activity不处理,如果返回false,则处理,即Activity.onTouchEvent被调用。</pre>3.ViewGroup的事件拦截方法的返回值对于ViewGroup的事件分发方法是否有影响?<pre>测试结果:有影响,决定了是否继续分发到下一层还是自己消费事件。</pre>4.ViewGroup的事件拦截方法的返回值是否决定了ViewGroup的事件消费方法的执行?<pre>测试结果:是!</pre>5.ViewGroup的事件消费方法的返回值是否决定了ViewGroup的事件分发的返回值?<pre>测试结果:是!</pre>6.测试ViewGroup的事件拦截方法的返回值是否影响了其子类的事件分发方法的执行?<pre>测试结果:是!当ViewGroup的拦截方法返回false,则把事件传递到子类,即子类的事件分发方法被调用继续分发</pre>7.测试View的事件消费方法的返回值是否决定了View的事件分发方法的返回值?<pre>测试结果:是!</pre>8.View的事件分发方法的返回值是否影响了其父类的对该事件做进一步处理?<pre>是!如果view消费的情况返回false,那么ViewGroup会认为你没有处理ViewGroup会消费该事件,如果返回true,ViewGroup会认为你已经处理不需要再去处理消费</pre>
</pre>
上面的问题是从上到下的测试流程,请自行测试。
测试思路
1.创建一个MainActivity,复写分发和消费方法
2.创建一个MyViewGroup继承FrameLayout,复写分发方法、拦截以及消费方法。作为MainActivity的布局的根节点
3.创建一个MyView继承TextView使用在MyViewGroup中
例如
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG,"Activity开始分发事件--------------------");
boolean result = super.dispatchTouchEvent(ev);
// boolean result = false;
// boolean result= true;
Log.e(TAG,"Activity事件分发结果-----------------------"+result);
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG,"Activity处理事件--------------------");
boolean result = super.onTouchEvent(event);
// boolean result = false;
// boolean result= true;
Log.e(TAG,"Activity处理事件结果-----------------------"+result);
return result;
}
首先要明确的一点是,这些个方法内调用super.xxxEvent(ev)还是直接返回true和false有本质的区别,使用super.xxxEvent(ev)则是采用父类的分发策略(比如Activity、ViewGroup、View),即这些方法都有三种情况要进行测试,调用父类的分发策略或者直接返回true和false;其中还有一个值得测试的点是,onTouchEvent方法的父类默认消费策略的结果会受哪些因素影响(比如clickable="true/false"),还有一个onDispatchTouchEvent的父类分发策略会不会受setOnTouchListener的影响?
测试前,首先看下Activity的dispatchTouchEvent方法的分发策略
Activity{
public boolean dispatchTouchEvent(MotionEvent ev) {
/**if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}*/
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
}
Activity的分发逻辑很简单,getWindow().superDispatchTouchEvent(ev)最终会去调用ViewGroup的事件分发方法。而Activity又会根据ViewGroup的分发结果来做处理,如果分发结果为true,则不做任何处理直接返回true,如果分发结果为false,则调用onTouchEvent消费事件,并把消费结果返回。
总结
测试完你会发现其实ViewGroup的分发逻辑才是重点,经过上面的测试,下面就可以写出ViewGroup的基本分发逻辑:
ViewGroup{
public void dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
if(onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev);
}else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
}
不过要知其所以然,还是需要从源码具体分析。
总结两个一劳永逸的点:
<pre>
onTouchEvent:一个事件如果按默认的分发策略,必然会被消费,即可能被Activity/ViewGroup/View的onTouchEvent方法消费,这个方法值决定了本层的dispatchTouchEvent方法的返回值,而本层的dispatchTouchEvent的返回值又影响了父类的dispatchTouchEvent方法对事件是否需要进一步处理的行为。<br />
dispatchTouchEvent:
在一个事件流中:
如果该方法对这个事件的分发结果返回false,那么意味着后续的事件将不再分发到该层,即不再调用该层的dispatchTouchEvent方法。
如果该方法对这个事件的分发结果返回true,那么意味着后续的事件将继续分发到该层,即调用dispatchTouchEvent方法。
</pre>