源码解析View事件的分发机制

首先,Android中View的事件分发机制有3个重要的方法

dispatchTouchEvent()onInterceptTouchEvent()onTouchEvent()

 

先来看dispatchTouchEvent()这个方法

    public boolean dispatchTouchEvent(MotionEvent event) {

        if (!onFilterTouchEventForSecurity(event)) {

            return false;

        }

 

        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&

                mOnTouchListener.onTouch(this, event)) {

            return true;

        }

        return onTouchEvent(event);

}

 

第一个判断指的是当前View被其它窗口覆盖,直接返回false,第二个判断首先判断mOnTouchListener不为null,并且view是enable的状态,然后 mOnTouchListener.onTouch(this, event)返回true,这三个条件如果都满足,直接return true ; 也就是下面的onTouchEvent(event)不会被执行了;

这个mOnTouchListener其实就是我们设置的View事件

 

   

    public void setOnTouchListener(OnTouchListener l) {

        mOnTouchListener = l;

    }

 

如果设置了View的事件setOnTouchListener,并且return true,那么View自己的onTouchEvent就不会被执行了,那么如果我们return false呢?看下面

 

看一下onTouchEvent()的源码

    public boolean onTouchEvent(MotionEvent event) {

        final int viewFlags = mViewFlags;

 

        if ((viewFlags & ENABLED_MASK) == DISABLED) {

            // A disabled view that is clickable still consumes the touch

            // events, it just doesn't respond to them.

            return (((viewFlags & CLICKABLE) == CLICKABLE ||

                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));

        }

 

        if (mTouchDelegate != null) {

            if (mTouchDelegate.onTouchEvent(event)) {

                return true;

            }

        }

 

        if (((viewFlags & CLICKABLE) == CLICKABLE ||

                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {

            switch (event.getAction()) {

                case MotionEvent.ACTION_UP:

                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;

                    if ((mPrivateFlags & PRESSED) != 0 || prepressed) {

                        // take focus if we don't have it already and we should in

                        // touch mode.

                        boolean focusTaken = false;

                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {

                            focusTaken = requestFocus();

                        }

 

                        if (!mHasPerformedLongPress) {

                            // 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)) {

                                    performClick();

                                }

                            }

                        }

 

                        if (mUnsetPressedState == null) {

                            mUnsetPressedState = new UnsetPressedState();

                        }

 

                        if (prepressed) {

                            mPrivateFlags |= PRESSED;

                            refreshDrawableState();

                            postDelayed(mUnsetPressedState,

                                    ViewConfiguration.getPressedStateDuration());

                        } else if (!post(mUnsetPressedState)) {

                            // If the post failed, unpress right now

                            mUnsetPressedState.run();

                        }

                        removeTapCallback();

                    }

                    break;

 

                case MotionEvent.ACTION_DOWN:

                    if (mPendingCheckForTap == null) {

                        mPendingCheckForTap = new CheckForTap();

                    }

                    mPrivateFlags |= PREPRESSED;

                    mHasPerformedLongPress = false;

                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());

                    break;

 

                case MotionEvent.ACTION_CANCEL:

                    mPrivateFlags &= ~PRESSED;

                    refreshDrawableState();

                    removeTapCallback();

                    break;

 

                case MotionEvent.ACTION_MOVE:

                    final int x = (int) event.getX();

                    final int y = (int) event.getY();

 

                    // Be lenient about moving outside of buttons

                    int slop = mTouchSlop;

                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||

                            (y < 0 - slop) || (y >= getHeight() + slop)) {

                        // Outside button

                        removeTapCallback();

                        if ((mPrivateFlags & PRESSED) != 0) {

                            // Remove any future long press/tap checks

                            removeLongPressCallback();

 

                            // Need to switch from pressed to not pressed

                            mPrivateFlags &= ~PRESSED;

                            refreshDrawableState();

                        }

                    }

                    break;

            }

            return true;

        }

 

        return false;

}

 

 if ((viewFlags & ENABLED_MASK) == DISABLED)如果当前View是Disabled状态且是可点击则会消费掉事件(return true);

这里有个最重要的方法是 if (((viewFlags & CLICKABLE) == CLICKABLE ||

                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)),如果短按或长按,就判断事件类型,有3个event状态,分别是

MotionEvent.ACTION_DOWN

MotionEvent.ACTION_MOVE

MotionEvent.ACTION_UP

 

onInterceptTouchEvent()方法是在dispatchTouchEvent里面调用:

public boolean dispatchTouchEvent(Motion e){     boolean result=false;     if(onInterceptTouchEvent(e)){     //如果当前View截获事件,那么事件就会由当前View处理,即调用onTouchEvent()        result=onTouchEvent(e);     }else{        //如果不截获那么交给其子View来分发        result=child.dispatchTouchEvent(e);     }     return result;}

 

从以上可以看出,这三个方法的联系。onInterceptTouchEvent()方法在dispatchTouchEvent中调用,来判断自己是否需要截取事件,如果该方法返回为true,那么View将消费该事件,即会调用onTouchEvent()方法。如果返回false,那么通过调用子View的dispatchTouchEvent()将事件交由子View来处理。

onTouchEvent()和onInterceptTouchEvent()一样也是在dispatchTouchEvent中调用的。用来处理点击事件,包括ACTION_DOWN,ACTION_MOVE,ACTION_UP。如果返回结果为false表示不消费该事件,并且也不会截获接下来的事件序列。如果返回为true表示当前View消费该事件。

posted @ 2016-07-27 09:46  moliying  阅读(163)  评论(0编辑  收藏  举报