Android中的事件分发(上)

不用我多说,相信大家都知道事件分发的重要性吧。它是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:
图1
onTouch返回值为false:
图2
如果修改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的事件分发大概就写这么多了,其实还有更多的结论,但是都可以通过源码分析出来,这里不过多介绍了。如有错误,欢迎指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值