美文网首页
onTouch和 onClick的那些事儿

onTouch和 onClick的那些事儿

作者: ModestStorm | 来源:发表于2020-06-21 14:29 被阅读0次

转载自:onTouch和 onClick的那些事儿

image

PS:在这个全民知识焦虑的时代,每个人都应该明白学习的重要性,应该成为“会学习的人”,而不是做“最勤奋的笨蛋”。

事件的分发流程可以说基本上已经阐述清楚,在阅读本篇文章之前,请先阅读下面几篇文章:

还有一个问题是 Android 事件传递过程中 onTouch 和 onClick 事件在整个事件过程中是如何进行事件传递的,下面主要是关于 onTouch 、 onClick 与事件传递过程中调用的先后顺序,将从如下几个方面介绍:

  1. 源码中的 onTouch() 方法

  2. 源码中的 onClick() 方法

  3. onTouch() 与 onClick() 方法之间的关系

  4. 总结

源码中的 onTouch() 方法

当要设置触摸事件的监听时,使用到 View 类中的 OnTouchListener 接口,然后通过 setOnClickListener 设置对触摸事件的监听,然后就可以通过具体的事件类型去执行某些操作,onTouch() 方法就是接口 OnTouchListener 中定义的方法,在 View 的 dispatchTouchEvent() 方法中调用,下面时 onTouch方法在源码中的具体调用:

 1//事件分发
 2public boolean dispatchTouchEvent(MotionEvent event) {
 3    ...
 4    //默认返回值
 5    boolean result = false;
 6    ...
 7    //注意判断条件
 8    if (onFilterTouchEventForSecurity(event)) {
 9        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
10            result = true;
11        }
12
13        ListenerInfo li = mListenerInfo;
14        //注意判断条件
15        if (li != null && li.mOnTouchListener != null
16                && (mViewFlags & ENABLED_MASK) == ENABLED
17                && li.mOnTouchListener.onTouch(this, event)) {
18            result = true;
19        }
20
21        if (!result && onTouchEvent(event)) {
22            result = true;
23        }
24    }
25   ...
26    return result;
27}

上述代码中,先来看一下最外面的条件 onFilterTouchEventForSecurity() 方法,只关心该方法的返回值即可,源码如下:


 1/**
 2 * 如果事件正常分发返回true,如果事件被丢弃返回false
 3 * @see #getFilterTouchesWhenObscured
 4 */
 5public boolean onFilterTouchEventForSecurity(MotionEvent event) {
 6    //noinspection RedundantIfStatement
 7    if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
 8            && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
 9        // Window is obscured, drop this touch.
10        return false;
11    }
12    return true;
13}

所以该方法正常情况下返回 true,然后关键的条件主要就是 ListenerInfo 是否为 null,mOnTouchListener 是否为 null,以及 onTouch() 方法的返回值,下面是 ListenerInfo 初始化的源码部分,具体如下:


 1//getListenerInfo()的具体调用
 2public void setOnTouchListener(OnTouchListener l) {
 3    getListenerInfo().mOnTouchListener = l;
 4}
 5//ListenerInfo的初始化
 6ListenerInfo getListenerInfo() {
 7    if (mListenerInfo != null) {
 8        return mListenerInfo;
 9    }
10    mListenerInfo = new ListenerInfo();
11    return mListenerInfo;
12}

显然,当通过 setOnTouchListener() 方法设置触摸事件的监听时就初始化了 ListenerInfo,同在在设置触摸事件监听的时候 mOnTouchListener != null 成立,最后 onTouch() 方法的返回值决定了 dispatchTouchEvent() 方法是否返回 true。所以,当设置了触摸监听事件且 onTouch() 方法返回 true 时,表示事件就此处理也就不再向子 View 传递了,同时,onTouchEvent() 方法也就不再执行,返回 false 则 onTouchEvent() 方法还会执行。

源码中的 onClick() 方法

