Android事件分发机制(一)

有时候会遇到一个MS题,onTouch()和onClick()的执行顺序?onTouch()和onTouchEvent()异同?

那么我们通过代码来验证一下他们的执行顺序,看代码:

 button.setOnClickListener(new OnClickListener() {  
    @Override  
    public void onClick(View v) {  
        Log.d("TAG", "onClick");  
    }  
});
button.setOnTouchListener(new OnTouchListener() {  
    @Override  
    public boolean onTouch(View v, MotionEvent event) {  
        Log.d("TAG", "onTouch");  
        return false;  
    }  
});

看一下运行结果:

很明显是先执行的onTouch()方法,但是细看onTouch()方法可以看出他有个返回值,默认返回的是false,那我们返回true试试。

onClick()方法就不执行了,为啥呢?那么我们来看一下点击事件的过程。

当你点击某一个控件的时候,这里我们以button为例,首先会调用他的dispatchTouchEvent(),但是查看源码会发现button并没有这个方法,那么再看下他的父类TextView,它里面也没有;继续往上找View,View里存在这个方法!这个方法是干什么用的呢?先来看下源码:

public boolean dispatchTouchEvent(MotionEvent event) {  
    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch(this, event)) {  
        return true;  
    }  
    return onTouchEvent(event);  
}

这个方法是个有返回值的方法,返回true的判断条件有三个:

mOnTouchListener!=null  这个参数,只要你给这个控件设置了onTouchListener监听,那么这个参数就不为Null

第二个参数意思是该控件的onclick属性为enabled,也就是可点击的,这个参数就为真

重点是第三个参数,onTouch()方法的返回值,如果返回true则整个方法返回true,反之返回false

上面的例子我们看到返回true,onclick()方法就不会执行了,也就是说onclick()方法是在onTouchEvent()中执行的!看下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;  
}

源码比较长,我们只看重点,up方法里有个performClick()方法,看下他的源码:

public boolean performClick() {  
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
    if (mOnClickListener != null) {  
        playSoundEffect(SoundEffectConstants.CLICK);  
        mOnClickListener.onClick(this);  
        return true;  
    }  
    return false;  
}

看到了如果注册了onTouchListener,就会回调onclick方法。由此我们也可以看出onclick方法实在up方法里触发的。如果down方法就返回的false,那么就不会有后面的onclick了。也就是只要上级的dispatchTouchEvent方法返回了true才会执行下面的动作。但是前面我们不是返回true不会执行下面的动作了吗?只要在ontouch方法里返回了false,就会进入touchEvent,那我们看下源码,在14行if判断以后,无论怎么走,左后都返回了true,所以才会执行后面的up,然后是onclick。如果ontouch返回了true就不会进入ontouchevent也就不会执行他的方法。

看到这里我们可以重新看一下开头说的问题,onTouch()和onTouchEvent()都是在View的dispatchTouchEvent中调用的,但是呢OnTouch要早于onTouchEvent执行,如果onTouch返回了true,则onTouchEvent不会执行。

今天先看到这里,下一篇博客会继续学习事件分发机制。

 

 

你可能感兴趣的:(Android事件分发机制(一))