Android相关事件分发

事件分发

2019-07-15  本文已影响141人  Anwfly

知识要点:

1.dispatchTouchEvent(MotionEvent ev)

2.onInterceptTouchEvent(MotionEvent ev)

3.onTouchEvent(MotionEvent ev)

目标要求:

掌握事件分发机制

一. 基础认知

  1. 事件分发的对象是谁?点击事件(Touch事件)
事件类型 具体动作
MotionEvent.ACTION_DOWN 按下View(所有事件的开始)
MotionEvent.ACTION_UP 抬起View(与DOWN对应)
MotionEvent.ACTION_MOVE 滑动View
MotionEvent.ACTION_CANCEL 结束事件(非人为原因)

特别说明:从手指接触屏幕 至 手指离开屏幕,这个过程产生的一系列事件

注:一般情况下,事件列都是以DOWN事件开始、UP事件结束,中间有无数的MOVE事件,如下图:
0.5290021690015503.png
  1. 事件分发的本质
    将点击事件(MotionEvent)传递到某个具体的View & 处理的整个过程
    即 事件传递的过程 = 分发过程。

  2. 事件在哪些对象之间进行传递?
    Activity、ViewGroup、View
    Android的UI界面由Activity、ViewGroup、View 及其派生类组成


    0.3402589943500538.png
  3. 事件分发的顺序
    即 事件传递的顺序:Activity -> ViewGroup -> View

  4. 事件分发过程由哪些方法协作完成?


    0.3574262937594854.png

二.分发流程

944365-aea821bbb613c195.png
Touch事件相关方法 Activity ViewGroup View
dispatchTouchEvent(MotionEvent ev)
onInterceptTouchEvent(MotionEvent ev) 没有 没有
onTouchEvent(MotionEvent event)
  1. Activity
  1. ViewGroup
  1. View

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

四.setOnTouchListener()和onTouchEvent()关系
源码如下:

public boolean dispatchTouchEvent(MotionEvent event) {
      ...
        if (onFilterTouchEventForSecurity(event)) {
          
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        ...
        return result;
    }

第7行会调用OnTouchListener.onTouch(this, event),在第11行,才会去调用onTouchEvent(event),所以setOnTouchListener的onTouch()方法先被调用,如果onTouch()方法返回true,事件被消费了,则onTouchEvent()方法不会调用.

五.滑动事件冲突

父容器想获取左右滑动事件,子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;//不拦截
    }
上一篇下一篇

猜你喜欢

热点阅读