不用我多说,相信大家都知道事件分发的重要性吧。它是Android知识体系中重要的一部分,当然对于初学Android的人来说是比较难的一部分,但是又不能不学,因为它是解决滑动冲突的理论基础,而且面试的时候面试官非常喜欢问这部分内容。
我不会单单的贴贴源码,我也不会单单的写个Demo打打Log,我也不会单单的写写结论。因为这篇文章里源码、log、结论都有……(哈哈开个玩笑),我觉得学习事件分发不能靠背背结论,看看别人写的流程图就算了,而是真的要看看源码写写Demo体会一下,这样才会有深刻的认识。
好了,废话就不说了,下面谈谈事件分发吧。本文主要谈View的事件分发。
我们先来写个栗子,就是给一个Button设置setOnTouchListener和setOnClickListener,其中onTouch是有返回值的,默认返回false。下面我们打一下log:
那么我们修改onTouch返回值为true后:
那么我们暂时可以得到两个小结论:
- onTouch先于onClick被触发
- 如果onTouch返回值为true,那么onClick便不会被触发
下面我们再写个栗子,写个MyButton类继承Button,重写dispatchTouchEvent和onTouchEvent方法。暂时并不加任何其它代码,只是打一下log:
onTouch返回值为true:
onTouch返回值为false:
如果修改dispatchTouchEvent返回值为false(这里onTouch返回true时):
@Override
public boolean dispatchTouchEvent(MotionEvent event){
Log.d("-------MyButton-------","----dispatchTouchEvent----"+event.getAction());
// return super.dispatchTouchEvent(event);
super.dispatchTouchEvent(event);
return false;
}
我们可以看到当dispatchTouchEvent返回值为false时,Button已经接收不到move、up事件了。而是传递给了它的父控件。
好了两个栗子到此结束,等我们看完了源码再回来看栗子吧。。
View的事件分发涉及到dispatchTouchEvent和onTouchEvent两个方法,我们先看dispatchTouchEvent的源码:
public boolean dispatchTouchEvent(MotionEvent event){
//......省略
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
//.......省略
return result;
}
这里是dispatchTouchEvent的关键代码,我们看一下第二个if语句:
- 条件一:只要我们对View设置了Listener,那么li!=null
- 条件二:只要我们对控件设置了setOnTouchListener事件,则条件二为true
- 条件三:该View是否是enabled
- 条件四:onTouch的返回值,其实这里可以看做是先执行onTouch,再判断返回值
再看一下第三个if语句,如果没有进入第二个if语句并且onTouchEvent返回值为true,才会进入第三个if里面。
其实两个if语句里只有一句代码,就是修改了一个boolean类型变量值,我们可以把dispatchTouchEvent源码修改下更容易理解:
public boolean dispatchTouchEvent(MotionEvent event){
//......省略
if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
//.......省略
return onTouchEvent(event);
}
暂时可以得到下面几个结论:
- 假设View设置了setOnTouchListener,如果onTouch返回值为true,那么onTouchEvent不会被调用;如果onTouch返回值为false,那么onTouchEvent会被调用,此时onTouch也是会被调用的,这点要注意。
- 如果该View是disenabled的,那么onTouch不会被调用,但是onTouchEvent会被调用。
- 注意分析dispatchTouchEvent的返回值,跟onTouch或者onTouchEvent有关系。
那么onClick是在哪里被调用的呢?别着急,我们先看看接着上面去看看onTouchEvent的源码,我们可以看到有个方法performClick(),其实是在这里面触发的onClick
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
现在我们对各个方法和结论再总结一下:
- 同一个事件序列包括down事件、n个move事件、up事件。
- 事件分发的顺序为dispatchTouchEvent–>onTouch–>onToucEvent
- dispatchTouchEvent方法是控制事件分发的,返回值为true时表示中止down事件的分发,同事件序列的move、up事件会传递给该控件;返回值为false时表示不中止down事件的分发,这个事件该控件不要,同事件序列的move、up事件不会传递给该控件。
- 当onTouchEvent被调用,它的返回值就是dispatchTouchEvent的返回值。
好了,View的事件分发大概就写这么多了,其实还有更多的结论,但是都可以通过源码分析出来,这里不过多介绍了。如有错误,欢迎指正。