• Android MotionEvent事故响应机制


    于android于。主要活动包括点击、按、拖累、滑动等操作,这些构成了Android事件响应,总体而言,,所有事件由例如以下三部分构成的基础:

    按(action_down),搬家(action_move),抬起(action_up)。View以及ViewGroup的,这两者中响应的方法分别有:

    View.java中:

    publi boolean dispatchTouchEvent(MotionEvent event)
    public boolean onTouchEvent(MotionEvent event)

    ViewGroup.java中

    public boolean dispatchTouchEvent(MotionEvent event)
    public boolean onTouchEvent(MotionEvent event) 
    public boolean onInterceptTouchEvent(MotionEvent event)

    在组件嵌套的情况下,对于事件的响应处理会从最顶层的组件不断向子组件传递,一直到最后的View组件。

    能够看到ViewGroup中比View中多出了一个onInterceptTouchEvent方法。这是由于ViewGroup组件能够存在子组件,因此须要通过Intercept推断是否将该事件传递到子组件中。

    函数的详细功能例如以下:

    onTouchEvent是真正用来进行业务逻辑处理的地方,返回true表示已经将该事件消费。返回false表明事件继续传递。

    onInterceptTouchEvent是用来进行推断是否须要对事件进行拦截从而阻止其继续往子组件传递的,返回false表示无需拦截,则递归的调用子组件的dispatchTouchEvent方法;返回true表示须要拦截,则直接调用本组件的onTouchEvent方法进行处理。

    以上两个的功能相对好理解一些,最基本的是第三个。之前在网上看了非常多。可是都没有讲的特别清楚的。

    大都说是用于事件分发的,返回true表示不继续分发,返回false表示继续分发。可是一直没讲明确这个跟採用onInterceptTouchEvent的差别在哪里。。

    直接点说,Android对于touch事件的处理是通过递归来进行的。而这样的递归就体如今dispatchTouchEvent上。以上所写的两个函数就是在dispatchTouchEvent中被调用而且运行从而实现其分发的业务逻辑的。

    在dispatchTouchEvent中有可能会调用三个方法:

    1、本组件的onInterceptTouchEvent

    2、子组件的dispatchTouchEvent

    3、本组件的onTouchEvent

    ViewGroup中dispatchTouchEvent()详细的运行逻辑:

    1、首先运行本组件的onInterceptTouchEvent。假设返回false,表明无需拦截。则调用第二个方法。即子组件的dispatchTouchEvent方法;假设返回true,无需向子组件传递。则直接调用本组件的onTouchEvent方法

    2、第一步中假设须要向子组件传递事件。假设递归调用子组件的dispatchTouchEvent返回false,则调用本组件的onTouchEvent方法。假设返回true,则无需调用本组件的onTouchEvent方法

    3、依据前两步的运行结果,将该dispatchTouchEvent的返回值返回给父组件的dispatchTouchEvent方法。


    view中的dispatchTouchEvent会直接调用其自身的onTouchEvent。



    一般没有必要重写dispatchTouchEvent方法,假设一定要重写。请注意调用super.dispatchTouchEvent()方法。否则递归调用到此处即停止。

    在不考虑dispatchTouchEvent的情况下,简单的运行流程是这种:

    最顶层的组件首先响应事件,然后不断向子组件进行传递,调用子组件的onInterceptTouchEvent方法,一直到某个组件A的onInterceptTouchEvent方法返回true或者到达了view组件,然后调用该组件的onTouchEvent方法,之后不断向父组件进行返回,调用父组件的onTouchEvent直到某个父组件的onTouchEvent方法返回true。

    事实上就是个首先从父组件不断向下调用onInterceptTouchEvent,然后从子组件不断向上调用onTouchEvent的过程。

    还须要注意的:

    1、假如这个过程中某个组件截获并处理了ACTION_DOWN事件。则之后对应的ACTION_MOVE、ACTION_UP等其它事件将不再会被传递到他的子孙组件,而是传递到该组件后就运行返回的流程。

    2、假设某个组件的onInterceptTouchEvent对ACTION_DOWN返回true。则之后的ACTION_MOVE,ACTION_DOWN等将不会再运行本onInterceptTouchEvent。而是直接传递给本组件的onTouchEvent,可是依旧会经过其父组件的onInterceptTouchEvent。


    过程中看到有篇文章里对dispatchTouchEvent的代码进行了凝视,能够更清楚的说明问题。详细例如以下。引用地址为:http://blog.csdn.net/hdxiaoyu2/article/details/25563453

     @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (mInputEventConsistencyVerifier != null) {
                mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
            }
    
    
            // Check for interception.
            final boolean intercepted;//拦截的标记变量
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                
                    //调用自身的 onInterceptTochEvent。推断是否须要拦截
                    intercepted = onInterceptTochEvent(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;
            }
    
    
            
            //假如没拦截
            if (!canceled && !intercepted) {
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
    
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        final View[] children = mChildren;
    
                        final boolean customOrder = isChildrenDrawingOrderEnabled();
                        //遍历全部的子View,而且调用他们的事件分发方法dispatchTouchEvent()
    
                        for (int i = childrenCount - 1; i >= 0; i--) {                      final int childIndex = customOrder ?

    getChildDrawingOrder(childrenCount, i) : i; final View child = children[childIndex]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { continue; } newTouchTarget = getTouchTarget(child); //newTouchTarget表示事件传递的View目标。当不为空的时候,直接跳出循环 if (newTouchTarget != null) { newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); //递归调用子View分发事件方法, if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); //设置分发目标newTouchTarget为当前View newTouchTarget = addTouchTarget(child, idBitsToAssign); //标记子View的分发结果,为True的话。以下的代码是不会调用当前View的onTouch方法的,也就是规则1生成的原因 alreadyDispatchedToNewTouchTarget = true; break; } } } } } if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { // Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. Cancel touch targets if necessary. TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; //假设刚才alreadyDispatchedToNewTouchTarget设为True的话。就不运行以下的dispatchTransformedTouchEvent //alreadyDispatchedToNewTouchTarget是由子View的onTouch返回值决定的, if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; //运行自身的Touch事件, if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } }


    另外还看到两篇将的不错的。能够作为參考:

    http://www.infoq.com/cn/articles/android-event-delivery-mechanism

    http://blog.csdn.net/hdxiaoyu2/article/details/25563453

    http://yizhi401.blog.51cto.com/6500239/1364958

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    从输入网址到显示网页简介
    java异常及日志注意事项
    java-Excel导出中的坑
    python-arcade时钟
    tkinter-clock实例
    http远程调用原生get、post模板
    eclipse使用小技巧
    sublime text3安装Package Control和Vue Syntax Highlight
    双十一剁手了吗
    Nginx配置HTTPS
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4834300.html
Copyright © 2020-2023  润新知