美文网首页
事件分发机制

事件分发机制

作者: 夜沐下的星雨 | 来源:发表于2020-06-06 10:25 被阅读0次

    1.简介

    事件分发是我们触摸屏幕产生的一系列事件,系统接收到这些事件后怎么去处理这些事件的逻辑,叫事件分发。事件分发的本质就是事件(MotionEvent)传递到某个具体的View & 处理的整个过程

    事件分发的对象是谁?点击事件(Touch事件)
    定义:当用户触摸屏幕时(View 或 ViewGroup派生的控件),将产生点击事件(Touch事件)
    Touch事件的相关细节(发生触摸的位置、时间等)被封装成MotionEvent对象

    2. 事件分类

    事件类型.PNG
    特别说明:从手指接触屏幕 至 手指离开屏幕,这个过程产生的一系列事件
    一般情况下,事件列都是以DOWN事件开始、UP事件结束,中间有无数的MOVE事件,如下图:
    事件.PNG

    3.涉及对象和方法

    事件的分发涉及三个对象

    Activity ViewGroup View

    当有一个点击事件,从actvity 传到viewGroup,再传到view层级,有外向内,层层传递。

    类型.PNG

    Activity 作为原始的事件分发者,如果 Activity 拦截了事件会导致整个屏幕都无法响应事件。因此一般不做处理。

    ViewGroup 有点特殊,它多了一个事件拦截的方法onInterceptTouchEvent,它的返回值确定了是调用子View的 dispatchTouchEvent还是自身的 onTouchEvent。如果没有拦截就交给子类view处理。如果拦截,就交给自身的onTouchEvent()处理;

    View :作为事件传递的最末端,要么消费掉事件,要么不处理进行回传。view的调度顺序为 onTouchListener > onTouchEvent > onLongClickListener >onClickListener

    事件分发主要涉及三个方法:

    dispatchTouchEvent-事件分发;在三个对象都有这个方法
    onInterceptTouchEvent 事件拦截;只有在viewgroup 里才有事件的拦截
    onTouchEvent-事件消费。在三个对象都有这个方法

    事件方法.PNG

    4.事件分发流程

    事件流程.PNG
    事件分发.PNG

    上图分析

    1. Activity
      dispatchTouchEvent(MotionEvent ev) -- 事件分发
      ①返回true 直接消费掉 ,没有分发;
      ②返回false,不消费,但是触摸事件不做处理;
      ③只有返回super.dispatchTouchEvent(ev)事件才会向子view分发,其实是调用了PhoneWindow的superDispatchTouchEvent(),进而调用了DecorView的superDispatchTouchEvent,里面又调用了super.dispatchTouchEvent(),而DecorView是一个FrameLayout
      onTouchEvent(MotionEvent event) --事件处理
      ①返回false和默认(super.onTouchEvent(event)),不做处理;
      ②返回true,消费事件
    1. ViewGroup
      dispatchTouchEvent(MotionEvent ev) -- 事件分发
      ①返回true 事件被消费了, 事件未分发
      ②返回false 事件未分发,事件传递到父容器的onTouchEvent()
      ③只有返回super.dispatchTouchEvent(ev),事件才会向下走,里面会调用onInterceptTouchEvent()
      onInterceptTouchEvent(MotionEvent ev) -- 事件拦截
      ①返回true,事件拦截,调用自己的onTouchEvent()
      ②返回false,事件未拦截,分发给了子view
      ③super.onInterceptTouchEvent(ev)与返回false效果一样
      onTouchEvent(MotionEvent event) --事件处理
      ①返回false和super.onTouchEvent(event)事件不消费,事件传递给父容器
      ②返回true,事件会被消费
    1. View
      dispatchTouchEvent(MotionEvent ev) -- 事件分发
      因为它最小,没有子view,所以这个方法其实没有进行分发的能力
      ①返回true,事件被消费,
      ②返回false ,事件传递到父容器的onTouchEvent()
      ③只有返回super.dispatchTouchEvent(ev),事件才会向下走,里面会调用自己 onTouchEvent()
      onTouchEvent(MotionEvent event) --事件处理
      ①返回false和super.onTouchEvent(event)事件不消费,事件传递给父容器
      ②返回true,事件会被消费

    5.点击事件触发的位置及拦截

    在onTouchEvent() 方法中MotionEvent.ACTION_UP中会调用performClick(),
    performClick()中会调用onClick()
    拦截点击事件:
    在父容器中重写 onInterceptTouchEvent(MotionEvent ev) ,
    返回true,会走父容器的onTouchEvent()方法

    6.滑动事件冲突

    父容器想获取左右滑动事件,子view想获取垂直滑动事件(如Viewpager中有ListView),都想获取事件,怎么办?
    思路:可以进行选择性的拦截事件,当水平滑动的距离大于垂直滑动的距离时,拦截事件

      @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            //选择性的进行拦截,当水平滑动距离大于垂直滑动距离时 ,拦截
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mDownX = ev.getX();
                    mDownY = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float moveX = ev.getX();
                    float moveY = ev.getY();
                    //水平和垂直的滑动距离(绝对值)
                    float dx = Math.abs(moveX - mDownX);
                    float dy = Math.abs(moveY- mDownY);
                    if (dx>dy){
                        return true;
                    }
                    break;
            }
    
            return false;//不拦截
        }
    

    getX()和getY():由这两个函数获得的x,y值是相对的坐标值,相对于父容器坐标。
    getRawX()和getRawY():有这两个函数获得的x,y值是绝对坐标,是相对于屏幕的。

    坐标.PNG

    相关文章

      网友评论

          本文标题:事件分发机制

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