安卓事件分发

在安卓开发的时候 我们点击一个按钮 一般会做出相应事件,我们感觉理所当然 ,但是为毛这样呢 为啥它会响应 它怎么知道我点击了它呢,好了,那么今天我们就进行学习一下(分析源码哦)

一、什么是事件

首先要了解 当我们点击屏幕的时候 系统会把我们点击的这个动作封装成一个点击事件MotionEvent 。

  • ACTION_DOWN:手指刚接触屏幕 (只有一个)
  • ACTION_MOVE:手指在屏幕上滑动(0个或n个)
  • ACTION_UP:手指从屏幕上松开的一瞬间(只有一个,cancel情况下可能没有)

好了上面就是我们的MotionEvent 简述,约简单越好 毕竟主攻事件分发呐亲 ~

 

二、事件分发谁来分发、需要哪些方法

2、事件分发 其实说到就是点击事件 Activity -->GroupView-->View 的事件分发。既然是分发肯定有一系列的方法,那么下面请大家记住这个三个关键方法!

  • dispatchTouchEvent:进行事件分发的方法(activity  groupView  View 都有该函数)
  • onInterceptTouchEvent:事件拦截的方法(groupView 才有哦 为啥呢,个人感觉 activity没有 是因为它本来就是分发的,还拦截干嘛,view 已经是分发的底层了,更不需要拦截了)
  • onTouchEvent:进行事件的处理(activity  groupView  View 都有该函数)

三、源码分析

1、activity的事件分发


  public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction(); //标记1
        }
        if (getWindow().superDispatchTouchEvent(ev)) //标记2 {
            return true; 
        }
        return onTouchEvent(ev); //标记3
    }


    ...标记1:我们点开看看:

     public void onUserInteraction() {}

     咦,空方法,其实这是实现屏保的地方,和我们今天要说的事件分发关系不大 那好吧继续往下看



    ...标记2:getWindow().superDispatchTouchEvent(ev),

    getWindow()是获取了Window对象,
     public Window getWindow() {
        return mWindow;
    }

    我们看到MotionEvent 传递到superDispatchTouchEvent()这个方法里面了 那我们点开看看,(进入
    Window类)

    public abstract boolean superDispatchTouchEvent(MotionEvent event);


    哎,咋是一个抽象方法啊 这可咋办 这事件传递到哪了,别慌 既然是抽象方法 肯定要实现的地方嗯

    Window唯一的实现类是PhoneWindow

    好进入PhoneWindow

    找到实现方法

     @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

    卧槽又调用了 ,继续进入

    进入DecorView类

    找到实现方法

      public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

    。。。super  调用父类 那我们就看看DecorView的父类是 FrameLayout 咦那它的父类不是ViewGroup吗,

    难道这就是activity传递事件到ViewGroup的交界处是的,嘿嘿,此时activity 事件分发结束

    此时看到标记2当 getWindow().superDispatchTouchEvent(ev) 为真的情况下(其实也就是事件向下传递的时候有控件消费了事件 

    返回true) activity的dispatchTouchEvent方法返回true

    不然就进行标记3,来看看标记3



    ...标记3:onTouchEvent(ev) activity消费事件的方法


   public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }

    无论onTouchEvent 返回什么事件都将被消费 至此activity的事件分发和事件消费结束

2、ViewGroup的事件分发

