Android view event distribution mechanism from source code analysis

Source: Internet
Author: User

Android view event distribution mechanism from source code analysis

I have never understood the event distribution mechanism of View, but I have encountered it in project development. I also found some solutions on the Internet, but I am not familiar with the principle. Now I have resigned for a while, today, we write the event distribution of the View, which can be learned together with the android source code. If something is wrong, we will learn to improve it together and get down to the truth.

Create an android project with only one activity and a button. Set setOnClickListener () and setOnTouchListener () for the Button and view the result through log:

 

btnClick.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.i(com.example.demo,button click );}});

Button Touch event:

 

 

btnClick.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {Log.i(com.example.demo,button touch +event.getAction());return false;}});

Log:

 

 

com.example.demo(30220): button touch 0com.example.demo(30220): button touch 2com.example.demo(30220): button touch 2com.example.demo(30220): button touch 2com.example.demo(30220): button touch 2com.example.demo(30220): button touch 2com.example.demo(30220): button touch 1com.example.demo(30220): button click 

We can see that onTouch takes precedence over OnClick, and OnTouch executes several times, because OnTouch events consist of DWON, MOVE, and UP, So onTouch executes several times, so why is the execution order onTouch followed onClick?

 

Observe onClick and onTouch and you will find that the onTouch () method has a return value. The default value is false. If we change to true, what is the difference? click Print log to see:

 

com.example.demo(3280): button touch 0com.example.demo(3280): button touch 2com.example.demo(3280): button touch 2com.example.demo(3280): button touch 2com.example.demo(3280): button touch 2com.example.demo(3280): button touch 1

The log results show that the Onclick event is not executed. We can understand that when onTouch returns true, the Button event is consumed, which is equivalent to blocking the view event and will not continue to be passed down, therefore, The OnClick event is not executed,

 

First of all, if you touch any control on the interface, the dispatchTouchEvent method of the control will be called. This method takes precedence over onTouch and onClick. When we click the Button, we will call the dispatchTouchEvent method in the Button class. Then we will find this method in the source code of the Button, the Button source code is rare. This method is not available. The Button source code is as follows:

Although this method is not available, we can see that the Button inherits TextView, so we can find it in TextView, but the dispatchTouchEvent () method is not found in TextView, you can only find the parent class of TextView, and the parent class of TextView is the View object. Find the dispatchTouchEvent () method in the View source code to see its execution logic:

First, let's translate the description of this method:

@ Param event The motion event to be dispatched,Event Action event dispatch

@ Return True if the event was handled by the view, false otherwise. if the event is processed, true is returned; otherwise, false is returned,

Now let's take a look at the code in the dispatchTouchEvent () method and see that the source code has a method. Not all code needs to be understood,

Focus on the dispatchTouchEvent () method

 

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

 

 

First, let's look at the first if statement,
When will the mOnTouchListener variable be initialized? We track and find that it is initialized

public void setOnTouchListener(OnTouchListener l) { mOnTouchListener = l; }

Therefore, when setOntouchListener is used, mOnTouchListener can assign values. Therefore, this variable is not null,

 

The second condition (mViewFlags & ENABLED_MASK) = ENABLED is used to determine whether the currently clicked control is enable. By default, all buttons are enable, so this condition is always true.

If condition's third condition mOnTouchListener. onTouch (this, event) We click onTouch () method to find:

 

 

 public interface OnTouchListener {        /**         * Called when a touch event is dispatched to a view. This allows listeners to         * get a chance to respond before the target view.         *         * @param v The view the touch event has been dispatched to.         * @param event The MotionEvent object containing full information about         *        the event.         * @return True if the listener has consumed the event, false otherwise.         */        boolean onTouch(View v, MotionEvent event);    }

It is found that it is an interface method used for callback. In fact, we set the return value of the onTouchListener method,

 

The return value is true, so the if condition determines that the return value is true, so the following statement will not be executed.

 

 if (onTouchEvent(event)) {                return true;            }
Let's take a look at the logic of the onTouchEvent (event) method: the source code is as follows:

 

 

 public boolean onTouchEvent(MotionEvent event) {        final int viewFlags = mViewFlags;        if ((viewFlags & ENABLED_MASK) == DISABLED) {            if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PRESSED) != 0) {                mPrivateFlags &= ~PRESSED;                refreshDrawableState();            }            // 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 (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.                            mPrivateFlags |= PRESSED;                            refreshDrawableState();                       }                        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) {                            postDelayed(mUnsetPressedState,                                    ViewConfiguration.getPressedStateDuration());                        } else if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now                            mUnsetPressedState.run();                        }                        removeTapCallback();                    }                    break;                case MotionEvent.ACTION_DOWN:                    mHasPerformedLongPress = false;                    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 |= PREPRESSED;                        if (mPendingCheckForTap == null) {                            mPendingCheckForTap = new CheckForTap();                        }                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());                    } else {                        // Not inside a scrolling container, so show the feedback right away                        mPrivateFlags |= PRESSED;                        refreshDrawableState();                        checkForLongClick(0);                    }                    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                    if (!pointInView(x, y, mTouchSlop)) {                        // 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;    }
We found that there are too many source code in this method, but the onTouchEvent () method only needs to look at case MotionEvent. the code in ACTION_UP, because we need to execute the code after touching it by hand. After Various judgments, the code will come here:

 

 

 if (!post(mPerformClick)) {                                    performClick();      }
Then execute the merge mclick () method and the code in the merge mclick method:

 

 

 public boolean performClick() {        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);        if (mOnClickListener != null) {            playSoundEffect(SoundEffectConstants.CLICK);            mOnClickListener.onClick(this);            return true;        }        return false;    }
First, let's look at the if condition. The mOnClickListener variable is set when we click, so it will not be null. Then, let's look at an important method, which is also a callback method,

 

MOnClickListener. onClick (this );

This is the click event for setting the view. Through the source code, we should now understand the sequence in which we set the onClick and onTouch events at first,

Summary:

1: If the setOnTouchListener method of the view object returns true, the view object will not execute the click event. If setTouchListener is set to false, the view will execute the click event,

We also know that touch events consist of DWON, MOVE, and UP, as shown below:

 

btnClick.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {if(event.getAction()==MotionEvent.ACTION_DOWN){Log.i(com.example.demo,ACTION_DOWN);return false;}else if(event.getAction()==MotionEvent.ACTION_MOVE){Log.i(com.example.demo,ACTION_MOVE);}else{Log.i(com.example.demo,ACTION_UP);}return false;}});

 



 


 

 

 

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.