Android View event distribution mechanism

Source: Internet
Author: User

Android View event distribution mechanism

Recently, I encountered a view slide conflict during development. As I knew from the very beginning that this problem was related to view event distribution, after reading a few articles about event distribution on the Internet, problems encountered during development are quickly solved.
Here I will summarize my understanding of view event distribution.

First, let's take a look at the event distribution flowchart:

Button event demonstration

Before analyzing the event distribution mechanism of the view, we can use a demo to view the event processing process of the Button.
Add a button control to the layout file, implement the setOnClickListener and setOnTouchListener methods of the Button in the code, and register the Click listener and Touch listener.

/*** Button event */private void showButtonTouch () {mBtn = (Button) findViewById (R. id. btn); mBtn. setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {Log. e (TAG, "Button onClick") ;}}); mBtn. setOnTouchListener (new View. onTouchListener () {@ Override public boolean onTouch (View v, MotionEvent event) {switch (event. getAction () {case MotionEvent. ACTION_UP: Log. e (TAG, "Button onTouch" + "ACTION_UP"); break; case MotionEvent. ACTION_MOVE: Log. e (TAG, "Button onTouch" + "ACTION_MOVE"); break; case MotionEvent. ACTION_DOWN: Log. e (TAG, "Button onTouch" + "ACTION_DOWN"); break;} return false ;}});}

  
      
  

After running the demo, click the button to view the log (when you click it, move it to ensure that the ACTION_MOVE event can be triggered ):

The log shows the event processing process.

View event distribution source code analysis

In the View source code, we can see the dispatchTouchEvent method, which can be understood as the entry to View event distribution. (The Code is based on 4.0.3, that is, API 15. It is recommended that you do not read too high versions when you understand the source code. The High Version source code will be optimized too much, which will impede our understanding of the main functional logic of the Code.)

DispatchTouchEvent method Parsing

The following is the dispatchTouchEvent method code in View:

Public boolean dispatchTouchEvent (MotionEvent event) {if (mInputEventConsistencyVerifier! = Null) {mInputEventConsistencyVerifier. onTouchEvent (event, 0);} if (onFilterTouchEventForSecurity (event) {// noinspection SimplifiableIfStatement // This is the main logic for judging the View event distribution ListenerInfo li = mListenerInfo; if (li! = Null & li. mOnTouchListener! = Null & (mViewFlags & ENABLED_MASK) = ENABLED & li. mOnTouchListener. onTouch (this, event) {return true;} if (onTouchEvent (event) {return true ;}} if (mInputEventConsistencyVerifier! = Null) {mInputEventConsistencyVerifier. onUnhandledEvent (event, 0);} return false ;}

In this method, the key to event distribution lies in this judgment.(Li! = Null & li. mOnTouchListener! = Null & (mViewFlags & ENABLED_MASK) = ENABLED & li. mOnTouchListener. onTouch (this, event )).

If this is true, the method returns true; otherwise, the onTouchEvent () method is executed and the specific result is returned Based on the return value of the onTouchEvent method.

Li! = Null: The ListenerInfo object li in the Code is a static class defined in the View. This class internally defines all Listener-related classes in the View. Generally, this class is not empty, I will not explain it too much here (this object is not found in source code 2.3 ).

Li. mOnTouchListener! = Null: whether it is null. The mOnTouchListener object is set through mBtn. setOnTouchListener (new View. OnTouchListener () {}), so it is not empty here.

(MViewFlags & ENABLED_MASK) = ENABLED: determines whether the control is enable. It is obviously true.

Li. mOnTouchListener. onTouch (this, event): Finally, judge whether the returned value in the onTouch method is true. The onTouch method is the onTouch method of @ Override when we register the Touch listener on the Button control.

In the previous demo, because the returned value of the mOnTouchListener. onTouch (this, event) method is false, we can see in the log that the button executes the touch event before executing the click event. Here we change the return value of the onTouch method to true, and we can see the following log:

The log shows that the click method is not executed. Why?

In fact, here we can think of the Code by reading the dispatchTouchEvent method. Because the onTouch return value is true, this judgment condition returns true immediately when the dispatchTouchEvent method is used.If (onTouchEvent (event)-> the onTouchEvent method is not executedIs the click method not executed due to this reason? Obviously, let's look at the source code of the onTouchEvent method.

OnTouchEvent source code parsing:

The source code of the onTouchEvent method 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 ;}// this is the main logic function of onTouchEvent code. 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 shoshould 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 // specify mclick directly. this lets other visual state // of the view update before click actions start. if (mPerformClick = null) {mPerformClick = new multiple mclick ();} if (! Post (mPerformClick) {initialize mclick () ;}} if (mUnsetPressedState = null) {mUnsetPressedState = new partition ();} 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 (descrimbuttonactionontouchdown (event) {break;} // Walk up the hierarchy to determine if we're re inside a scrolling container. boolean isInScrollingContainer = isInScrollingContainer (); // For views inside 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 | = PRE SSED; 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 ;}

For the onTouchEvent method, we only need to read the main functional logic from the first line of code.
First, determine whether the View is clickable. Because the Button is clickable by default, this condition is true and we can go to the switch branch to determine.I would like to leave a question here. If this condition is not met immediately, the control cannot be clicked. What will happen?

MotionEvent. ACTION_UP: In this Branch judgment, we can see that there is a semi mclick () method in row 58. We can see it in this method body:

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

In this method, we can see that li. mOnClickListener. onClick (this) has been called. Here we can confirmThe click event of the Button is called in the onTouchEvent method..

TextView and Button comparison

In onTouchEvent, I left a question. If you can determine whether the control can be clicked to false, what will happen?

To verify this problem, we can compare TextView and Button, because TextView cannot be clicked by default, and Button can be clicked by default.

First, add TextView and Button in xml, and register the setOnTouchListener listener for them in the Code. The Code is as follows:


  
      
      
   
/*** Button event */private void showButtonTouch () {mBtn = (Button) findViewById (R. id. btn); mBtn. setOnTouchListener (new View. onTouchListener () {@ Override public boolean onTouch (View v, MotionEvent event) {switch (event. getAction () {case MotionEvent. ACTION_UP: Log. e (TAG, "Button onTouch" + "ACTION_UP"); break; case MotionEvent. ACTION_MOVE: Log. e (TAG, "Button onTouch" + "ACTION_MOVE"); break; case MotionEvent. ACTION_DOWN: Log. e (TAG, "Button onTouch" + "ACTION_DOWN"); break;} return false ;}});}/*** TextView event */private void showTextTouch () {mTv = (TextView) findViewById (R. id. TV); mTv. setOnTouchListener (new View. onTouchListener () {@ Override public boolean onTouch (View v, MotionEvent event) {switch (event. getAction () {case MotionEvent. ACTION_UP: Log. e (TAG, "TextView onTouch" + "ACTION_UP"); break; case MotionEvent. ACTION_MOVE: Log. e (TAG, "TextView onTouch" + "ACTION_MOVE"); break; case MotionEvent. ACTION_DOWN: Log. e (TAG, "TextView onTouch" + "ACTION_DOWN"); break;} return false ;}});}

Because we need to enter the onTouchEvent method, we must make the setOnTouchListener. onTouch () method return value false. Here let's take a look at the log printed by Button and TextView after running the demo:
The system will think that the ACTION_DOWN event is not completed, so other touch events cannot be triggered.

Now, let's look at the View section in the event distribution flowchart !!!

Related Article

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.