Android View event distribution mechanism detailed _android

Source: Internet
Author: User
Tags abstract gety

Prepared for a while, always wanted to write an event distribution of the article summed up, this knowledge point is too important.

An application of the layout is rich, there are textview,imageview,button and so on, these child view of the outer layer there are viewgroup, such as Relativelayout,linearlayout. As a developer, we think, when clicked on a button, how did the Android system make sure that I ordered the button instead of the TextView? It then responds correctly to the button's Click event. What kind of process is going on inside?

First, some knowledge can be more clear understanding of the event distribution mechanism:
1. The view that is set through Setcontentview is Decorview's child view, that is, Decorview is the parent container.
2. Click on the screen, when the fingers press and lift between, will produce many events, down...move...move...up, there will be a lot of move events, this series of events for an event sequence
3. Dispatchtouchevent method for distributing events
4. Onintercepttouchevent method for intercepting events
5. Ontouchevent Method for handling events

When a click event (motionevent) is generated, the event is first passed to the current interface (activity), which is well understood. The activity then passes the event to the window, and window passes the event to the top-level view (Decorview). At this point, the event has reached the view. The top view then distributes the events according to the event distribution mechanism. Specifically, this is true:

For a root viewgroup, when the Click event is generated, it is first passed to it, at which point its Dispatchtouchevent method is invoked, If this ViewGroup Onintercepttouchevent method returns true it means that it will intercept the current event, and then the event will be given to the viewgroup processing, that is, its Ontouchevent method will be invoked. If this ViewGroup Onintercepttouchevent method returns false, it means that it does not intercept the current event, and the current event continues to pass to its child elements, and then the Dispatchtouchevent method of the child element is invoked. So repeatedly until the event is finally dealt with.
If the Ontouchevent method of a view returns False, the Ontouchevent method of its parent container is invoked, and if its parent container's Ontouchevent method returns false, it continues to be thrown up. When all elements do not handle this event, the event is eventually passed on to the activity processing, where the Ontouchevent method of the work is invoked.

Well, now that the groundwork has been laid, then we will analyze the event distribution mechanism from the source point of view.

Of course, it starts with the Dispatchtouchevent method of activity. The source code is as follows:

public boolean dispatchtouchevent (motionevent ev) {
  if (ev.getaction () = = Motionevent.action_down) {
    Onuserinteraction ();
  }
  if (GetWindow (). superdispatchtouchevent (EV)) {return
    true;
  }
  return ontouchevent (EV);
}

If the current event is down, call the Onuserinteraction method, Onuserinteraction is an empty method, we can temporarily ignore. The GetWindow method is then invoked to obtain the Window,window that is associated with the current activity and then calls the Superdispatchtouchevent method to pass the event in for distribution.
If the Superdispatchtouchevent method returns True, view has handled the event. The entire event loop ends. If you return false, no view handles this event. The event is thrown up, and the activity is handled by itself, that is, the Ontouchevent method is invoked.

Because you want to know the whole distribution process of the event, and now focus on the Superdispatchtouchevent method of the window, then go in and see:

Public abstract Boolean Superdispatchtouchevent (Motionevent event);

Window is an abstract class, Superdispatchtouchevent is an abstract method, then we have to find the implementation of the window of the class to do, but how to find the vast sea? You see the description of the window class, you get it.

* <p>the only existing implementation the This abstract class
 is * Android.view.PhoneWindow, which you should ins Tantiate when needing a
 * Window.
 * * Public
abstract class Window

It means that the only implementation of window existence is Android.view.PhoneWindow

So the Superdispatchtouchevent method in Phonewindow is the information we are looking for, as follows:

@Override Public
Boolean superdispatchtouchevent (Motionevent event) {return
  mdecor.superdispatchtouchevent (event);
}

The event is passed directly to the Decorview. At this point, the event is already in view.

Then follow the Decorview superdispatchtouchevent method to see, as follows:

public boolean superdispatchtouchevent (Motionevent event) {return
  super.dispatchtouchevent (event);
}

The Dispatchtouchevent method of the parent class is called internally, so what is the Decorview parent class? Decorview is definitely view, so just now the opening mentions that the view we set through Setcontentview is Decorview's view. So it's more accurate to say that Decorview is a viewgroup.

Private Final class Decorview extends Framelayout implements Rootviewsurfacetaker

Can see Decorview is inherited from Framelayout,framelayout is ViewGroup, that is to say Decorview is a viewgroup.

So now just pay attention to the ViewGroup dispatchtouchevent method. Keep moving

Event distribution for ViewGroup

The Dispatchtouchevent method for the

ViewGroup is as follows:

