Android_事件纷发

Posted CodeCopyer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android_事件纷发相关的知识,希望对你有一定的参考价值。

关于事件你应该知道的是
当一个事件产生后,他的传递过程遵循如下顺序Activity > Window > View
事件来源于activity,activity假如你没有重写任何关于事件纷发的方法的话,他会把事件传递给window,window将事件传递给decorView

现在我们来看下假如我们在activity中重写了dispatchEvent的方法是什么样的一个情况.

  1. 我们把activity中的dispatchTouchEvent 返回true
    这里写图片描述
    事件被activity的dispatchTouchEvent消耗掉,所以activity的
    onTouchEvent方法得不到响应,我们看下log输出
    这里写图片描述
    很明显的能看出来,log只打印了dispatchTouchEvent中的相关方法,整个事件被dispatchTouchEvent消耗掉,并没有传递到我们的window > decorView 所有我界面的返回键接受不到事件,所以返回按钮的点击事件也没生效,

  2. 我们在把activity中的dispatchTouchEvent返回false看看是什么情况,,这也是我的一个疑问点,麻烦大神帮忙解答一下就是我activity中的dispatchTouchEvent返回了false,那么事件标志着事件没人处理,此时事件的根源是从activity来的,如果他都不管的话,这个事件已经不能返回他的父view ,当前activity的onTouchEvent()也无log输出,那么事件哪里去了????
    这里写图片描述
    然后我们看下返回false的log输出如下图:
    这里写图片描述
    log输出一只执行了activity中的dispatchTouchEvent的相关方法,
    TouchEvent并没有任何输出,表明activity的onTouchEvent也没有接受到这个事件,此时我再点击返回键,的onclick依然是失效的,
    所以.麻烦大神帮忙解答下这一点,当activity中的dispatchtouchevent返回false,事件的流程是怎么样的,???

  3. 最后我们让activity中的dispatchtouchevent 返回super看下
    这里写图片描述
    然后看下log日志的输出,以及log 的顺序,
    这里写图片描述

注意log的顺序,dispatchTouchEvent down > onTouchEvent down
dispatchTouchEvent move > onTouchEvent move
dispatchTouchEvent up > onTouchEvent up

面试的时候注意面试官问的问题,和执行顺序的问题

  • 然后我们往activity中加入一个linearlayout,我们重写一下linearlayout的有关事件的三个方法,并打印log看一下,暂时不往linearlayout中加入任何子view,单纯的看一下事件从activity传递到viewgroup的效果.

    首先我们在书写一个TESTLinearlayout,如下

public class AAATestLinearlayout extends LinearLayout {
    private static final String TAG = "AAATestLinearlayout";

    public AAATestLinearlayout(Context context) {
        super(context);
    }

    public AAATestLinearlayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public AAATestLinearlayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Logger.e(TAG, TAG + ">>>>dispatchTouchEvent.ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Logger.d(TAG, TAG + ">>>>dispatchTouchEvent.ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Logger.w(TAG, TAG + ">>>>dispatchTouchEvent.ACTION_UP");
                break;
        }

//        return true;
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Logger.e(TAG, TAG + ">>>>onInterceptTouchEvent.ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Logger.d(TAG, TAG + ">>>>onInterceptTouchEvent.ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Logger.w(TAG, TAG + ">>>>onInterceptTouchEvent.ACTION_UP");
                break;
        }

//        return true;
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Logger.e(TAG, TAG + ">>>>onTouchEvent.ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Logger.d(TAG, TAG + ">>>>onTouchEvent.ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Logger.w(TAG, TAG + ">>>>onTouchEvent.ACTION_UP");
                break;
        }

        return super.onTouchEvent(event);
    }

}

我们就先默认的让测试的linearlayout全部返回super值.

看下事件传递的过程从activity > window > decorView > 我们自己的linearlayout;
这里写图片描述

并且我们在linearlayout中的构造中设置了额外的onTouchLinstener,
我们看下的结果;
这里写图片描述
这里写图片描述

这里简答描述一下,我们往activity中加入了一个linearlayout.并且在构造中给linearlayoutset了一个touchlistener.
我们从log可以看到,事件从activity中开始传递,然后我们在linearlayout中有关事件的方法全部返回super.
其中关于onInterceptTouchEvent
我想说两句,这句话的返回值影响的是viewgroup容器中子view的事件传递,并不会对当前的viewgroup的onTouchEvent或者set 的touchlistener有影响,我们从log可以看出来,linearlayout的 onTouchEvent.down 和 touchliste的down均执行了

假如onInterceptTouchEvent的值返回的是super的话,我们看viewgroup的源码可以知道
默认是false,也就是不拦截…重要的是不拦截….

    * appear here.
     * </ol>
     *
     * @param ev The motion event being dispatched down the hierarchy.
     * @return Return true to steal motion events from the children and have
     * them dispatched to this ViewGroup through onTouchEvent().
     * The current target will receive an ACTION_CANCEL event, and no further
     * messages will be delivered here.
     */
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

这是viewgroup的源码,和android艺术探索第143页第六条说的完全吻合,
还有一篇博客,应该是比较经典的,其中对onInterceptTouchEvent的解释如下;
这里写图片描述

