Android event distribution mechanism Summary

Source: Internet
Author: User

Android event distribution mechanism Summary

To understand the event distribution mechanism, we need to discuss the distribution of View and ViewGroup events. The difference between a View and a ViewGroup is that a View control can no longer contain child controls, such as TextView, Button, and ImageView, while a ViewGroup inherits from a View, however, it can contain some child controls, including views or nested viewgroups. Most of the commonly used la s are ViewGroup components, such as LinearLayout, RelativeLayout, and FrameLayout.

The first thing to understand is that when we touch a control (whether it is View or ViewGroup), we call the dispatchTouchEvent () method to start event distribution and processing. Let's first customize a simple linear layout:

 

public class MyLinearLayout extends LinearLayout {      public MyLayout(Context context, AttributeSet attrs) {          super(context, attrs);      }   }

Layout file:

 

 

 
  
 

 

I. View event Distribution

After running, click the Button control. When the event is passed to the Button, the dispatchTouchEvent method of the Button is called (the Button itself does not have the dispatchTouchEvent method, and finally finds the dispatchTouchEvent method of its parent class View ). Analyze the processing process based on the dispatchTouchEvent source code:

 

Public boolean dispatchTouchEvent (MotionEvent event) {if (mOnTouchListener! = Null & (mViewFlags & ENABLED_MASK) = ENABLED & mOnTouchListener. onTouch (this, event) {// return true;} return onTouchEvent (event); // Step 2}
Step 1: first judge the three conditions:
(1) check whether the OnTouchListener () event is set for the button;
(2) Whether the control is Enable; (the control is enable by default)
(3) Whether the onTuch () method in the OnTouchListener listener implemented in the button returns true;

 

If all conditions are met, the event will be consumed and will not be processed in onTouchEvent.

Step 2: When the preceding three conditions are not met at the same time, the event will be handled by the onTouchEvent method. Analyze the processing process based on the onTouchEvent source code:

 

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 shoshould 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 // 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 (); // second point }}} 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; mhas#medlongpress = false; postDelayed (mPendingCheckForTap, ViewConfiguration. getTapTimeout (); break; case MotionEvent. ACTION_CANCE L: 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 ;}
The source code is very long. We only need to pay attention to the important points.

 

First: there is a long if statement.

if (((viewFlags & CLICKABLE) == CLICKABLE ||              (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE))
It is used to determine whether the View is clickable or long-pressed. Obviously, our Button is a clickable View control. After entering the if clause, it is transferred to the switch. After the switch statement is executed, it is directly executed.
return true;
What does it mean? As long as the control is clickable or long-pressed View, this event will be consumed! This is also in line with our cognition. Isn't Button or something that people can click to process, but for non-clickable controls such as TextView and ImageView, can we handle click events? Recall that when processing these click events, we must use setOnClickListener () to set the click listener OnClickListener (or declare android: clickable = "true" in the layout "), the source code of setOnClickListener is as follows:

 

 

public void setOnClickListener (OnClickListener l) {          if (!isClickable()) {              setClickable( true);          }          mOnClickListener = l;  }
We can see that the click event or long-pressed event control automatically changes to the CLICKABLE or LONG_CLICKABLE status;


 

Second: The onclick event we set is executed when the finger raises ACTION_UP.

Here, we can summarize the process of processing the View event. For a View, the event first enters the dispatchTouchEvent Method for distribution processing. In the dispatchTouchEvent, first check whether the View sets the OnTouchListener event and whether the onTouch method returned value in the listener is true, if the condition is met, the event will be consumed and will not be processed. If the condition is not met, the event will be processed in the onTouchEvent method. In the onTouchEvent method, first, check whether the View is clickable or long-pressed (set the listener and layout to set android: clickable). If yes, the event is consumed.

We can see that the onTouch () method takes precedence over onTouchEvent (). The return value of the onTouch () method determines whether the onTouchEvent () method can return the value of the dispatchTouchEvent () method, the onTouch () method or onTouchEvent () method dependent on OnTouchListener.

 

Ii. Distribution and handling of ViewGroup events

Take the custom Layout above as an example. When we click the button, the event is actually distributed first in our custom MyLinearLayout. Similarly, first, go to the dispatchTouchEvent (LinearLayout itself does not have dispatchTouchEvent) method of MyLinearLayout for distribution, and finally find the dispatchTouchEvent of its parent ViewGroup. Analyze the dispatchTouchEvent source code:

 

Public boolean dispatchTouchEvent (MotionEvent ev) {final int action = ev. getAction (); final float xf = ev. getX (); final float yf = ev. getY (); final float scrolledXFloat = xf + mScrollX; final float scrolledYFloat = yf + mScrollY; final Rect frame = mTempRect; // The default value is false, then, we can use the requestDisallowInterceptTouchEvent (boolean disallowIntercept) method // to change the value of disallowIntercept boolean disallowIntercept = (MGroupFlags & FLAG_DISALLOW_INTERCEPT )! = 0; // here is the processing logic of ACTION_DOWN. if (action = MotionEvent. ACTION_DOWN) {// clear mMotionTarget. mMotionTarget is set to null every time ACTION_DOWN. if (mMotionTarget! = Null) {mMotionTarget = null;} // The default value of disallowIntercept is false. For details, refer to the onInterceptTouchEvent () method if (disallowIntercept |! OnInterceptTouchEvent (ev) {// first vertex ev. setAction (MotionEvent. ACTION_DOWN); final int scrolledXInt = (int) scrolledXFloat; final int scrolledYInt = (int) scrolledYFloat; final View [] children = mChildren; final int count = mChildrenCount; // traverse its child View for (int I = count-1; I> = 0; I --) {// second point final View child = children [I]; // if the sub-View is VISIBLE or the sub-View is being animated, it indicates that the View is ready. // you can accept the Touch event if (child. mViewFl Ags & VISIBILITY_MASK) = VISIBLE | child. getAnimation ()! = Null) {// obtain the child View's position range: child. getHitRect (frame); // if (frame. contains (scrolledXInt, scrolledYInt) {// offset the event to the view's coordinate system final float xc = scrolledXFloat-child. mLeft; final float yc = scrolledYFloat-child. mTop; ev. setLocation (xc, yc); child. mPrivateFlags & = ~ CANCEL_NEXT_UP_EVENT; // call the dispatchTouchEvent () method of the child View if (child. dispatchTouchEvent (ev) {// if child. dispatchTouchEvent (ev) returns true, indicating that the event is consumed. If mMotionTarget is set to mMotionTarget = child, true return true ;} // The event didn't get handled, try the next view. // Don't reset the event's location, it's not // necessary here .}}}}} // determine whether it is ACTION_UP or ACTION_CANCEL boolean isUpOrCancel = (ac Tion = MotionEvent. ACTION_UP) | (action = MotionEvent. ACTION_CANCEL); if (isUpOrCancel) {// if it is ACTION_UP or ACTION_CANCEL, set disallowIntercept to the default false // if we call requestDisallowInterceptTouchEvent () method To set disallowIntercept to true // when we lift the finger or cancel the Touch event, we need to reset disallowIntercept to false // so the above disallowIntercept is set to false by default every time we move the ACTION_DOWN. mGroupFlags & = ~ FLAG_DISALLOW_INTERCEPT;} // The event wasn't an ACTION_DOWN, dispatch it to our target if // we have one. final View target = mMotionTarget; // If mMotionTarget is null, the View for consuming the Touch event is not found. Therefore, we need to call the // dispatchTouchEvent () method of the parent class of ViewGroup, that is, View's dispatchTouchEvent () method if (target = null) {// We don't have a target, this means we're handling the // event as a regular view. ev. setLocation (xf, yf); if (mP RivateFlags & CANCEL_NEXT_UP_EVENT )! = 0) {ev. setAction (MotionEvent. ACTION_CANCEL); mPrivateFlags & = ~ CANCEL_NEXT_UP_EVENT;} return super. dispatchTouchEvent (ev);} // The Code ACTION_DOWN in the if statement will not be executed. Only ACTION_MOVE // ACTION_UP will be executed here. if the ACTION_MOVE or ACTION_UP event is intercepted, distribute ACTION_CANCEL to target, and then return true // directly, indicating that the Touch event if (! DisallowIntercept & onInterceptTouchEvent (ev) {final float xc = scrolledXFloat-(float) target. mLeft; final float yc = random-(float) target. mTop; mPrivateFlags ~ CANCEL_NEXT_UP_EVENT; ev. setAction (MotionEvent. ACTION_CANCEL); ev. setLocation (xc, yc); if (! Target. dispatchTouchEvent (ev) {}// clear the target mMotionTarget = null; // Don't dispatch this event to our own view, because we already // saw it when intercepting; we just want to give the following // event to the normal onTouchEvent (). return true;} if (isUpOrCancel) {mMotionTarget = null;} // finally offset the event to the target's coordinate system and // dispatch the event. final fl Oat xc = scrolledXFloat-(float) target. mLeft; final float yc = scrolledYFloat-(float) target. mTop; ev. setLocation (xc, yc); if (target. mPrivateFlags & CANCEL_NEXT_UP_EVENT )! = 0) {ev. setAction (MotionEvent. ACTION_CANCEL); target. mPrivateFlags & = ~ CANCEL_NEXT_UP_EVENT; mMotionTarget = null;} // If ACTION_MOVE and ACTION_DOWN are not intercepted, the Touch event is directly distributed to target return target. dispatchTouchEvent (ev );}
The dispatchTouchEvent method of ViewGroup is very long and mainly involves two points:

 

First, use the onInterceptTouchEvent method to determine whether the ViewGroup intercepts the event. By default, the method returns false, that is, the event is not intercepted and distributed to its subview or subviewgroup. Most of the time, we can rewrite this method to customize some complex controls and flexibly process the returned values as needed.

Second point:

1. When onInterceptTouchEvent returns false and does not intercept, the if judgment condition is true and enters the if field to traverse the sub-ViewGroup or sub-View of the ViewGroup, distribute the event to the subviewgroup or the dispatchTouchEvent method of the subview. If the ViewGroup in the middle is not intercepted, the Touch event will be distributed to the View at the bottom of the finger press. At this time, the View dispatchTouchEvent method will be called according to the View event distribution process, and then handed over to the onTouchEvent method for processing,

(1) If onTouchEvent returns true, that is, the dispatchTouchEvent of the View returns true, indicating that the event is consumed and the event is terminated.

(2) If onTouchEvent returns false, that is, if the Touch event is not consumed, the Touch event will call the onTouchEvent of its parent layout to the parent layout for processing.

2. When onInterceptTouchEvent returns true, it indicates that the ViewGroup needs to intercept the event, and the event is handled by the ViewGroup itself to call the onTouchEvent method of the ViewGroup.

We can see that ViewGroup event transmission is actually divided into two steps: Event distribution and event processing, first of all, the distribution of Touch events, first, distribute the View from the top-level View to the View at the bottom of the finger press. Then, judge whether the View is processed (or consumed ), if it is not processed (false), it is submitted to the parent layout layer by layer. If it is consumed (true), it will not be handed over to the parent layout and the event is terminated. The flowchart is as follows:

 

Here, we will summarize the overall process of event distribution and processing in View and ViewGroup. When we touch the screen with our fingers, the event will be passed into the root layout of the layout file we wrote, for example, in the Custom MyLinearLayout above, we searched for the dispatchTouchEvent method of MyLinearLayout for event distribution. Because the dispatchTouchEvent method of the parent ViewGroup is not found in LinearLayout, view the onInterceptTouchEvent method of the ViewGroup to determine whether to intercept the event. If you do not intercept the event, traverse its sub-ViewGroup or sub-View until it is intercepted by a ViewGroup during the next distribution process, or finally distribute the View to the innermost part of the finger-pressed View, and process the event according to the View event processing process. During ViewGroup event distribution, the system determines whether to continue traversal and distribution based on the returned values of the dispatchTouchEvent method of the child View or ViewGroup.

Now, the theory of event distribution is almost the same. The most important thing is to take a look at some of the custom controls of the great gods and learn about the event distribution mechanism in practice. It also integrates the draw process of Android View and ViewGroup.

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.