Android View的事件分发

来源:互联网 发布:淘宝上杰胜体育怎么样 编辑:程序博客网 时间:2024/05/16 17:27
  • View的事件分发机制
    • 分析的对象
      • MotionEvent: 表示点击事件, 例如Action_DOWN、Action_UP、Action_MOVE等
    • 事件的传递过程
      • dispatchTouchEvent: 事件分发 (事件能否传递给当前View)
      • onInterceptTouchEvent : 事件拦截(是否拦截事件)
      • onTouchEvent : 处理点击事件

1、 产生一个Action_Down时,即产生了一个点击操作,事件由此开始,其顺序为 Activity -> Window -> View。最开始事件会在Activity的dispatchTouchEvent中进行事件的分发,实际的工作由Activity内部的Window完成。Window是一个抽象类,其唯一实现是PhoneWindow。在PhoneWindow中有一个内部类DecorView,而DecorView可以看成是我们当前界面的底层容器(也是我们setContentView设置的View的父容器)。在这,我们可以发现,事件会经过PhoneWindow -> DecorView传递到了顶级View,即setContentView设置的View。

下面看一张草图,以简单的形式描绘了一下大致的一个传递过程,包含了Activity层,ViewGroup层,View层。ViewGroup层是setContentView设置的View,View层是ViewGroup中的子View。

这里写图片描述

来捋一下上图中大致的流程,图中return super为父类的实现;false表示表示不消费事件;true表示消费事件。
《1》若在Activity层中的dispatchTouchEvent就return true(false),事件将不会再往下传递。
《2》事件传递到ViewGroup层中的dispatchTouchEvent时,若返回false,则事件会回传到父容器Activity层的onTouchEvent中消费。
反之,会传递到onInterceptTouchEvent,其默认为false,即不拦截事件,事件会传递给View层中的dispatchTouchEvent。若返回true时,表示拦截当前事件,直接会在ViewGroup层中的onTouchEvent决定是否消费此事件,返回true为消费,否者不消费,事件回传到父容器中的onTouchEvent。
《3》若View层中的dispatchTouchEvent可以接收事件,则由此再事件分发,若返回false,事件会回传到ViewGroup层中的onTouchEvent决定是否消费,这里再不消费,就会再回传到Activity层中的onTouchEvent消费。反之,再当前View层中消费事件。

注: onInterceptTouchEvent方法只属于ViewGroup。

2、 看一个简单的Demo,自定义两个布局文件,CustomViewR继承于RelativeLayout,CustomViewButton继承于AppCompatButton。实现相应的dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent。并且也在MainActivity重写dispatchTouchEvent和onTouchEvent方法。

MainActivity表示Acitvity层

public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        switch (ev.getAction()){            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "dispatchTouchEvent: ->"+"Activity dispatch DOWN" );                break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "onTouchEvent: ->"+"Activity onTouchEvent DOWN" );                break;        }        return super.onTouchEvent(event);    }}

CustomViewR表示ViewGroup层

public class CustomViewR extends RelativeLayout {//省略构造方法    private static final String TAG = "CustomViewR";    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {       switch (ev.getAction()){           case MotionEvent.ACTION_DOWN:               Log.e(TAG, "dispatchTouchEvent: ->"+"CustomR dispatch DOWN" );              break;       }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()){            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "onInterceptTouchEvent: ->"+"CustomR onIntercept DOWN" );                break;        }        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "onTouchEvent: --->"+"CustomR onTouchEvent DOWN");                break;        }        return super.onTouchEvent(event);    }}

CustomViewButton表示View层

public class CustomViewButton extends AppCompatButton {//省略构造方法    private static final String TAG = "CustomViewButton";    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "dispatchTouchEvent: ->"+"CustomButton dispatch DOWN" );                break;        }        return super.dispatchTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "onTouchEvent: --->"+"CustomButton onTouchEvent DOWN" );               break;        }        return super.onTouchEvent(event);    }}

<1>上述代码中默认返回父类的实现,那么看一下输出Log:

MainActivity: dispatchTouchEvent: ->Activity dispatch DOWNCustomViewR: dispatchTouchEvent: ->CustomR dispatch DOWNCustomViewR: onInterceptTouchEvent: ->CustomR onIntercept DOWNCustomViewButton: dispatchTouchEvent: ->CustomButton dispatch DOWNCustomViewButton: onTouchEvent: --->CustomButton onTouchEvent DOWN

可以看出,当以默认的父类的实现返回时,View的onTouchEvent会默认消耗事件,即返回true。

<2>还是以上述代码为例,当在View层的onTouchEvent中返回false,代码我就不再贴出来,看下输出日志:

MainActivity: dispatchTouchEvent: ->Activity dispatch DOWNCustomViewR: dispatchTouchEvent: ->CustomR dispatch DOWNCustomViewR: onInterceptTouchEvent: ->CustomR onIntercept DOWNCustomViewButton: dispatchTouchEvent: ->CustomButton dispatch DOWNCustomViewButton: onTouchEvent: --->CustomButton onTouchEvent DOWNCustomViewR: onTouchEvent: --->CustomR onTouchEvent DOWNMainActivity: onTouchEvent: ->Activity onTouchEvent DOWN

对比第一次的日志输出,可以明显的看出,后面多输出了两条数据,也就是说,在CustomViewButton中的onTouchEvent返回false,没有消费这次的点击事件,则回传给父容器中的onTouchEvent。

<3>再看下当我们在CustomViewR中的onInterceptTouchEvent中返回true,即拦截了当前事件。Log如下:

MainActivity: dispatchTouchEvent: ->Activity dispatch DOWNCustomViewR: dispatchTouchEvent: ->CustomR dispatch DOWNCustomViewR: onInterceptTouchEvent: ->CustomR onIntercept DOWNCustomViewR: onTouchEvent: --->CustomR onTouchEvent DOWNMainActivity: onTouchEvent: ->Activity onTouchEvent DOWN

上述代码可以看出,当onInterceptTouchEvent拦截了事件后,将不会再传递到下一层CustomViewButton中,而为什么又回传到了Activity 层的
onTouchEvent呢?是因为在ViewGrop层中的onTouchEvent 默认返回false,所以不消耗事件,把事件回传给了父容器。

总结:这一次View事件分发就写到这,结合图和代码的展示,相信这样看会看的比较明了。这里虽然只写了一个单一的Action_DOWN事件,但是View的分发机制中,一个事件产生后都会遵循刚才说的规则传递:Activity -> Window -> View
注: 一个事件的完成,表示从手指点击屏幕开始,直到离开屏幕的过程,即Action_DOWN开始,中间可能会有很多个Action_MOVE,以Action_UP结束。

原创粉丝点击