这里博客博主说是返回super的话默认被拦截,并把拦截的事件交给当前view,处理,
我们这里的当前view就是我们的linearlayoutlayout 从log看确实执行了onTouchEvent.但是这个onTouchEvent并不是因为返回了super他没有拦截而执行了.因为笔者试着返回true false,super,对我们设置的touchlistener和onTouchEvent的down事件没有任何影响,

所以这块我也迷茫了半天,看了书本和咨询了群里的nil大概猜测一下可能是博主理解有出入或者笔误,再次留下博客的地址:请大神批阅和校正一下原文链接如下Android编程下的事件纷发

接着说我们的场景,目前我们linearlayout中的事件处理方法均没有对事件做处理,这样我们linearlayout中的子view比如我们的返回按钮确实能接收到事件,然后点击返回箭头,activity关闭,此事第一个场景走完.

  • 第二个场景,我们把我们的liearlayout中的dispatchTouchEvent返回true,
    我们猜想是这样的,如果返回true,事件就在当前的方法中终止了也就是在liearlayout中终止,所以其他的方法根本不会接收到这个事件,所以看下我们执行的log输出,
    这里写图片描述

这里写图片描述

这里以为linearlayout中dispatchonTouchEvent返回了true,事件在这里终止了,所以我们的返回箭头就接收不到事件,于是我们的返回按钮就失效了.

  • 第二个场景,我们让linearlayout的dispatchonTouchEvent返回false,那么这个触摸事件将是一个怎么样的过程呢?

    dispatchonTouchEvent.返回false,事件要被来自哪里,就要被返回哪里,假如我们的linearlayout还有父view ,那么这个事件将返回给父view的onTouchEvent事件处理,如果来自activity,那么将被activity的onTouchEvent处理,看下我们的log输出;

这里写图片描述

![这里写图片描述](https://img-blog.csdn.net/20160331173347685)

这里写图片描述

当我们点击我们的返回箭头的时候,因为事件从liearlayout中返回给了activity消费,所以子控件没法接收到事件,所以点击返回的箭头也无法生效;

  • 第三个场景,linearlayout中的onInterceptTouchEvent返回true,其他返回super,猜想,拦截事件返回true,那么事件不会传递给子view,
    返回按钮不会生效.因为我们其他的方法都是返回到 super,所以事件在onTouchEvent中返回了给其父view 或者activity,由于activity中的onTouchEvent也返回到 super,所以事件消失,..具体看log输出的过程和解释
    这里写图片描述

这里写图片描述

这里写图片描述

因为事件也在activity中,并没有传递给子view 所以返回按钮失效

  • 下一个场景,让我们的liearlayout中的onInterceptTouchEvent返回false,不拦截,那么事件将会传递给子view,子view在去调用自己的dispatch方法去纷发,这样事件就能被返回箭头接受,可以正常关闭activity

    我们看下log输出,
    这里写图片描述

这里写图片描述

由于onInterceptTouchevent返回了false,所以事件执行完他里面的方法后就把事件传递给了子view,于是事件就从发源地activity 和linearlayout中的 dispatch 和 intercept 起作用,并没有onTouchEvent的事情了,具体细节看log输出描述,然后由于事件传递给了子view 所以 返回箭头生效,这个方法返回false和返回super产生的效果是一模一样的,这里就不在截图了有兴趣的可以输出一下

  1. 到此为止我们activity中只有一个viewgroup的场景基本分析完毕了,然后我们想liearlayout中添加一个textview如下图

    这里写图片描述

因为我们这里加入的是最小单位的view就是view不能再添加额外的内容,我们手指触摸屏幕事件传递过程,有activity 到 liearlayout 在到view 在返回 linearlayout ,返回activity 直到消失,具体过程如下;

这里写图片描述

这里写图片描述

这里写图片描述

再来一个场景,这次我们让childview中的dispatch 事件返回false,
笔者本来也是对这个传递有点模糊的,今天博客写到这里我都能猜到打印结果了,由于textviewdispatch返回了false ,所以事件执行完dispatch down后直接把事假返回给父view linearlayout的onTouchEvent,由于onTouchEvent设置了touchlistener 所以后执行,然后就是父view也返回了super,所以事件传递给activity的onTouchEvent 由于activity的onTouchEvent也返回了super.所以事件消失,最后在activity中执行 move 和 up

然后我们看下打印结果;

这里写图片描述

这里写图片描述

写到这里,理解的事件传递在脑海里的模糊场景已经有点透明了,还需要更多的实践,文中可能有理解不到位的也请大神批正,到此我理解的事件传递在viewgroup 和view中全部结束,也感谢群里nil的指点谢谢!

如果你看完还是对这个过程不是很理解,请自己模拟场景打印log,因为我感觉没有在比笔者更笨出身更贫寒的了,当你模拟看log有点想吐甚至能猜到不同的返回值会是什么样的传递场景,那么你对事件传递的过程相比之前你的理解肯定有恍然大悟的!谢谢….

以上是关于Android_事件纷发的主要内容,如果未能解决你的问题,请参考以下文章

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

Android:将片段和弹出窗口的点击事件中生成的变量传递给活动的方法

android:来自片段的菜单项单击事件

在同一个片段中实现多个事件监听器 - Android

Android - 如何录制简单的声音片段?

Android YouTube Player API Fragment无法手动处理触摸事件