android view 事件分发笔记

来源:互联网 发布:pcb仿真软件 编辑:程序博客网 时间:2024/05/12 21:45

对于Button :

button.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View arg0, MotionEvent arg1) {// TODO Auto-generated method stubSystem.out.println("button onTouch");return true;}});

button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubSystem.out.println("button onClick");}});

当Button注册onTouch()事件,并且返回true时,点击Button时会打印两个Button onTouch,并且屏蔽掉onClick()事件。因此不会打印“Button onClick"。其中,打印两次”Button onTouch"是因为
public boolean dispatchTouchEvent(MotionEvent event) {    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&            mOnTouchListener.onTouch(this, event)) {        return true;    }    return onTouchEvent(event);}

这个方法非常的简洁,只有短短几行代码!我们可以看到,在这个方法内,首先是进行了一个判断,如果mOnTouchListener != null,(mViewFlags & ENABLED_MASK) == ENABLED和mOnTouchListener.onTouch(this, event)这三个条件都为真,就返回true,否则就去执行onTouchEvent(event)方法并返回。

先看一下第一个条件,mOnTouchListener这个变量是在哪里赋值的呢?我们寻找之后在View里发现了如下方法:


public void setOnTouchListener(OnTouchListener l) {    mOnTouchListener = l;}
1)mOnTouchListener正是在setOnTouchListener方法里赋值的,也就是说只要我们给控件注册了touch事件,mOnTouchListener就一定被赋值了。

2)第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true。

3)第三个条件就比较关键了,mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。


因此,当Button的OnTouch()事件返回true时,则不会执行onTouchEvent()方法,由于前一个动作返回true,那么事件分发方法则会继续执行点击事件,因此会打印两个“Button onTouch",其中一个是响应down事件的,另一个是响应up事件。此根据借鉴于(touch事件的层级传递。我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。)


二.当Button的onTouch事件返回false时,则会打印两次"Button onTouch",而且会执行OnClick()方法,打印"Button OnClick".

1)打印两次"Button OnTouch"的原因:onTouch()方法返回false,那么dispatchTouchEvent()里面则会执行onTouchEvent()事件,在ouTouchEvent()方法里面,由于Button是默认可以点击的,所以,根据源码:

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()方法里面,因此,不管是down,up,还是move动作都会放回true,根据前面说的,要想继续执行下一个动作,那么前一个动作一定要返回true,即dispatchTouchEvent()返回true,才能继续执行下一个动作,例如up动作,由于Button在View里dispatchTouchEvent()都是返回true的,所以,不论Button的onTouch()事件是返回true,还是false,它都会执行打印两次“Butt onTouch".

2)而至于打印”Button onClick"的原因是:执行了OnTouchEvent()方法,而OnClick()方法是在动作up之后执行的,所以会打印“Button OnClick".







1 0
原创粉丝点击