android事件传递

事件传递过程  Activity  Window  View

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

onUserInteraction

activity在分发各种事件的时候会调用该方法。  各种事件都会出发这个api  

/**
 * 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.
if (getWindow().superDispatchTouchEvent(ev)) {
    return true;
}

Window的真正实现类是PhoneWindow 其实调用最后执行到decorview的

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

DecorView的dispatchTouchEvent方法

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

最后调用ViewGroup的dispatchTouchEvent方法


所以真正的事件传递就是首先传递给Activity的onTouchEvent方法  随后就是遍历这个界面所有的view看看有没有某个view消费这个事件。


事件传递:View  viewgroup  phonewindow  decorview  activity   


https://www.jianshu.com/p/90648dddd141


ViewGroup  view的事件传递

eg:

//      注意事件在处理的时候  如果事件传递给下面的view没有任何一个处理  
这个时候事件会给顶层 eg activity
//      加入一个view消费这个事件  以后的事件都会交给他处理。
        rela= (MyRelativeLayout) findViewById(R.id.rela);
        tv= (MyTextView) findViewById(R.id.tv);
//      这个方法使用场景没有模拟出来  ?????  原因:这个方法调用位置不对  
应该在dispatchTouchEvent事件里面进行处理
//      这个地方在调用在VG的down事件里面已经恢复了,
        tv.getParent().requestDisallowInterceptTouchEvent(true);


@Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d(TAG, "dispatchTouchEvent: ");
        getParent().requestDisallowInterceptTouchEvent(true);
        return super.dispatchTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent: ");
        return true;
//        return super.onTouchEvent(event);
    }

//    1:  总结  当viewgroup拦截了事件  并且将事件处理了(就是onTouchEvent  TouchListener
 click只要有一个返回值是true 仅仅拦截不处理这个事件
//        还是会传递给父view来处理)
//       这个流程中只有action_down事件会触发Viewgroup的
//    onInterceptTouchEvent   (VG的onInterceptTouchEvent在down事件一定会触发)
//    随后就不会再次出发onInterceptTouchEvent事件   原因如下:想触发这个方法前提是:
actionMasked == MotionEvent.ACTION_DOWN
//    || mFirstTouchTarget != null  down事件 或者mFirstTouchTarget 
(就是事件有VG的子元素处理了 这个字view就会赋值给)
//    mFirstTouchTarget 所以此时这两个条件都不成立
//    2:子view处理事件  这个时候onInterceptTouchEvent每次都会执行


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "dispatchTouchEvent: ");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "onInterceptTouchEvent: ");
//
        if(ev.getAction()==MotionEvent.ACTION_DOWN){
//            return super.onInterceptTouchEvent(ev);
            return false;
        }else{
            return true;
        }
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent: ");
//     return true;
        return super.onTouchEvent(event);
    }


结论性语言

 

dispatchTouchEvent

onTouchEvent

onInterceptTouchEvent

onTouchListener

onClickListener

1:正常情况下  一个时间序列只可以有一个view处理

2:

mFirstTouchTarget

这个就是要处理事件的目标对象,

3:当Vg拦截了down事件之后  以后所有的事件都会有他处理  另外以后再也不会触发onIntercept拦截事件的api

     所以父view一定不可以拦截down事件。否则 子view以后就不可以获取事件。

4:

requestDisallowInterceptTouchEvent

子view调用这个方法将会是父view不能拦截除down之外的其他事件。

5:接下来寻找处理事件的view 过滤条件是1:不执行动画   2:在事件出发的区域内。

6:

dispatchTransformedTouchEvent

这个方法其实就是调用子元素的dispatchevent方法  另外最后VG也是调用View里面的dispatch方法   因为他调用的是super

调用的就是父class的api  所以就是view里面的方法。(归根结底)

onMeasure  onDraw  onLayout全面解析

7:

newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;

赋值获取事件处理的view  child


8:随后就是view里面对事件的处理流程。



9:自己需要再重新核对一下整体流程   梳理一下  自己写写看看

     将事件处理这一块  写几个简单的demo试试。




10:事件传递流程梳理:核心点

     1:onFilterTouchEventForSecurity(ev) 检测一下view是否处于最顶层 是否是被遮盖的

     2:if (actionMasked == MotionEvent.ACTION_DOWN) {

    // Throw away all previous state when starting a new touch gesture.
    // The framework may have dropped the up or cancel event for the previous gesture
    // due to an app switch, ANR, or some other state change.
    cancelAndClearTouchTargets(ev);
    resetTouchState();
    }

         action_down的时候 会强行清除一切状态和变量(所以我们在调用这个api

         view.getParent().requestDisallowInterceptTouchEvent();对down事件是没有任何作用的)

    3:final boolean intercepted;

    if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        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;
    }
    随后执行事件拦截这一块流程  onInterceptTouchEvent  注意条件 MotionEvent.ACTION_DOWN    
    mFirstTouchTarget != null
    (归纳几个点:我们在做时间拦截的时候 VG不可以将down时间拦截了,否则后期一切事件都会被VG处理:原因down事件被拦截
      导致mFirstTouchTarget 不会被复制其他事件过来的时候就直接走else语句 intercepted变量一直会为true,事件就被拦截了) 

 4:两条路:被VG拦截  handled = dispatchTransformedTouchEvent(ev, canceled, null,
        TouchTarget.ALL_POINTER_IDS);
  
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    final boolean handled;

    // Canceling motions is a special case.  We don't need to perform any transformations
    // or filtering.  The important part is the action, not the contents.
    final int oldAction = event.getAction();
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        return handled;
    }
注意此时child为null执行 handled = super.dispatchTouchEvent(event);  这个就是执行VG的父view的api
事件就直接传递给父view处理事件

5:事件传递给子view处理:过滤条件:view没有执行动画 view位置在可点击区域 view处理了这个事件 主意这个地方是一个
递归逻辑 直到找到一个执行事件的view(也可能没有child 执行流程就回到的上面的流程)
找到之后:
 newTouchTarget = addTouchTarget(child, idBitsToAssign);
 alreadyDispatchedToNewTouchTarget = true;
 
private TouchTarget addTouchTarget(View child, int pointerIdBits) {
    TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
    target.next = mFirstTouchTarget;
    mFirstTouchTarget = target;
    return target;
}
给相关变量赋值 mFirstTouchTarget = target; 此次事件应该派遣给某个子view处理
疑问点随后解析:???
11:结论:
VG和VIEW事件处理区别关键就在 dispatchTouchEvent 事件分发流程和VG还多了一个事件拦截 onInterceptTouchEvent不一样,
onTouchEvent OnTouchListener onClickListener 其实最后都是调用的父view的方法
VG是VIEW的子类
  12:View的 dispatchTouchEvent流程分析:

        

  if (onFilterTouchEventForSecurity(event)) {
    //noinspection SimplifiableIfStatement
    ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnTouchListener != null
            && (mViewFlags & ENABLED_MASK) == ENABLED
            && li.mOnTouchListener.onTouch(this, event)) {
        result = true;
    }

    if (!result && onTouchEvent(event)) {
        result = true;
    }
  }
     首先判断 是否调用我们的 OnTouchListener 前提条件是:1:存在这个listener 2:view是enable的

     如果没有listener或者非enable的就执行onTouchEvent

13:view的onTouchEvent分析:

       view的enable状态不影响onTouchEvent的api执行

  CLICKABLE  
LONG_CLICKABLE
为true就可以:
performClick();执行点击事件  
更为详细的分析????14:VG的
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return false;
}

  系统默认都是返回false 父view不拦截事件:


事件传递的原则是:父view如果不主动拦截事件即便自己可以处理事件  也会先将事件传递给子view处理 当自己的子

view都不处理的时候,这个事件才会再次传递给父view 又父veiw来执行。








 



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值