Research on Android event distribution mechanism

Write a Demo and call the onTouch and onClick functions of a Button respectively:

        bt1 = (Button) findViewById(R.id.bt1);
        bt1.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "onTouch,event action:" + event.getAction());
                return false;
            }
        });

        bt1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "onClick...");
            }
        });

Click on the mobile phone and the result is as follows:

01-16 06:41:54.078 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:0
01-16 06:41:54.173 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:1
01-16 06:41:54.217 7507-7507/com.acxingyun.touchevent I/TouchActivity: onClick...

0 for press, 1 for lift:

Then there are other definitions such as drag:

Try dragging:

01-16 06:49:16.708 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:0
01-16 06:49:16.833 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:2
01-16 06:49:16.848 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:2
01-16 06:49:16.865 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:2
01-16 06:49:16.899 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:2
01-16 06:49:16.956 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:2
01-16 06:49:16.987 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:2
01-16 06:49:17.056 7507-7507/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:1
01-16 06:49:17.088 7507-7507/com.acxingyun.touchevent I/TouchActivity: onClick...

After action menu down, there is a series of action menu moves. Finally, action menu up is to click, drag, and then lift.
Change the following code and onTouch returns true:

bt1 = (Button) findViewById(R.id.bt1);
        bt1.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "onTouch,event action:" + event.getAction());
                return true;
            }
        });

        bt1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "onClick...");
            }
        });

result:

01-16 07:12:19.293 9767-9767/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:0
01-16 07:12:19.355 9767-9767/com.acxingyun.touchevent I/TouchActivity: onTouch,event action:1

Why didn't you call onClick back?
In Andorid, when any control is clicked, it will call the dispatchTouchEvent of the control. Button does not have this method, but it will search online in turn, from the parent class TextView to View, and finally find this method. In this method, part of the code is extracted:

            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }

The value of mentouchlistener is assigned when listening is set. The default value of view is ENABLED, that is (mviewflags & ENABLED [mask] = = ENABLED, mentouchlistener.onTouch (this, event) means to call the onTouch function of mentouchlistener. This function override s when listening is set:

bt1.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "onTouch,event action:" + event.getAction());
                return true;
            }
        });

If override returns true and a series of conditions are true and resule = true, the following ontoucheevent will not be entered:

            if (!result && onTouchEvent(event)) {
                result = true;
            }

Once the control's onTouch is overridden, the return of true indicates that the onTouch event has been consumed and will not enter onTouchEvent again. onClick is in onTouchEvent

public boolean onTouchEvent(MotionEvent event) {
        ..................

        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
                    if (!clickable) {
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
                    }
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_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 (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                        }

                        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)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;

                case MotionEvent.ACTION_DOWN:
                    if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
                        mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
                    }
                    mHasPerformedLongPress = false;

                    if (!clickable) {
                        checkForLongClick(0, x, y);
                        break;
                    }

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we're inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true, x, y);
                        checkForLongClick(0, x, y);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    if (clickable) {
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    break;

                case MotionEvent.ACTION_MOVE:
                    if (clickable) {
                        drawableHotspotChanged(x, y);
                    }

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        // Remove any future long press/tap checks
                        removeTapCallback();
                        removeLongPressCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            setPressed(false);
                        }
                        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    }
                    break;
            }

            return true;
        }

        return false;
    }

In motionevent. Action UUP, performClick() is executed:

    public boolean performClick() {
        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;
    }

In li.mOnClickListener.onClick(this), the onClick function of View is called back.

There is a question about how a series of actions, action down, action move, and action up, are triggered one after another???

Tags: Mobile

Posted on Mon, 04 May 2020 21:45:06 -0700 by gizmola