ViewGrop 的dispatchTouchEvent 中的代码太多 我主要列出一部分代码 分发逻辑不变

    TouchTarget mFirstTouchTarget=null//标记1

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

             boolean handled = false;//标记2

            final boolean intercepted; //标记3
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev); //标记4
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }

            if (!canceled && !intercepted) {//标记5

			  for (int i = childrenCount - 1; i >= 0; i--) { //标记6


                   if (!canViewReceivePointerEvents(child) //标记7
                        || !isTransformedTouchPointInView(x, y, child, null)) {
                          ev.setTargetAccessibilityFocus(false);
                                continue;
                    }

			  if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {//标记8
                     mFirstTouchTarget=被赋值
                }

			  }

            }

            if(mFirstTouchTarget==null){

               handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS); 
            }else{

             if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) { 
                handled = true;
               }

            }

    }


    ...标记1:mFirstTouchTarget 当前是否有子view消费事件的标记

    ...标记2:dispatchTouchEvent 的返回给activity的值 ture 代表事件被消费  flase 事件没消费

    ...标记3:intercepted 是否拦截事件的标记

    ...标记4:onInterceptTouchEvent 咦 好熟悉 这不是ViewGroup特殊的拦截方法吗 

             一般情况下都是默认返回 false 代表不拦截 当我们重写onInterceptTouchEvent 时返回true 
             那么代码ViewGroup拦截事件 子view就不被分发事件

    ...标记5:判断ViewGroup是否拦截事件

    ...标记6:如果不拦截的情况下进入 这个时候我们遍历子view 

    ...标记7: // 条件1:canViewReceivePointerEvents,当前child是否能够接收到点击事件
              // 条件2:isTransformedTouchPointInView,点击事件的坐标是否落在当前child的区域内
              // 因此,当前child无法接收到事件或者点击事件不在当前child的区域内,就跳过,继续遍历下一个child

    ...标记8:调用 dispatchTransformedTouchEvent()方法,进行事件分发判断 


     那么现在开始看dispatchTransformedTouchEvent()方法的部分源码


    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {
        
        final boolean handled; //标记 9

         if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) { //标记10
                handled = super.dispatchTouchEvent(event);//标记11
            } else {
                handled = child.dispatchTouchEvent(event);//标记12
            }
            event.setAction(oldAction);
            return handled;
        }

        return handled;//标记13
    }


    ...标记9:是否消费的返回值

    ...标记10:child == null 代表拦截事件

    ...标记11:调用 super. dispatchTouchEvent()
              咦super 调用父类 那不是view吗对这个 然后会调用自己的onTouchEvent方法 这个先记住 牵扯到view层的事件分发

    ...标记12:调用子view的dispatchTouchEvent()好了马上进入view的dispatchTouchEvent()

    ...标记13:返回是否被消费的结果

    ok ViewGroup的事件分发结束

2、View的事件分发

View的事件分发简单的多 作为事件分发的结束来一起看看吧 部分代码~

public boolean dispatchTouchEvent(MotionEvent event) {

    boolean result = false;//标记1

 if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {//标记2
                result = true;
            }

            if (!result && onTouchEvent(event)) {//标记3
                result = true;
            }


      return result;      
 }


 ...标记1:是否消费事件的标记

 ...标记2:这个重点说一下 大家应该给某一个控件设置过onTouch事件 也设置过点击事件 当你onTouch中返回true时 
 这个时候你的点击事件就会失效 为啥呢来看看 li.mOnTouchListener.onTouch(this, event) 当他为真也就是返回
 true的时候 我们的view dispatchTouchEvent 返回ture 结束 根本运行不到onTouchEvent 所以点击事件会失效。

 同样view不可点击 view dispatchTouchEvent 返回ture

 这个时候我们可以知道onTocuch事件早于onTouchEvent

 by the way imageview默认不可点击哦 

 ...标记3:顺利进入onTouchEvent方法了 我们进去看看

 public boolean onTouchEvent(MotionEvent event) {

 if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {//标记3
       switch (action) {
            case MotionEvent.ACTION_UP:
                 performClickInternal();//标记4
    }

    return false;

 }

...标记3:点击到了该view

...标记4:手送开手 进入performClick()

    public boolean performClick() {

        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this); //我们熟悉的onClick方法 
            result = true; //注册了点击事件 消费true
        } else {
            result = false;//不消费 false
        }

        return result;
    }


    ok View的事件分发也结束

四、结尾:

补充:onTouch 的各种响应事件都早于onClick方法哦 记住它永远比onClick先执行就对了

大概讲述了安卓的事件分发的整个过程 事件传递的过程 大致就是Activity  -- >ViewGroup -- >View 的事件传递 其中ViewGroup 可以用onInterceptTouchEvent 方法进行事件拦截 ,ok就这样 事件分发大致就这样 ,如果有什么错误的地方或者不懂的地方麻烦留言 拜~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值