当要设置点击事件的事件监听时,使用到 View 类中的 OnClickListener 接口,然后通过 setOnClickListener 设置对单击事件的监听,然后就可以通过具体的事件类型去执行某些操作,onClick() 方法就是 OnClickListener 接口中定义的方法,在 View 的 onTouchEvent() 方法中调用,在下文中也会进一步得到验证,下面是 onClick() 方法在源码中的具体调用:

 1//事件处理
 2public boolean onTouchEvent(MotionEvent event) {
 3    ...
 4    if (((viewFlags & CLICKABLE) == CLICKABLE ||
 5            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
 6            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
 7        switch (action) {
 8            case MotionEvent.ACTION_UP:
 9                ...
10                //如果执行了该方法,其返回值就是onTouchEvent()的返回值
11                performClick();
12                ...
13                break;
14        }
15        return true;
16    }
17    return false;
18}

上面代码中至少找到了 onClick() 方法的调用位置,下面是 performClick() 方法:


 1/**
 2 * 主要回调了 OnClickListener
 3 */
 4public boolean performClick() {
 5    final boolean result;
 6    final ListenerInfo li = mListenerInfo;
 7    if (li != null && li.mOnClickListener != null) {
 8        playSoundEffect(SoundEffectConstants.CLICK);
 9        //调用了OnClickListener接口中的onClick()方法
10        li.mOnClickListener.onClick(this);
11        result = true;
12    } else {
13        result = false;
14    }
15    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
16    return result;
17}

显然,只要我们在代码中通过 setOnClickListener() 方法设置了对单击事件的监听,则对应 View 的 onTouchEvent() 方法返回 true,当然事件就此消费,反之返回 false,那么 onTouch 与 onClick 之间的调用顺序如何,它们之间会互相影响吗,下面就会从案列的角度了解它们之间的关系。

onTouch() 与 onClick() 方法之间的关系

还是之前的案例,MLinearLayout 嵌套 MRelativeLayout,MRelativeLayout 嵌套 MTextView,三个 View 都只是重写了与它们自身相关的事件分发,然后为 MTextView 设置对触摸事件、单击事件的监听,具体如下:

 1@Override
 2protected void onCreate(Bundle savedInstanceState) {
 3    super.onCreate(savedInstanceState);
 4    setContentView(R.layout.activity_main);
 5
 6    findViewById(R.id.textView).setOnTouchListener(new View.OnTouchListener() {
 7        @Override
 8        public boolean onTouch(View v, MotionEvent event) {
 9            Log.i("Event", "TextView-------onTouch---------------return:" + false);
10            return false;
11        }
12    });
13
14    findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
15        @Override
16        public void onClick(View v) {
17            Log.i("Event", "TextView-------onClick");
18    }
19    });
20}

  • 让 onTouch() 返回 false,查看日志如下:
image

结论:设置了对触摸事件的监听,onTouch() 方法 false 时 onTouchEvent() 方法在 onTouch() 方法之后执行,事件就此消费,接着接受 ACTION_DOWN 之后的一系列事件,途中使用鼠标,故没有 ACTION_MOVE 事件,还有在 onTouch() 方法返回 false 的情况下 onClick() 执行了。

  • 让 onTouch() 返回 true,查看日志如下:
image

结论 :当 onTouch() 返回 true 的时候,正如前面所述 onTouchEvent() 将不会再执行,故 onClick() 也就不会再执行。

总结

onTouch() 方法的返回值决定了 onTouchEvent() 方法要不要执行,如果 onTouch() 返回 true,则 onTouchEvent() 不会再执行,返回 false ,则 onTouchEvent() 继续执行,而 onClick() 的回调是在 onTouchEvent() 方法中调用,onTouchEvent() 不执行则 onClick() 不执行。

相关文章

网友评论

      本文标题:onTouch和 onClick的那些事儿

      本文链接:https://www.haomeiwen.com/subject/egfaxktx.html