@Override public boolean dispatchtouchevent (motionevent ev) {//code ellipsis//Check for interception.
    Final Boolean intercepted; if (actionmasked = = Motionevent.action_down | | mfirsttouchtarget!= NULL) {Final Boolean disallowintercept
      = (Mgroupflags & flag_disallow_intercept)!= 0;
        if (!disallowintercept) {intercepted = Onintercepttouchevent (EV); Ev.setaction (action);
      Restore action in case it is changed} else {intercepted = false; } else {//There are no touch the targets and this action are not a initial down/so this view group cont
      Inues to intercept touches.
    intercepted = true; 
        }//code omitted if (!canceled &&!intercepted) {//code omitted final int childrencount = Mchildrencount;
          if (Newtouchtarget = = null && childrencount!= 0) {final float x = Ev.getx (Actionindex);
          Final float y = ev.gety (Actionindex); FiND a child can receive the event.
          Scan children from the front to the back.
          Final arraylist<view> preorderedlist = Buildorderedchildlist ();
          Final Boolean customorder = Preorderedlist = = null && ischildrendrawingorderenabled ();
          Final view[] children = Mchildren; for (int i = childrenCount-1 i >= 0; i--) {final int childindex = Customorder? getchil
            Ddrawingorder (Childrencount, i): i; Final View child = (preorderedlist = null)?

            Children[childindex]: Preorderedlist.get (Childindex); If there is a view this has accessibility focus we want it/to get the event I/if not handled we Would perform a//normal dispatch.
            We'll do a double iteration but this is//safer given the timeframe. if (Childwithaccessibilityfocus!= null) {if (Childwithaccessibilityfocus!= chiLD) {continue;
              } childwithaccessibilityfocus = null;
            i = childrenCount-1; } if (!canviewreceivepointerevents (child) | |!istransformedtouchpointinview (x, y, child, NULL
              )) {Ev.settargetaccessibilityfocus (false);
            Continue
            } Newtouchtarget = Gettouchtarget (child);
              if (newtouchtarget!= null) {//child be already receiving touch within its bounds.
              Give it the new pointer in addition to the ones it is handling.
              Newtouchtarget.pointeridbits |= idbitstoassign;
            Break
            } resetcancelnextupflag (child); if (Dispatchtransformedtouchevent (EV, FALSE, Child, Idbitstoassign)) {//child wants to receive touch Withi
              n its bounds.
              Mlasttouchdowntime = Ev.getdowntime ();
     if (preorderedlist!= null) {           Childindex points into the presorted list, find original index for (int j = 0; J < Childrenco Unt
                    J + +) {if (children[childindex] = = Mchildren[j]) {mlasttouchdownindex = J;
                  Break
              }} else {mlasttouchdownindex = Childindex;
              } Mlasttouchdownx = Ev.getx ();
              Mlasttouchdowny = Ev.gety ();
              Newtouchtarget = Addtouchtarget (Child, idbitstoassign);
              Alreadydispatchedtonewtouchtarget = true;
            Break }//The accessibility focus didn ' t handle of the event, so clear//the flag and do a normal dispat
            Ch to all children.
          Ev.settargetaccessibilityfocus (FALSE);
        } if (preorderedlist!= null) preorderedlist.clear ();
       } if (Newtouchtarget = = null && mfirsttouchtarget!= null) {   Did not find a child to receive the event.
          Assign the pointer to the least recently added target.
          Newtouchtarget = Mfirsttouchtarget;
          while (Newtouchtarget.next!= null) {newtouchtarget = Newtouchtarget.next;
        } newtouchtarget.pointeridbits |= idbitstoassign;
    }}//Dispatch to touch targets.
      if (Mfirsttouchtarget = = null) {//No touch targets so treat this as a ordinary view.
    handled = dispatchtransformedtouchevent (EV, canceled, NULL, touchtarget.all_pointer_ids);
}//code omitted return handled;

 }

The code is longer, 1.1 point analysis, first see the beginning of the judgment

if (actionmasked = = Motionevent.action_down | | mfirsttouchtarget!= NULL)

The meaning of Mfirsttouchtarget!= null is that ViewGroup does not intercept events and is handled by child elements, so remember that this can be concluded from the Addtouchtarget method later.

And then it comes to this if judgment.

if (!disallowintercept) {
  intercepted = onintercepttouchevent (EV);
  Ev.setaction (action); Restore action in case it is changed
}

Let's look at the disallowintercept. And in the disallowintercept assignment, there is a flag_disallow_intercept tag bit

Final Boolean disallowintercept = (Mgroupflags & flag_disallow_intercept)!= 0;

This flag_disallow_intercept tag bit can be set by the Requestdisallowintercepttouchevent method.

Return to the IF (!disallowintercept) judgment, enter this if judgment, and it will come to

