Android基础之View的事件传递及分发机制

当我们点击一个View,点击的动作是怎么传递到当前View的,系统会做哪些处理呢?我们在处理滑动冲突的时候,从哪里下手,毫无头绪;我们先来看下事件的传递及分发机制,系统是如何传递事件,分发事件的,给我们处理相关问题提供基础知识
先了解下Activity的层级结构,便于更好的理解事件的传递顺序;

要点总结:


触摸事件有一个down,多个move,一个up组成;


事件的传递是从Activity开始的,Activity -->PhoneWindow–>DectorView–>ViewGroup–>View;主要操作在ViewGroup和View中;


ViewGroup类主要调用:dispatchTouchEvent()–>onInterceptTouchEnent()–>dispatchTransformedTouchEvent();ViewGroup不直接调用onTouchEvent()方法;

 

dispatchTouchEvent()方法,是这个事件分发的核心,解决滑动冲突主要在这个方法中操作;


onInterceptTouchEnent()方法默认情况下,直接返回false,父View需要拦截事件,重写该方法,返回true;如果被拦截了,down不会传递到子View;只要某个Action被拦截了,从这个action开始及其后的action都不会再传递到子View中了;


ViewGroup可以调用requestDisallowInterceptTouchEvent(boolean disallowIntercept) 默认情况下是允许父View拦截的;如果disallowIntercept=true,父View将无法拦截事件的传递;


如果ViewGroup没有子View,或者子View都不消费事件,或者父View拦截了事件,会将ViewGroup当做普通View处理;看ViewGroup自身是否处理事件;


View类主要调用:dispatchTouchEvent()–>onTouchEvent()–>performClick();


如果View重写了OnTouchListener的onTouch()方法,并且返回了true,则onTouchEvent()方法不会再执行了;由此可见onTouch()方法的优先级高于onTouchEvent();


如果View重写了onTouchEvent()方法,不调用super.onTouchEvent(),则onClick()方法将不执行;

执行优先级:OnTouchListener的onTouch() > onTouchEvent() > onClick();

disable的View只要是可点击的,也可以消费touch 事件,但是无法响应;


View是clickable的,就可以消费该事件;onClickListener,在接受up之后,才执行;onClickListener在onTouchEvent方法中被执行;如果View重写了onTouchEvent方法,不调用super.onTouchEvent方法onClick()方法不会执行;


事件的传递顺序:

1)down 从最外层的View往里层的子View传递;如果最里层的View不消费事件,在将事件往父View上抛;
2)把父View当做普通View看待(不在当做ViewGroup来看),如果当前的View也不消费该事件,继续往父View抛;
3)重复步骤2),整个View的传递和分发过程,呈现U型;


如果down已经找到target view,move和up都通过层层传递直接到target view;不再经过遍历获取target view了;

流程分析
Window类,PhoneWindow是Window的具体实现类;
public abstract boolean superDispatchTouchEvent(MotionEvent event);
1
Window类的Callback的接口回调;Activity实现了该接口;
/**
 * API from a Window back to its caller.  This allows the client to
 * intercept key dispatching, panels and menus, etc.
 */
