美文网首页
Android事件分发源码分析

Android事件分发源码分析

作者: L_先森0 | 来源:发表于2019-08-12 14:14 被阅读0次
  1. 事件分发
    事件从Activity开始分发,首先Activity 实现了Window.Callback接口,并实现了接口里面的dispatchTouchEvent()方法
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback,  // Activity 实现了 Window.Callback接口 并实现了接口里面的dispatchTouchEvent()方法
        KeyEvent.Callback,OnCreateContextMenuListener, ComponentCallbacks2,Window.OnWindowDismissedCallback, WindowControllerCallback, AutofillManager.AutofillClient {
         //开始处理事件
        public boolean dispatchTouchEvent(MotionEvent ev) {  
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
              //如果收到 ACTION_DOWN事件, 调用onUserInteraction(), 里面是一个空实现,等用户自己实现一个交互
                onUserInteraction();  
            }
            if (getWindow().superDispatchTouchEvent(ev)) {  //getWindow()即是PhoneWindow  把事件从Activity传入到PhoneWindow中,若PhoneWindow返回true,代表消费了此事件
                return true;  //事件被getWindow().superDispatchTouchEvent(ev)里面消费了,有可能是里面的ViewGroup或View
            }
            //若事件在以上都没用被消费,则执行Activity的onTouchEvent()方法去处理该事件
            return onTouchEvent(ev); 
        }
            
        public boolean onTouchEvent(MotionEvent event) {
            if (mWindow.shouldCloseOnTouch(this, event)) { //查看window是否应该关闭OnTouch事件 如果关闭,则finish()当前Activity
                finish();
                return true;
            }
            return false;
    }
            
}

在Activity中调用getWindow().superDispatchTouchEvent(ev) , 事件从Activity派发到PhoneWindow在把事件从PhoneWindow派发到DecorView中

// PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
    // DecorView extends FrameLayout 
    private DecorView mDecor;    //DecorView也即是一个FrameLayout extends ViewGroup
    
    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        //在PhoneWindow中又把事件派发到 mDecor处理
        return mDecor.superDispatchTouchEvent(event);
    }
}


// DecorView.java    
// mDecor.superDispatchTouchEvent(event)
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
     public boolean superDispatchTouchEvent(MotionEvent event) {
         //在此处又调用了super.dispatchTouchEvent(event) ,即调用父类的dispatchTouchEvent(event) 方法去处理事件
        return super.dispatchTouchEvent(event);
    }
}

因为FrameLayout extends ViewGroup , 即在DecorView中又默认调用了ViewGroup的dispatchTouchEvent()方法去处理事件

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    /**
     *  事件分发到ViewGroup中,在dispatchTouchEvent()方法处理
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
        }

        // If the event targets the accessibility focused view and this is it, start
        // normal event dispatch. Maybe a descendant is what will handle the click.
        // 
        if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
            ev.setTargetAccessibilityFocus(false);
        }

        boolean handled = false;   // 事件是否被处理
        if (onFilterTouchEventForSecurity(ev)) {
            //省略部分代码......
            
            // Check for interception.
            final boolean intercepted;  // 该事件是否被拦截
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    // 调用onInterceptTouchEvent(ev)方法查看事件是否被拦截,一般情况下默认是不拦截。自定义ViewGroup时可根据实际开发情况重写onInterceptTouchEvent()方法
                    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;
            }

            //省略部分代码......
        
            //如果事件没有取消并且没有被拦截
            if (!canceled && !intercepted) {
                    //省略部分代码......
             
                    final int childrenCount = mChildrenCount;  // 子view数量
                    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 ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        
                        //遍历子所有view, 分发事件
                        for (int i = childrenCount - 1; i >= 0; i--) { 
                            //省略部分代码......  
                            
                            //开始把事件派发给子view处理, 在dispatchTransformedTouchEvent()把事件真正交给子view处理
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

                            // The accessibility focus didn't handle the event, so clear
                            // the flag and do a normal dispatch to all children.
                            ev.setTargetAccessibilityFocus(false);
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }
                    //省略部分代码......  
                }
            }
           //省略部分代码...... 
        }

        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;
    }

    /**
     *  判断事件是否被拦截(拦截事件是在ViewGroup中处理,重写该方法,根据实际条件去拦截事件分发)
     */
     public boolean onInterceptTouchEvent(MotionEvent ev) {
        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;
    }


    /**
     *  事件派发给子view处理
     */
    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) {
                //如果传入的view为空,则默认调用super.dispatchTouchEvent(event),即View的dispatchTouchEvent(event)方法
                handled = super.dispatchTouchEvent(event);  
            } else {
                // 调用子view自己的的dispatchTouchEvent()
                handled = child.dispatchTouchEvent(event);  //
            }
            event.setAction(oldAction);
            return handled;
        }

      //省略部分代码...... 

        // Perform any necessary transformations and dispatch.
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }
            handled = child.dispatchTouchEvent(transformedEvent);
        }

        // Done.
        transformedEvent.recycle();
        //返回事件处理结果
        return handled;
    }
}

ViewGroup遍历子view,把事件派发给View处理,即调用View中的dispatchTouchEvent()方法处理事件

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
    
    /**
    *事件最后传入到View中处理
    */
     public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        //该view是否是focus
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }
        //处理结果(是否消费了该事件)
        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        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;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            // 如果ListenerInfo不为空, 该View设置了setOnTouchListener,并且在OnTouchListener接口中的onTouch方法中返回了true,即消费了该事件
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                //以上if判断都满足时,则消费了该事件(即处理了此次的事件分发)
                result = true;
            }
            //如果没有设置setOnTouchListener或者在在OnTouchListener接口中的onTouch方法中返回false,则执行下面的的代码
            //result为false时,执行onTouchEvent方法
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        //省略部分代码...... 
        
        return result;
    }
    
    
    /**
    * onTouchEvent方法
    */
    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是否可点击的,即是否设置了setClickable() ,Button是默认设置了setClickable(),TextView默认没有设置
        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

        //省略部分代码...... 
        
        //如果该view是可点击的,即设置了setClickable(),或者在xml中设置了clickable = true
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                   //省略部分代码...... 
             
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                       //省略部分代码...... 
                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            // This is a tap, so remove the longpress check
                            removeLongPressCallback();
                            // 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();
                                }
                                if (!post(mPerformClick)) {
                                    //如果该view中设置了setOnClickListener()方法,则在ACTION_UP事件中会处理 点击事件
                                    performClickInternal();
                                }
                            }
                        }
                       //省略部分代码...... 
                    break;
               //省略部分代码...... 
            return true;
        }
        return false;
    }

     /**
     *处理点击事件
     */
    private boolean performClickInternal() {
        // Must notify autofill manager before performing the click actions to avoid scenarios where
        // the app has a click listener that changes the state of views the autofill service might
        // be interested on.
        notifyAutofillManagerOnClick();
        return performClick();
    }
            
   /**
    * 处理view的点击事件并消费该事件
    */
    public boolean performClick() {
        // We still need to call this method to handle the cases where performClick() was called
        // externally, instead of through performClickInternal()
        notifyAutofillManagerOnClick();
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        notifyEnterOrExitForAutoFillIfNeeded(true);
        return result;
    }

}

事件分发大致流程图:


image.png

相关文章

网友评论

      本文标题:Android事件分发源码分析

      本文链接:https://www.haomeiwen.com/subject/awcujctx.html