intercepted = Onintercepttouchevent (EV);

Call the Onintercepttouchevent method to ask ViewGroup whether to intercept the event.

Read here, you can recall the beginning of the foreshadowing of the conclusion, for ViewGroup, click event Generated, the first pass to it, then its Dispatchtouchevent method will be called, and then call its Onintercepttouchevent method, If this ViewGroup Onintercepttouchevent method returns true it means that it will intercept the current event, and then the event will be given to the viewgroup processing, that is, its Ontouchevent method will be invoked. If return false means no interception, usually viewgroup does not intercept the event.

That now first analysis does not intercept the situation, do not intercept that is good to do. After a series of judgments, it comes to a for loop traversal.

for (int i = childrenCount-1 i >= 0; i--)

The ViewGroup begins distributing delivery events, traversing the child elements.

First of all, it is necessary to filter out some of the child elements of irrelevant click events, determine whether the child elements can receive the click event, click the event's coordinates to fall within the child element area.

if (!canviewreceivepointerevents (child)
    | |!istransformedtouchpointinview (x, y, child, null)) {
  Ev.settargetaccessibilityfocus (false);
  Continue;
}

If you are unable to receive the click event or the click event's coordinates do not fall in the child element area, you will jump out of the current loop and continue traversing the next child element. This is why the Android system can know the click of the button rather than TextView, in fact, the internal just made a judgment.

Then continue the analysis, after the child elements meet the above two conditions, the event is passed to this child element. Would have come to this judgment.

if (Dispatchtransformedtouchevent (EV, FALSE, Child, Idbitstoassign))

Executes the Dispatchtransformedtouchevent method, passing the child elements in. This method is very important, then follow up and see

/** * Transforms A motion event into the coordinate space of a particular child view,
 * Filters out irrelevant pointer IDs, and overrides its action if necessary.
 * If is NULL, assumes the motionevent would be sent to this viewgroup instead. * Private Boolean Dispatchtransformedtouchevent (Motionevent event, Boolean cancel, View child, int desiredpointeridbi

  TS) {Final Boolean handled; Canceling motions is a special case. We don ' t need to perform any transformations//or filtering.
  The important part is the action and not the contents.
  Final int oldaction = Event.getaction ();
    if (Cancel | | oldaction = = motionevent.action_cancel) {event.setaction (motionevent.action_cancel);
    if (child = = NULL) {handled = Super.dispatchtouchevent (event);
    else {handled = Child.dispatchtouchevent (event);
    } event.setaction (Oldaction);
  return handled; }//code omitted} 

We saw child!. In the case of =null, if the child element is not empty, the Dispatchtouchevent method of the calling child element continues to distribute the event, returning the processing result boolean value, and then passing the event to the child view processing. Completed a round of event distribution. This is a good way to get here first.

Looking back at the Dispatchtouchevent method of ViewGroup, if the Dispatchtransformedtouchevent method returns True, then the event has been passed to the child element processing, and ViewGroup has already taken care of the event.
Then it goes into the IF statement and finally comes to the Addtouchtarget method, which was previously mentioned and used to mfirsttouchtarget the tag bit.

Then follow this method to see

/**
 * Adds A touch target for specified child to the beginning of the list.
 * Assumes the target child are not already present.
 *
Private Touchtarget Addtouchtarget (View child, int pointeridbits) {
  Touchtarget target = Touchtarget.obtain ( Child, pointeridbits);
  Target.next = Mfirsttouchtarget;
  Mfirsttouchtarget = target;
  return target;
}

In fact, let Mfirsttouchtarget point to the child element.

After you finish this addtouchtarget method, you end up with the break statement, and then you jump out of the entire for loop body. ViewGroup end the distribution process!

And back to the Dispatchtransformedtouchevent method, if the Dispatchtransformedtouchevent method returns False, then a large piece of code for the IF statement is not executed. Instead, it goes back to the for loop and continues traversing child elements for distribution. This repeats the delivery process for the event.

Now analyze the ViewGroup interception event, if ViewGroup intercept the event, then you will enter the following judgment

if (Mfirsttouchtarget = = null) {
  //No touch targets so treat this as a ordinary view.
  handled = dispatchtransformedtouchevent (EV, canceled, NULL,
  touchtarget.all_pointer_ids);
}

Note that the third parameter of the Dispatchtransformedtouchevent method, the child, is passed null, and then the following statement is taken in the Dispatchtransformedtouchevent method

if (child = = null) {
  handled = Super.dispatchtouchevent (event);
}

And ViewGroup is inherited from view, then it is viewgroup to deal with the events themselves. This is the following analysis of the view of the event distribution process can be understood.

The above is the ViewGroup event distribution

So now that the analysis has passed the event to the child view, view continues to invoke the Dispatchtouchevent method, let's look at the Dispatchtouchevent method of the view.

Event distribution for view

View's Dispatchtouchevent method source code is as follows:

/** * Pass the touch screens motion event down to the target view, or this * view if it is the target.
 * * @param event The motion event to be dispatched.
 * @return True If the event is handled by the view, false otherwise. */Public Boolean dispatchtouchevent (Motionevent event) {//code omitted if (Onfiltertoucheventforsecurity (event)) {//noi
    Nspection simplifiableifstatement 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; } if (!result && minputeventconsistencyverifier!= null) {Minputeventconsistencyverifier.onunhandledev
  ENT (event, 0);
  }//Clean up after nested scrolls if it is the end of a gesture; Also cancel it if we tried an action_down but we didn ' t want the rest//of the gesture.
      if (actionmasked = = Motionevent.action_up | |
      actionmasked = = Motionevent.action_cancel | | (actionmasked = = Motionevent.action_down &&!result))
  {Stopnestedscroll ();
return result;

 }