public interface Callback {
    /**
     * Called to process touch screen events.  At the very least your
     * implementation must call
     * {@link android.view.Window#superDispatchTouchEvent} to do the
     * standard touch screen processing.
     *
     * @param event The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent event);
}
1234567891011121314151617
Activity的dispatchTouchEvent()方法
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();//空方法
    }
    //调用Window的superDispatchTouchEvent()方法,如果事件已经被具体的View处理了;返回true;否则调用Activity的onTouch();
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

public boolean onTouchEvent(MotionEvent event) {
    if (mWindow.shouldCloseOnTouch(this, event)) {
        finish();
        return true;
    }
    return false;
}
123456789101112131415161718
PhoneWindow的superDispatchTouchEvent()方法
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;//DecorView是一个FrameLayout;

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
1234567
DecorView类的superDispatchTouchEvent();DecorView是FrameLayout的子类,FrameLayout继承ViewGroup;返回ViewGroup的dispatchTouchEvent()的返回值;
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}
123
ViewGroup的dispatchTouchEvent():分发事件;主要看dispatchTransformedTouchEvent()方法
如果当前事件已经被自身,或者子View消费,则返回true;没有被消费,则返回false,向上抛;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean handled = false;
    //如果不进入if方法,dispatchTouchEvent()则返回false;
    if (onFilterTouchEventForSecurity(ev)) {
        //如果是down,重置相关的状态,mFirstTouchTarget=null;
        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();
         }
        //1:检查是否拦截事件的分发
        // Check for interception.
        final boolean intercepted;
        //如果拦截或者有一个View已经开始处理该事件,正常执行事件的分发;
        //down:可以满足条件;move,up:mFirstTouchTarget!=null;
        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
            //子类设置了requestDisallowInterceptTouchEvent(true);不允许父类拦截Touch事件;
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            //允许父类拦截
            if (!disallowIntercept) {
                //onInterceptTouchEvent()可以认为默认情况下,返回false;需要拦截重写onInterceptTouchEvent(),返回true;
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action); // restore action in case it was changed
            } else {
                intercepted = false;
            }
        }
        //touch event没有被取消并且没有被拦截,通过不断的递归,找到target view;为mFirstTouchTarget赋值;
        if (!canceled && !intercepted) {
            //down会执行下面的代码,move,up不再往下执行;已经找到了对应的target view;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                 final int childrenCount = mChildrenCount;
                //遍历DecorView的子View;
                 if (newTouchTarget == null && childrenCount != 0) {
                    final View[] children = mChildren;
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        //child不为null;如果从子View中找到target view,消费了该事件,将为mFirstTouchTarget赋值;
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            //addTouchTarget()为mFirstTouchTarget赋值newTouchTarget;
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            break;
                        }
                    }
                }
            }
        }
        
        //mFirstTouchTarget不为null,找到了target view;否则没有找到target view;
        // Dispatch to touch targets.
        if (mFirstTouchTarget == null) {
            //没有找到target view;意味着1)没有子View;2)有子View,但是子View都不消费事件;3)
            //down就被拦截了,自己消费; 就将ViewGroup当做普通View来对待;
            // No touch targets so treat this as an ordinary view.
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            //找到了target view;
            // 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;
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    //如果被拦截了(down没有被拦截,move或者up被拦截)
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                    }  
                }
                predecessor = target;
                target = next;
            }
        }
    }
    return handled;
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
ViewGroup的onInterceptTouchEvent():可以认为默认情况下,返回false;需要拦截重写onInterceptTouchEvent(),返回true;
public boolean onInterceptTouchEvent(MotionEvent ev) {
    //满足 鼠标点击,Down等条件返回 true;
    if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
            && ev.getAction() == MotionEvent.ACTION_DOWN
            && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
            && isOnScrollbarThumb(ev.getX(), ev.getY())) {
        return true;
    }
    return false;
}
12345678910
ViewGroup的dispatchTransformedTouchEvent();不管是什么情况就做一件事;

child为null,意味着没有子View消费,就把当前ViewGroup当做普通View来处理;判断当前View是否消费该事件;
child不为null,否则就往子View继续分发事件;

由dispatchTouchEvent()决定返回值;true:子View或者自己消费了该事件;false:没有消费该事件,就往上抛;
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    final boolean handled;

    // Perform any necessary transformations and dispatch.
    if (child == null) {
         //child== null;调用View的dispatchTouchEvent();当做普通的View;如果当前View的消费了该事件即(当前View的OnTouchListener的onTouch()返回了ture;或者onTouchEvent()返回了true),就返回true;
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        //1)child为ViewGroup,继续调用ViewGroup的dispatchTouchEvent(),继续往下分发;
        //2)child为普通的View;如果子View消费了该touch event即(子View的OnTouchListener的onTouch()返回了ture;或者onTouchEvent()返回了true),则dispatchTouchEvent()返回true;
        handled = child.dispatchTouchEvent(transformedEvent);
    }

    // Done.
    transformedEvent.recycle();
    return handled;
}
123456789101112131415161718
View的dispatchTouchEvent():向target view 分发Touch事件或者当前View就是target view;
事件被target view消费了,即如果OnTouchListener的onTouch()返回了ture;或者onTouchEvent()返回了true,则返回true;否则touch事件没有被消费,则返回false;
可以看出先执行onTouch()方法和onTouchEvent()方法的执行优先级,一旦执行了具体的实现类重写了onTouch()方法并且返回了true,则onTouchEvent()方法将不再执行;
public boolean dispatchTouchEvent(MotionEvent event) {
    boolean result = false;
    final int actionMasked = event.getActionMasked();
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Defensive cleanup for new gesture
        stopNestedScroll();
    }

    if (onFilterTouchEventForSecurity(event)) {
        //鼠标事件
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
        }
        
        //onTouch()方法,一旦执行了具体的实现类重写了onTouch()方法并且返回了true,则onTouchEvent()方法将不再执行
        //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
        //onTouchEvent();
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    return result;
}
1234567891011121314151617181920212223242526272829
View的onTouchEvent()方法:有对应的target view消费了该触摸事件即target View是可点击的(clickable = true),就返回true;否则返回false;
public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();
    //View是可点击的条件;
    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
    //一个disable的View依然可以消费touch事件,只是无法响应它而已;
    if ((viewFlags & ENABLED_MASK) == DISABLED) {
        // A disabled view that is clickable still consumes the touch
        // events, it just doesn't respond to them.
        return clickable;
    }
    //虽然是当前View接受touch事件但是实际上是另一个View消费,setTouchDelegate()设置;
    //一般使用在ViewGroup的子类中,帮组其子View扩大点击事件范围;
    if (mTouchDelegate != null) {
        //如果事件在构造函数中指定的范围内,则将Touch事件转发给委托View。
        if (mTouchDelegate.onTouchEvent(event)) {
            return true;
        }
    }

    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                      
                        // Only perform take click actions if we were in the pressed state
                        if (!focusTaken) {
                            // Use a Runnable and post this rather than calling
                            // performClick directly. This lets other visual state
                            // of the view update before click actions start.
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            //发送到message到MessageQueue中,如果主线程还在运行中,就会返回true;
                            if (!post(mPerformClick)) {
                                performClick();
                            }
                        }
                    }
                }
                mIgnoreNextUpEvent = false;
                break;

            case MotionEvent.ACTION_DOWN:
                if (!clickable) {
                    checkForLongClick(0, x, y);
                    break;
                }
                  //检查是否长按;最后会调用performLongClick()方法
                break;

            case MotionEvent.ACTION_CANCEL:
                //取消相关Callback,修改状态值;
                break;

            case MotionEvent.ACTION_MOVE:
                //不在按钮内滑动,取消相关Callback
                // Be lenient about moving outside of buttons
                if (!pointInView(x, y, mTouchSlop)) {
                    // Outside button
                    // Remove any future long press/tap checks
                }
                break;
        }

        return true;
    }

    return false;
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
PerformClick类
 private final class PerformClick implements Runnable {
     @Override
     public void run() {
        performClick();
     }
 }
123456
performClick()调用onClick()方法;
onClick()方法是在onTouchEvent()方法中的action==ACTION_UP的时候才执行的;
onTouch()、onTouchEvent()、onClick()三个方法的执行优先级依次递减;
public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        //调用onClick()方法;
        li.mOnClickListener.onClick(this);
        result = true;
    } else {
        result = false;
    }
    return result;
}
12345678910111213
以上就是事件分发及传递的主要内容,如有问题,请多指教,谢谢!
————————————————
版权声明:本文为CSDN博主「fengluoye2012」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fengluoye2012/article/details/83782042

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值