精华内容
下载资源
问答
  • Android 点击事件分发

    2021-01-03 14:25:05
    Android 点击事件分发Activity中对事件的处理ViewGroup是如何进行事件处理的View的dispatchTouchEvent相当重要,让我们继续look总结 Activity中对事件的处理 Activity事件分发方法,返回true表示事件被消费掉了 ...
  • 1、 引言:Android如此受欢迎,其优秀的交互性功不可没,优秀的事件分发机制在交互性中起到了重要的作用。想做出有良好交互性的应用,深入了解事件分发机制是十分必要的。2、 View和ViewGroup:Android的UI界面都是...

    1、 引言:Android如此受欢迎,其优秀的交互性功不可没,优秀的事件分发机制在交互性中起到了重要的作用。想做出有良好交互性的应用,深入了解事件分发机制是十分必要的。

    2、 View和ViewGroup:Android的UI界面都是由View和ViewGroup的派生类组成的,其中View是UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是ViewGroup的派生类。

    3、 事件:简单来时,事件就是当用户的手触摸到屏幕上时,Android所做出的一系列响应,比如点击按钮,滑动屏幕等。

    4、 意义:其实Android的各个控件已经提供了完善的事件分发机制,比如我们在点击按钮的时候会触发按钮的监听,滑动一个listview时也会自动的移动,既然Android自身的事件分发机制已经如此完善,那么我们了解它会在我们项目开发时有什么好处呢?这里我想说,Android再完善的事件分发机制也无法满足我们在项目中千变万化的UI设计需求,我们了解事件分发机制,就是为了让我们能够更加灵活的做出各种UI效果。比如我想在做一个抽屉效果,当然大家会想到使用DrawerLayout,那么在抽屉收起的时候我想能够对屏幕上的各个控件正常操作,但是当抽屉拉出时我又想屏蔽掉被抽屉遮挡住的控件。做过抽屉效果的同学都知道,使用Android的DrawerLayout,要么全时的屏幕掉遮挡的控件,要么就根本无法屏蔽,做不到随心控制,这时就需要我们自定义DrawerLayout,并且灵活的编写自定义DrawerLayout中的事件分发!

    5、 事件分发中的三个函数:dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent。网络上关于这三个函数的描述非常的多而且全面,在这里我就不多解释了,这里我只从应用的角度来对三个函数做个总结; dispatchTouchEvent兄弟节点间的事件拦截,onInterceptTouchEvent父子节点间的事件拦截,onTouchEvent本节点对事件的消费。简单的一个总结,相信大家能够对这三个函数有个大概的了解,其实我们对事件分发的控制无非就是“拦截”和“分发”。以刚才的抽屉为例,当抽屉拉出时拦截事件的分发,当抽屉收回时不拦截事件的分发。

    6、 事件分发的流程:

    e9560617ec646192d5782d51cec715b3.png

    这幅图描述了一个UI的树形结构,其中ViewGroup1是Viewgroup2、View2、View3的父节点,Viewgroup2是View1的父节点。事件分发的流程是Activity->ViewGroup1->ViewGroup2->View1->View2->View3;基本的原则就当一个节点获得到事件后,先向子节点分发,然后再向兄弟节点分发,整个过程中任何一个节点消费了事件,那么分发停止。

    7、 实例分析:MyLayout和Mylayout2都继承RelativeLayout,并且重写其中的三个事件函数;MyView继承ImageView,重写其中的onTouchEvent。

    5d744669043f85489b3d327e97e79def.png

    MyLayout代码如下:

    7f2693c67b9ee56dc95bf4727593beb3.png

    c9b134927c4b09317ebe85727ba3a267.png

    ade6fb0e142ec0ca40884a5a4ebac33e.png

    MyLayout2代码如下:

    a4b6767709bd3d61f26de3b0e474cd4b.png

    71bfb49c5cf73cb0eafb4848d8650952.png

    d97627c97e0b403eb116b661086b8531.png

    MyView代码如下:

    224daeecbf3bb5f193593e80dd6da781.png

    MainActivity代码如下:

    f848818357328a2e893937ed24700a94.png

    16fcdbf23efada024fa099b57dac67b7.png

    布局文件如下:

    2c3deff5df5754fb9c69edbd38c71bb9.png

    066449a62dbb5f1f1b4301079b4534f6.png

    b2fafc8a1ebaaaf10e0e5d7fc53e01eb.png

    我们注意到,事件的三个函数都有一个boolean的返回值,dispatchTouchEvent和onInterceptTouchEvent返回true时表示要拦截,false表示不拦截;onTouchEvent返回true表示要消费,false表示不消费。既然了解了返回值的作用,那么大家应该可以想到,我们对事件分发的控制就是通过控制返回值来实现的。当前布局是MyLayout2遮挡MyLayout,为兄弟节点;MyView为MyLayout2的子节点。以当前布局为前提,我们来进行几个实验.

    实验1:MyLayout2的dispatchTouchEvent返回true,onInterceptTouchEvent返回true,onTouchEvent返回true;MyView和MyLayout随意。

    点击MyLayout2和MyLayout重合部分

    a28303b81aafd2408fc1f1479ca68fc7.png

    此时只有MyLayout2获得到了响应。

    实验2:MyLayout2不变,将MyLayout的三个事件函数返回值全部定义为true。

    点击MyLayout2和MyLayout重合部分

    16ee5de21af4b1300b836739552b4308.png

    点击MyLayout不被遮挡的部分

    304d22729628cb8da26138e4eff4102a.png

    对比发现MyLayout虽然有获得响应的能力,但是由于MyLayout2进行了兄弟节点的拦截,所以在点击MyLayout2和MyLayout重合部分时MyLayout无法获得事件的响应。

    通过实验1和实验2我们可以看到dispatchTouchEvent对兄弟节点的事件拦截能力。

    实验3:将MyView的onTouchEvent返回值定义为true,MyLayout2保持不变,点击MyView。

    1cdbf488b89a0e7547ffe34840744be8.png

    结果发现MyView虽然onTouchEvent返回true表示想要得到事件响应,但是却并没有获得到响应。

    实验4: MyLayout2的onInterceptTouchEvent返回值定义为false,MyView不变,点击MyView.

    16b88d43a67bc949cb17494ca6b6833c.png

    发现MyView成功获得到响应,实验3、4证明了onInterceptTouchEvent对父子节点的拦截能力。

    8、总结:

    需要声明的是,任何事件函数都会无条件的获得到MotionEvent.ACTION_DOWN 的事件,也就是用户手触摸的屏幕时的事件。

    以上通过4个简单的小实验介绍了一下事件的拦截和分发的基本用法,也给大家提供了一些分析此类问题的思路,有了本次实验作为基础,相信大家在学习Android事件分发相关知识的时候将不会再束手无策。

    展开全文
  • 本篇文章主要介绍了解析Android点击事件分发机制,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 1、事件分发机制用户点击产生一个MotionEvent,系统把这个事件分发给具体的view消费2、MotionEvent介绍手势接触屏幕产生的事件MotionEvent对象操作有:1、MotionEvent.ACTION_DOWN 手势按下,是所有触发操作中最开始的...

    1、事件分发机制

    用户点击产生一个MotionEvent,系统把这个事件分发给具体的view消费

    2、MotionEvent介绍

    手势接触屏幕产生的事件

    MotionEvent对象操作有:

    1、MotionEvent.ACTION_DOWN   手势按下,是所有触发操作中最开始的动作

    2、MotionEvent.ACTION_UP 手势向上,是所有触发操作最后的动作

    3、MotionEvent.ACTION_MOVE 手势按下后没有收回

    4、MotionEvent.ACTION_CANCEL  事件结束 理论上是ACTION_UP 后执行,但也有可能是非人为因素导致的.

    3e62fb448876d68b3a4760ff74312da6.png

    3、事件分发的本质

    将MotinEvent传递到某个具体的view处理的过程

    4、事件分发的对象及分发顺序

    Activity ViewGroup View

    8c1900948c8cf8271633c92044048d29.png

    Activity: 控制生命周期、处理事件

    统筹视图的现实和添加、通过其它回调方法与Window、View交互.

    View:所有UI组件的基类

    ViewGroup:一组view的组合,其本身也是view的子类,是android布局的所有父类.比view多了可包含子view和定义布局参数的功能.

    5、事件分发的过程协助者

    协助者:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()

    dispatchTouchEvent():用来进行事件的分发,当点击事件能够传递给当前view,该方法就会被调用.返回结果受到当前View的TouchEvent和下级view的dispatchTouchEvent方法影响,表示是否消费当前的事件

    onTouchEvent:在dispatchTouchEvent方法内部调用,用来处理事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前view无法再接受到后续的事件队列。而且onTouchEvent返回值由clickable和longclickable共同决定

    onInterceptTouchEvent():在dispatchTouchEvent()内部调用.用来表示是否拦截当前的事件.如果当前view拦截了某个事件,那么在同一个事件序列当中,此方法不会被调用,返回结果表示是否拦截当前事件。

    6、代码说明演示

    ViewGroup 代表 -》新建类DemoLayout 继承LinearLayout

    public class DemoLayout extends LinearLayout {

    private static final String TAG = "DemoLayout";

    public DemoLayout(Context context) {

    super(context);

    }

    public DemoLayout(Context context, @Nullable AttributeSet attrs) {

    super(context, attrs);

    }

    public DemoLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

    super(context, attrs, defStyleAttr);

    }

    public DemoLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

    super(context, attrs, defStyleAttr, defStyleRes);

    }

    @Override

    public boolean dispatchTouchEvent(MotionEvent ev) {

    // 父容器的分发事件

    Log.i(TAG, "----父容器的分发事件"+ev.getAction());

    switch (ev.getAction()) {

    case MotionEvent.ACTION_DOWN:

    break;

    case MotionEvent.ACTION_MOVE:

    break;

    case MotionEvent.ACTION_UP:

    break;

    case MotionEvent.ACTION_CANCEL:

    break;

    }

    return super.dispatchTouchEvent(ev);

    }

    @Override

    public boolean onInterceptTouchEvent(MotionEvent ev) {

    //父容器的拦截事件

    Log.i(TAG, "----父容器的拦截事件"+ev.getAction());

    switch (ev.getAction()) {

    case MotionEvent.ACTION_DOWN:

    break;

    case MotionEvent.ACTION_MOVE:

    break;

    case MotionEvent.ACTION_UP:

    break;

    case MotionEvent.ACTION_CANCEL:

    break;

    }

    return super.onInterceptTouchEvent(ev);

    }

    @Override

    public boolean onTouchEvent(MotionEvent ev) {

    //父容器的触摸事件

    Log.i(TAG, "----父容器的触摸事件"+ev.getAction());

    switch (ev.getAction()) {

    case MotionEvent.ACTION_DOWN:

    break;

    case MotionEvent.ACTION_MOVE:

    break;

    case MotionEvent.ACTION_UP:

    break;

    case MotionEvent.ACTION_CANCEL:

    break;

    }

    return super.onTouchEvent(ev);

    }

    }

    View 代表-》 新建类View集成Button

    public class View extends android.support.v7.widget.AppCompatButton {

    private static final String TAG = "View";

    public View(Context context) {

    super(context);

    }

    public View(Context context, AttributeSet attrs) {

    super(context, attrs);

    }

    public View(Context context, AttributeSet attrs, int defStyleAttr) {

    super(context, attrs, defStyleAttr);

    }

    @Override

    public boolean dispatchTouchEvent(MotionEvent event) {

    Log.i(TAG, "----子容器的分发事件"+ event.getAction());

    return super.dispatchTouchEvent(event);

    }

    @Override

    public boolean onTouchEvent(MotionEvent event) {

    Log.i(TAG, "----子容器的触摸事件"+event.getAction());

    return super.onTouchEvent(event);

    }

    }

    操作准备:在layout布局中创建DemoLayout布局,包含View控件

    android:layout_width="500dp"

    android:layout_height="500dp"

    android:clickable="true"

    android:background="#000000">

    android:id="@+id/view_touch"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:text="事件分发"/>

    初次运行看下运行流程:

    没有拦截时

    481ba690de78e471a870787f2165d194.png

    父容器把事件交给了子容器处理 dispatchTouchEvent进行事件分发 onInterceptTouchEvent没有拦截 子容器执行dispatchTouchEvent事件分发,再执行onTouchEvent事件

    拦截后 代码和流程

    @Override

    public boolean onInterceptTouchEvent(MotionEvent ev) {

    //父容器的拦截事件

    Log.i(TAG, "----父容器的拦截事件"+ev.getAction());

    switch (ev.getAction()) {

    case MotionEvent.ACTION_DOWN:

    break;

    case MotionEvent.ACTION_MOVE:

    break;

    case MotionEvent.ACTION_UP:

    break;

    case MotionEvent.ACTION_CANCEL:

    break;

    }

    return true;

    }

    220930658daf7bd47006340937bc09ac.png

    DemoLayout  dispatchTouchEvent进行事件分发 onInterceptTouchEvent拦截  父容器执行onTouchEvent,(备注:父容器clickable默认值是false即代表onTouchEvent 返回false,后续操作系列Action_UP不会执行,如下图,所以在xml中要声明clickable="true").从上看出拦截事件onIntercepeTpuchEvent不会再执行,只会被消费一次

    5d9adb9ea7b830ae5da57d00c9a0ced6.png

    若子容器不消费onTouchEvent,则事件会传递给父容器,而且不会接受后续操作,父容器onInterceptTouchEvent也不会再执行.同样若子容器在xml声明clickable="false"也就代表着子容器的onTouchEvent返回false。(备注操作:事件系列操作指的是按下ACTION_DOWN、移动ACTION_MOVE、抬起ACTION_UP,所谓后续操作如图子容器的ACTION_UP就没有再执行了)

    @Override

    public boolean onTouchEvent(MotionEvent event) {

    Log.i(TAG, "----子容器的触摸事件"+event.getAction());

    return false;

    }

    d4583178dcdf15d176fbf99c353fdb60.png

    若子容器的onTouchEvent返回false,onClick方法也是不能够执行的,因为回调方法onClick()是在onTouchEvent中执行的

    总结

    bbc8c5f79290099a7ebf7ece631ec017.png

    展开全文
  • android点击事件分发

    2016-03-19 00:07:50
    android点击事件分发,...1、当他们全部返回默认false的时候,当你按下的时候依次是activity分发事件->父View分发事件->父View拦截事件->子View分发事件->子View拦截事件->子View消费事件->父View消费事件->activity

    android的点击事件分发,拦截,消费,即dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent。


    1、当他们全部返回默认false的时候,当你按下的时候依次是activity分发事件->父View分发事件->父View拦截事件->子View分发事件->子View拦截事件->子View消费事件->父View消费事件->activity消费事件

    当手指移动或是放开的时候一直都是activity分发事件->activity消费事件。

    2、如果父View的dispatchTouchEvent返回为true,则一直都是activity分发事件->父View分发事件。
    3、如果父View的onInterceptTouchEvent返回true,则按下时候一直都是activity分发事件->父View分发事件->父View拦截事件->父View消费事件->Activity消费事件。

    当手指移动或是放开的时候一直都是activity分发事件->activity消费事件。

    4、如果父View的onTouchEvent返回true,当你按下的时候依次是activity分发事件->父View分发事件->父View拦截事件->子View分发事件->子View拦截事件->子View消费事件->父View消费事件。

    当手指移动或是放开的时候一直都是activity分发事件->父View的分发事件->父View的消费事件。


    5、如果子View的dispatchTouchEvent返回为true,则一直都是activity分发事件->父View分发事件->父View的拦截事件->-子View的分发事件。

    6、如果子View的onInterceptTouchEvent返回true,则按下时候一直都是activity分发事件->父View分发事件->父View拦截事件->父View消费事件->Activity消费事件。

    当手指移动或是放开的时候一直都是activity分发事件->activity消费事件。

    7、如果子View的onTouchEvent返回true,当你按下的时候依次是activity分发事件->父View分发事件->父View拦截事件->子View分发事件->子View拦截事件->子View消费事件。

    当手指移动或是放开的时候一直都是activity分发事件->父View的分发事件->父View的拦截事件->子的分发->子的消费。


    由此得出规律:

    1,点击事件由外到里分发,消费事件由里向外消费。

    2.点击事件一直会往里传,但是按下以外的操作,在最先onTouchEvent返回为true的那层地方消费。

    展开全文
  • [android][Android点击事件分发机制分析]
  • Android点击事件分发流程

    千次阅读 2017-11-18 11:16:21
    首先对Android点击事件分发流程进行一个大概的总结,然后再贴出伪代码进行分析。一、当一个点击事件发生时,调用顺序如下:1.事件最先传到Activity的dispatchTouchEvent()进行事件分发;2.调用Window类实现类...

    首先对Android点击事件分发流程进行一个大概的总结,然后再贴出伪代码进行分析。

    一、当一个点击事件发生时,调用顺序如下:

    1.事件最先传到Activity的dispatchTouchEvent()进行事件分发;
    2.调用Window类实现类PhoneWindow的superDispatchTouchEvent();
    3.调用DecorView的superDispatchTouchEvent();
    4.最终调用DecorView父类的dispatchTouchEvent(),即ViewGroup的dispatchTouchEvent();
    5.ViewGroup的dispatchTouchEvent()根据情况调用View的dispatchTouchEvent;
    6.如果DecorView的dispatchTouchEvent()返回true就不执行Activity的onTouchEvent()方法;如果返回false,就执行Activity的onTouchEvent()方法。
    所以,先从Acitivity的dispatchTouchEvent()方法看起,就能把整个流程串联起来。
    二、ViewGroup的onInterceptTouchEvent方法,该方法一旦返回一次true,以后就再也不会被调用了。该事件列的其他事件(Move、Up)将直接传递给该ViewGroup的onTouchEvent()方法。

    三、前提:ViewGroup包含一个View,而且假设ViewGroup没有拦截DOWN事件,View 处理了DOWN事件(即DOWN事件传递到View的onTouchEvent方法,返回了true),但ViewGroup半路拦截了接下来的MOVE事件。

    那么:在这后续到来的第一个MOVE事件中,ViewGroup的onInterceptTouchEvent方法返回true拦截该MOVE事件,但该事件并没有传递给ViewGroup的onTouchEvent方法;这个MOVE事件将会被系统变成一个CANCEL事件传递给View的onTouchEvent方法。后续又来了一个或几个MOVE或者UP事件,(不会再传递给ViewGroup的onInterceptTouchEvent方法),该MOVE、UP事件才会直接传递给ViewGroup的onTouchEvent()进行处理,View再也不会收到该事件列产生的后续事件。

    所以自定义控件最好考虑一下CANCEL事件,可能与UP处理逻辑一致,不然滑动嵌套复杂时可能有隐藏的bug。

    四、参考文章:
    1.http://www.jianshu.com/p/38015afcdb58

    2.http://blog.csdn.net/guolin_blog/article/details/9097463


    下面进入伪代码分析阶段,请注意参考上面第(一)条总结的流程:

    一、Activity

    public class Activity {
        /**
         * Called to process touch screen events.  You can override this to
         * intercept all touch screen events before they are dispatched to the
         * window.  Be sure to call this implementation for touch screen events
         * that should be handled normally.
         *
         * @param ev The touch screen event.
         * @return boolean Return true if this event was consumed.
         */
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                onUserInteraction();
            }
            if (getWindow().superDispatchTouchEvent(ev)) {
                return true;
            }
            return onTouchEvent(ev);
        }
        /**
         * Called whenever a key, touch, or trackball event is dispatched to the
         * activity.  Implement this method if you wish to know that the user has
         * interacted with the device in some way while your activity is running.
         * This callback and {@link #onUserLeaveHint} are intended to help
         * activities manage status bar notifications intelligently; specifically,
         * for helping activities determine the proper time to cancel a notfication.
         *
         * <p>All calls to your activity's {@link #onUserLeaveHint} callback will
         * be accompanied by calls to {@link #onUserInteraction}.  This
         * ensures that your activity will be told of relevant user activity such
         * as pulling down the notification pane and touching an item there.
         *
         * <p>Note that this callback will be invoked for the touch down action
         * that begins a touch gesture, but may not be invoked for the touch-moved
         * and touch-up actions that follow.
         *
         * @see #onUserLeaveHint()
         */
        //该方法为空方法
        //从注释得知:当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法
        //所以onUserInteraction()主要用于屏保
        public void onUserInteraction() {
        }
        /**
         * Called when a touch screen event was not handled by any of the views
         * under it.  This is most useful to process touch events that happen
         * outside of your window bounds, where there is no view to receive it.
         *
         * @param event The touch screen event being processed.
         *
         * @return Return true if you have consumed the event, false if you haven't.
         * The default implementation always returns false.
         */
        public boolean onTouchEvent(MotionEvent event) {
            if (mWindow.shouldCloseOnTouch(this, event)) {
                finish();
                return true;
            }
            return false;
        }
    }

    二、PhoneWindow 

    public class PhoneWindow extends Window {
        @Override
        public boolean superDispatchTouchEvent(MotionEvent event) {
            return mDecor.superDispatchTouchEvent(event);
           //mDecor是DecorView的实例
           // DecorView是视图的顶层view,继承自FrameLayout(即ViewGroup),是所有界面的父布局
        }
    }

    三、DecorView 

    public class DecorView extends FrameLayout {
        public boolean superDispatchTouchEvent(MotionEvent event) {
            return super.dispatchTouchEvent(event);
            //DecorView继承自FrameLayout
            //那么它的父类就是ViewGroup
            //而super.dispatchTouchEvent(event)方法,其实就应该是ViewGroup的dispatchTouchEvent()
        }
    }

    四、ViewGroup

    /**
     *5.0代码有变更,但是基本思路差不多,本代码是4.x的
     */
    public class ViewGroup extends View {
        //重写了view的dispatchTouchEvent()方法
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.action == MotionEvent.ACTION_DOWN) {
                if (mMotionTarget != null) {
                    mMotionTarget = null;
                }
                //disallowIntercept默认是false,受子view控制,代表是否启用拦截onInterceptTouchEvent(),

                //(android开发艺术探索中提出的内部拦截法用到了此标志位)
                //onInterceptTouchEvent(ev)默认实现是false,就是不拦截事件,取反,进入for循环,事件分发给子view
                if (disallowIntercept || !onInterceptTouchEvent(ev)) {
                    for (View child : childrenViews) {
                        //如果点击事件位于该子view区域
                        if (child.contains(ev.getX(), ev.getY())) {
                            if (child.dispatchTouchEvent(ev)) {
                                mMotionTarget = child;
                                return true;
                            }
                        }
                    }
                }
            }
            //如果没有子view消耗事件,或者点击了没有子view的空白处
            //就会走到这里,从而通过super.dispatchTouchEvent(ev)调用到View类的onTouchEvent(ev)方法

            //所以,如果重写onInterceptTouchEvent(ev),决定某条件下拦截点击事件,那么就要重写View的onTouchEvent(ev)方法进行

            //点击事件的相关操作。ViewGroup类本身没有onTouchEvent(ev)方法,是继承自View的。
            if (mMotionTarget == null) {
                return super.dispatchTouchEvent(ev);
            }
            return true;
        }

        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return false;
        }
    }
    注意:
            .Rect frame=new Rect();
            child.getHitRect(frame);
            frame.contains(x,y);
            可以用如上代码判断某个坐标点、某个点击事件是否在某个控件的区域内。

    五、View

    public class View {
        public boolean dispatchTouchEvent(MotionEvent event) {
            //一般ENABLED都是true
            //onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,onTouchEvent将不会再执行。

            //不可用view,即DISENABLED的view是不能响应mOnTouchListener事件的(也不响应onClickListener)
            if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                    mOnTouchListener.onTouch(this, event)) {
                return true;
            }

            //如果mOnTouchListener.onTouch()返回false,或者是ENABLED == false,才会走View的onTouchEvent(event)方法,

            //并且View的onClickListener是在onTouchEvent(event)中被执行的。
            return onTouchEvent(event);
        }
        //该onTouchEvent(event)方法是view的默认行为,可以通过重写该方法进行自定义处理逻辑。

        //onClick的调用是在onTouchEvent(event)方法中的
        public boolean onTouchEvent(MotionEvent event) {

    if ((viewFlags & ENABLED_MASK) == DISABLED) { // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. //通过上面英文注释可知,如果该view是 clickable或者longclickble,那么即使该View是DISABLED的,仍然消费事件, //只是消费该事件,并不做具体处理。 //再仔细分析上面的if条件和下面的return逻辑可知: //1.如果view是DISABLED,并且clickable或者longclickble,那么view将消费该事件,只是消费该事件,
                //并不做具体处理,不响应onClickListener(也不响应mOnTouchListener);
                //2.如果view是DISABLED,并且不能clickable或者不能longclickble,那么那么view将不消费该事件,不响应onClickListener(也不响应mOnTouchListener);
                //3.如果view是DISABLED,并且不管能不能clickable或者能不能longclickble,都将不处理点击事件对应的逻辑,
                //  但是分情况决定是否消费该事件,不响应onClickListener()(也不响应mOnTouchListener)。
                return (((viewFlags & CLICKABLE) == CLICKABLE ||  
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));  
         } 
            //如果该view是可clickble或者longclickble,(并且DISABLED == false)才能进入下面的switch判断
            if (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_UP:
                        performClick();
                        break;
                    case MotionEvent.ACTION_DOWN:
                         ...
                        break;
                    case MotionEvent.ACTION_CANCEL:
                        ....
                        break;
                    case MotionEvent.ACTION_MOVE:
                        ...
                        break;
                }
                //clickblez或者longclickble进入switch后,无条件返回true,即消费事件,不管performClick()中返回的是true还是false
                return true;       ---------- 代码第一处
            }
            //如果该view不是clickble或者不是longclickble的,就返回false,就不消费事件
            return false;
        }
        public boolean performClick() {
            if (mOnClickListener != null) {
                playSoundEffect(SoundEffectConstants.CLICK);
                mOnClickListener.onClick(this);
                return true;
            }
            //如果该view没有设置mOnClickListener,本处返回false,但是该view的onTouchEvent()返回值仍然是true,参考代码第一处
            return false;
        }
        public void setOnClickListener(OnClickListener l) {
            if (!isClickable()) {
                setClickable(true);
            }
            mOnClickListener = l;
        }
    }

    这样,将上述伪代码从头到尾逐步分析一遍,点击事件的分发流程就基本清楚了。

    需要注意的是,该流程是系统view和viewgroup默认的处理方式,总体上dispatchTouchEvent()、onInterceptTouchEvent()、 onTouchEvent()方法的调用流程是不能改变的。但是在具体处理点击事件的逻辑时,可以通过重写ViewGroup的onInterceptTouchEvent()方法,来改变系统默认的事件拦截逻辑,比如决定什么条件下拦截事件,什么条件下不拦截事件;可以通过重写view的onTouchEvent()方法,来改变系统默认的事件相关处理逻辑,比如disable情况下怎么处理,是否响应或者什么条件下响应onTouchListener(或者onClickListner)等等。

    ViewGroup的onTouchEvent()方法,在拦截事件自己处理的时候被执行;也可能是因为子view没有处理事件,子view的dispatchTouchEvent()返回false的时候会执行。

    理论上dispatchTouchEvent()这个方法也是可以被重写的,因为它是public并且不是final方法,只不过一般不建议重写该方法,因为这样处理起来会很复杂,只根据条件重写ViewGroup的onInterceptTouchEvent()或者onTouchEvent()方法即可。

    另外还有两张图片进行总结,图片是从http://www.jianshu.com/p/38015afcdb58网站拷贝出来的,感谢图片原创作者。



    好了,android事件分发流程基本讲完了。


    展开全文
  • Android开发中我们可能会遇到这样的问题,onTouch与onTouchEvent的...等等这一系列的问题都与Android事件分发机制有关,现在我们来慢慢剖析Android事件分发原理。先提俩个问题:事件是怎么传递的?事件又是如...
  • Android的事件处理过程中,经常会遇到需要拦截事件或者手动分发事件的情况,所以简单的了解一下Android的事件分发处理机制还是很有必要的。接下来, 借助一个简单的UI布局说明一下android的事件分发处理的工作流程...
  • 从Listview与Button点击事件冲突看安卓点击事件分发机制 时间2014-05-22 12:30:55 CSDN博客 相似文章 (0)原文 http://blog.csdn.net/hhysirius/article/details/26575145题目有点长。其实实现Listview的时候...
  • Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroup 及其子类、...
  • Android事件分发机制 详解攻略,您值得拥有

    万次阅读 多人点赞 2017-01-06 11:43:05
    Android事件分发机制是Android开发者必须了解的基础 网上有大量关于Android事件分发机制的文章,但存在一些问题:内容不全、思路不清晰、无源码分析、简单问题复杂化等等 今天,我将全面总结Android事件分发机制,...
  • 这篇博文是在阅读了《Android开发艺术探索》(任玉刚),并在自己写了一个相关控件后进行总结的目的的博文,本文主要介绍自己从书中学到的Android点击事件分发拦截相关知识点以作记录。 一、点击事件的流向 点击...
  • 解锁Android点击事件分发机制

    千次阅读 2017-12-19 15:06:43
    开头说说初衷网上关于点击事件分发的文章一搜一大堆,标题一看,不是“30分钟让你弄明白XXX”就是“这是讲解XXX最好的文章”,满怀憧憬与信心,忍不住兴奋的点进去一看,发现不是代码就全是图,我基本上看完了所有...
  • 点击事件由Activity从高到低(从父View到子View)传到最内层View,得到点击事件的view可决定是自己消耗掉该事件,还是继续传给子View(如果有的话)。 主要的方法有三个  public boolean dispatchTouchEvent...
  • 首先进入Activity的dispatchTouchEvent方法。默认会调用userInteraction方法,这里你可以重写...若有,返回true把事件分发给下层view没有调用onTouchEvent方法。 public boolean dispatchTouchEvent(MotionEvent ev) {
  • Activity onClick + | + Window | | +ViewGroup.dispatchTouchEvent + | + onInterceptTouchE...
  • Listview与Button点击事件冲突的菜鸟式解决方法
  • 补充一下,如果最前层的view设置了setOnClickListener,那么事件也无法传回父布局的onTouchEvent里
  • Android 安卓事件分发

    2018-07-13 18:29:26
    开发的过程中,会遇到很多很多的滑动冲突,当然,我们解决滑动冲突就会用到事件分发,由于网上的例子较多,你可能直接找一个工具类而不去考虑是怎么解决的,所以面试的时候一脸蒙蔽,在这里我也为大家讲解一下。...
  • android View点击事件分发流程

    千次阅读 2018-09-10 23:19:43
    android View事件分发流程 android 中的view虽然不是四大组件,但是同样也是相当重要的。不论是我们在平时自定义控件还是面试的时候总会遇到一些关于view点击事件分发的一些问题。接下来就让我给大家分享一下关于...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 29,570
精华内容 11,828
关键字:

安卓点击事件分发