The Dispatchtouchevent method code for view is less than the ViewGroup dispatchtouchevent method. is relatively simple.
The first will come to the following judgments:

if (Li!= null && li.montouchlistener!= null
    && (mviewflags & enabled_mask) = = ENABLED
    && Amp Li.mOnTouchListener.onTouch (this, event))

Where are the LI variables assigned? Usually when the Setonclicklistener method or the Setontouchlistener method is used.

public void Setonclicklistener (@Nullable onclicklistener l) {
  if (!isclickable ()) {
    setclickable (true);
  }
  getlistenerinfo (). Monclicklistener = l;
}

public void Setontouchlistener (Ontouchlistener l) {
  getlistenerinfo (). Montouchlistener = l;
}

And this getlistenerinfo () is as follows:

Listenerinfo Getlistenerinfo () {
  if (mlistenerinfo!= null) {return
    mlistenerinfo;
  }
  Mlistenerinfo = new Listenerinfo ();
  return mlistenerinfo;
}

Listenerinfo is an internal class that holds references to various listening events.

The following conditions will be judged:

Li.montouchlistener!= NULL

Similarly, this reference is not empty as long as the Setontouchlistener method is set. These are all good to understand. That's the point,

Li.mOnTouchListener.onTouch (this, event)

To the last condition. This Ontouch method is what we do, it returns a Boolean value, and if it returns true, then it enters this if judgment and then returns true and jumps out of the entire method, so we can see that the next Ontouchevent method is not executed.
That is, the execution of Ontouch before ontouchevent. So if we also call the Setonclicklistener method to monitor the Click event, where is the OnClick method invoked? We have reason to believe that it was invoked in the Ontouchevent method. Then follow up and see.

 public boolean ontouchevent (Motionevent event) {//code omitted if ((ViewFlags & CLIC
      Kable) = = Clickable | |
      (ViewFlags & long_clickable) = = long_clickable) | |
        (ViewFlags & context_clickable) = = context_clickable) {switch (action) {case MOTIONEVENT.ACTION_UP:

  Boolean prepressed = (Mprivateflags & pflag_prepressed)!= 0; Code ellipsis if (!focustaken) {//Use a Runnable and post this rather than calling// PerformClick directly.
              This lets is the visual state//of the view update before click Actions Start.
              if (Mperformclick = = null) {Mperformclick = new PerformClick ();
              } if (!post (Mperformclick)) {PerformClick ();
    }}//code omits break;
  return true;
return false; }

As long as clickable or long_clickable not empty, will deal with this event, but how to ensure that clickable or long_clickable not empty? In fact, you will find that the above posted in the Setonclicklistener source code, the Clickabl property will be set to True

public void Setonclicklistener (@Nullable onclicklistener l) {
  if (!isclickable ()) {
    setclickable (true);
  }
  getlistenerinfo (). Monclicklistener = l;
}

This will be able to enter the if judgment to deal with this event, and then come to the PerformClick () method, it should be it, follow in to see it.

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);
  return result;
}

Familiar with the interface callback mechanism, you must also read the PerformClick () method of the source code,

Li.mOnClickListener.onClick (this);

is when we execute this line of code, we call our familiar OnClick method

The above is the event distribution mechanism for view.

Now that the event distribution mechanism has been analyzed, because of my technical reasons, the control is not good, some key points still did not analyze clearly, but I believe that after learning this article can let me and you have a general understanding of the implementation of the event distribution mechanism, have this already, and then can be a little more to strengthen the exercise, An in-depth understanding of the event distribution mechanism. Can pave the way for a good foundation for a custom control.

The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.

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.