Android Click event Distribution mechanism source code Analysis _android

Source: Internet
Author: User
Tags gety

Overview

have been trying to write an article on the Android event distribution mechanism, but has not written, these two days is just the weekend, have time, think about writing a bar, or always only stay at the level of use but not understand its internal mechanism. I use 4.4 source code, open look, very complex, especially how the event is distributed from the activity, too puzzling. People who understand the Windows Messaging mechanism will find that Android's event distribution mechanism is similar to Windows ' messaging mechanism, which is a typical message bubbling mechanism, with many platforms using this mechanism, where messages first reach the bottom view, And then it's first to judge if it's what it needs, otherwise, the message will be passed to its child view, so that the message from the bottom of the bubble to float a little distance, and so on, the bubble reached the top and air contact, broken (message was processed), of course, there are bubbles to the top layer, Not broken (message unattended), this message will be handled by the system, and for Android, it will be handled by the activity.

The Android Click event Distribution mechanism

1. Transfer from activity to bottom view

The Click event is motionevent to indicate that when a click occurs, the event is first passed to the current activity, and the event is distributed by the dispatchtouchevent of the action. The specific work is done by Windows within the Activity, window will pass events to decor View,decor view is generally the underlying container of the current interface (that is, the parent container of the view set by Setcontentview), can be obtained by Activity.getWindow.getDecorView (). In addition, look at the following code, mainly to see my comments, the code is a lot of complex, I can not explain, but I commented that the place is the key point, is the blogger carefully read the code summed up.

Source code Interpretation:

It's not clear where the event was passed to the activity, but it doesn't matter, but it's not enough for us to start analyzing it, and it's sufficient for us to understand its internal implementation.

Code:activity#dispatchtouchevent

   /**
   * Called to process with screen events. You can override this to
   * intercept all touch screens events before they are dispatched to the
   * window. Be sure to call this implementation for touch screen events
   * should is handled normally.
   * 
   @param ev the touch screen event.
   * 
   * @return Boolean return True if this event is consumed.
   */Public
  Boolean dispatchtouchevent (motionevent ev) {
    if (ev.getaction () = = Motionevent.action_down) {
      //This function is actually an empty function, nothing dry, if you do not rewrite, do not care about
      onuserinteraction ();
    }
    Here the event begins to be distributed to the window in which the activity is attached, and if true, the entire event loop ends
    //returns false means that the event was not handled, and all Ontouchevent returned false. Then the activity will come to a final conclusion.
    if (GetWindow (). superdispatchtouchevent (EV)) {return
      true;
    }
    Here, the activity comes to an end, the activity of the ontouchevent is called return
    ontouchevent (EV);
   

How window is passing events to ViewGroup's

Code:window#superdispatchtouchevent

  /**
   * Used by custom windows, such as Dialog, to pass the touch screen event
   * Further down the view hierarchy. Application developers should
   * not need to implement or call this.
   *
   *
  /Public Abstract Boolean superdispatchtouchevent (Motionevent event);

This is actually an abstract function, but also indicate the application developer do not implement it or call it, what is this? Take a look at a description of the following class, to the effect that this class can control the appearance and behavior strategies of top view, And also said that the only implementation of this class is located in Android.policy.PhoneWindow, when you want to instantiate the window class, you do not know its details, because this class will be refactored, only one factory method can be used. Well, it's still blurry, but we can take a look at the class of Android.policy.PhoneWindow, although this class will be refactored when instantiated, but the functionality is similar.

Abstract base class for a top-level windows look and behavior policy. An instance of this class should is used as the top-level view added to the window manager. IT provides standard UI policies such as a background, title area, default key processing, etc. The only existing implementation of the ' this ' abstract class is Android.policy.PhoneWindow, which to should instantiate when n Eeding a Window. Eventually that class'll be refactored and a factory to added for creating Window instances without knowing about a particular implementation.

Code:phonewindow#superdispatchtouchevent

 @Override public boolean superdispatchtouchevent (Motionevent event) {return Mdeco
  R.superdispatchtouchevent (event); This logic is very clear, Phonewindow will pass the event to Decorview, this decorview is what, please see the following private final class Decorview extends Framelayout
  Implements Rootviewsurfacetaker//This is the top-level view of the window, containing the window decor.

  Private Decorview Mdecor;
    @Override Public final View Getdecorview () {if (Mdecor = = null) {Installdecor ();
  return Mdecor; }

By the way, the most commonly used window is ((viewgroup) GetWindow (). Getdecorview (). Findviewbyid (Android. r.id.content). Getchildat (0) means that the internal view is obtained through activity. This mdecor is clearly the view returned by GetWindow () Getdecorview (), and the view we set through Setcontentview is one of its child view. The current event is passed to Decorview here, because Decorview inherits from Framelayout and is our parent view, so the final event will be passed to our view for the first reason, in other words, the event will surely pass to our view, How else would our application respond to the Click event? But that's not our point, but the point is that it's more useful for us to be able to pass the event to our view. From here, events have passed to our top view, noting that top view is actually the bottom view, also called Root view.

2. The distribution process of the underlying view to the event

After clicking the event to the underlying view (typically a viewgroup), the ViewGroup Dispatchtouchevent method is invoked, Then the logic is this: if the underlying ViewGroup intercept event that Onintercepttouchevent returns True, then the event is handled by ViewGroup, and if ViewGroup's Montouchlistener is set, The Ontouch will be invoked, otherwise the ontouchevent will be invoked, that is, if it is provided, the Ontouch will block out ontouchevent. In Ontouchevent, if a monclicklistener is set, the onclick is invoked. If the top-level viewgroup does not intercept the event, the event is passed to its child view on the Click event Chain, at which point the dispatchtouchevent of the child view is invoked, so that the event has been passed from the bottom view to the previous view, The next act is consistent with its underlying view, so that the loop completes the entire event distribution. Also note that the ViewGroup default is not to intercept the Click event, its onintercepttouchevent return false.

Source code Interpretation:

Code:viewgroup#dispatchtouchevent

  @Override public boolean dispatchtouchevent (motionevent ev) {if (minputeventconsistencyverifier!= null) {
    Minputeventconsistencyverifier.ontouchevent (EV, 1);
    Boolean handled = FALSE;
      if (onfiltertoucheventforsecurity (EV)) {final int action = Ev.getaction ();

      Final int actionmasked = action & motionevent.action_mask;
      Handle an initial down. 
        if (actionmasked = = Motionevent.action_down) {//Throw away all previous ' when starting a the new touch gesture. The framework may have dropped the "Up" or "Cancel event for" the previous gesture//due to a app switch
        , ANR, or some the other.
        Cancelandcleartouchtargets (EV);
      Resettouchstate ();
      }//Check for interception.
      Final Boolean intercepted; if (actionmasked = = Motionevent.action_down | | mfirsttouchtarget!= NULL) {Final Boolean Disallowinterc EPT = (Mgroupflags & FLAG_DISALLOW_INTERCEPT)!= 0;
          if (!disallowintercept) {//here determines whether to intercept click events, if intercepted, then intercepted=true intercepted = onintercepttouchevent (EV); Ev.setaction (action);
        Restore action in case it is changed} else {intercepted = false; } else {//There are no touch targets and this action are not a initial down/I view Grou
        P continues to intercept touches.
      intercepted = true;
      }//Check for cancelation. Final Boolean canceled = Resetcancelnextupflag (this) | |

      actionmasked = = Motionevent.action_cancel;
      Update list of touch targets to pointer down, if needed.
      Final Boolean split = (Mgroupflags & flag_split_motion_events)!= 0;
      Touchtarget newtouchtarget = null;
       Boolean alreadydispatchedtonewtouchtarget = false; There's a lot of it. Event to Child view, if intercepted is true, skip if (!canceled &&!intercepted) {if (actionmasked = = Mo Tionevent.action_down | | (Split && actionmasked = Motionevent.action_pointer_down) | | actionmasked = = motionevent.action_hover_move) {final int actionindex = Ev.getactionindex ();//Always 0 for Do WN final int idbitstoassign = split?

          1 << Ev.getpointerid (actionindex): Touchtarget.all_pointer_ids;
          Clean up earlier touch targets for this pointer ID in case they//have become out of sync.

          Removepointersfromtouchtargets (idbitstoassign);
          Final int childrencount = Mchildrencount;
            if (Newtouchtarget = = null && childrencount!= 0) {final float x = Ev.getx (Actionindex);
            Final float y = ev.gety (Actionindex);
            Find a can receive the event.
            Scan children from the front to the back.

            Final view[] children = Mchildren;
            Final Boolean customorder = ischildrendrawingorderenabled (); For (int i = childrenCount-1; I >= 0;
                  i--) {final int childindex = Customorder?
              Getchilddrawingorder (Childrencount, i): i;
              Final View child = Children[childindex];
                if (!canviewreceivepointerevents (child) | |!istransformedtouchpointinview (x, y, child, null)) {
              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 wit
                Hin its bounds.
           Mlasttouchdowntime = Ev.getdowntime ();     Mlasttouchdownindex = Childindex;
                Mlasttouchdownx = Ev.getx ();
                Mlasttouchdowny = Ev.gety (); Note that if a child view handles the Click event, the Newtouchtarget will be assigned,//and the Alreadydispatchedtonewtouchtarget will be true, and these two variables are directly affecting the following code
                Logical.
                Newtouchtarget = Addtouchtarget (Child, idbitstoassign);
                Alreadydispatchedtonewtouchtarget = true;
              Break }} if (Newtouchtarget = null && mfirsttouchtarget!= null) {//D
            ID 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. Here if the current ViewGroup intercepts the event, or its child viewOntouchevent returns FALSE, the event is handled by ViewGroup if (Mfirsttouchtarget = null) {//No touch targets so treat this a
       s an ordinary view. This is ViewGroup's handling of the Click event handled = Dispatchtransformedtouchevent (EV, canceled, NULL, TOUCHTARGET.ALL_PO
      Inter_ids); else {//Dispatch to touch targets, excluding the "new touch" target if we already//dispatched to it. C
        Ancel Touch targets if necessary.
        Touchtarget predecessor = NULL;
        Touchtarget target = Mfirsttouchtarget;
          while (target!= null) {final touchtarget next = Target.next;
          if (alreadydispatchedtonewtouchtarget && target = = newtouchtarget) {handled = true;
            else {Final Boolean cancelchild = Resetcancelnextupflag (target.child) | | | intercepted;
              if (dispatchtransformedtouchevent (EV, Cancelchild, Target.child, target.pointeridbits)) { handled = TRUe
              } if (Cancelchild) {if (predecessor = = null) {Mfirsttouchtarget = next;
              else {predecessor.next = next;
              } target.recycle ();
              target = next;
            Continue
          } predecessor = target;
        target = next;
      }//Update list of touch targets to pointer up or cancel, if needed. if (Canceled | | actionmasked = = MOTIONEVENT.ACTION_UP | | actionmasked = = Motionevent.action_hover_move
      ) {resettouchstate (); else if (split && actionmasked = = motionevent.action_pointer_up) {final int actionindex = Ev.getactioni
        Ndex ();
        final int idbitstoremove = 1 << ev.getpointerid (actionindex);
      Removepointersfromtouchtargets (Idbitstoremove); } if (!handled && minputeventconsistencyverifier!= null) {MinputevEntconsistencyverifier.onunhandledevent (EV, 1);
  return handled;

 }

Next, look at ViewGroup's handling of the Click event.

Code:viewgroup#dispatchtransformedtouchevent

   /** * Transforms A motion event into the coordinate spaces of a particular child view, * filters out irrelevant p
   Ointer 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 desiredpointer

    Idbits) {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) {//Here is ViewGroup's handling of the Click event, which invokes the view's Dispatchtouchevent method handled = Super.dispatchtouchevent
      (event);
      else {handled = Child.dispatchtouchevent (event);
      } event.setaction (Oldaction); return handled;
    }//Calculate the number of pointers to deliver.
    Final int oldpointeridbits = Event.getpointeridbits ();

    Final int newpointeridbits = oldpointeridbits & desiredpointeridbits;  If for some reason we ended up in a inconsistent state where it looks like we//might produce a motion event with
    No pointers in it, then drop the event.
    if (newpointeridbits = = 0) {return false; //If The number of pointers is the same and we don ' t need to perform any fancy//irreversible transformations
    , then we can reuse the motion event for this//dispatch as long as we are careful to revert any changes we make.
    Otherwise we need to make a copy.
    Final Motionevent transformedevent; if (newpointeridbits = = oldpointeridbits) {if child = null | | Child.hasidentitymatrix ()) {if child = N
        ull) {handled = Super.dispatchtouchevent (event); else {final float OffsetX = Mscrollx-chilD.mleft;
          Final float OffsetY = mscrolly-child.mtop;

          Event.offsetlocation (OffsetX, OffsetY);

          handled = Child.dispatchtouchevent (event);
        Event.offsetlocation (-offsetx,-offsety);
      return handled;
    } transformedevent = Motionevent.obtain (event);
    else {transformedevent = Event.split (newpointeridbits);
    }//perform any necessary transformations and dispatch.
    if (child = = NULL) {handled = Super.dispatchtouchevent (transformedevent);
      else {final float OffsetX = mscrollx-child.mleft;
      Final float OffsetY = mscrolly-child.mtop;
      Transformedevent.offsetlocation (OffsetX, OffsetY);
      if (! Child.hasidentitymatrix ()) {Transformedevent.transform (Child.getinversematrix ());
    } handled = Child.dispatchtouchevent (transformedevent);
    }//Done.
    Transformedevent.recycle ();
  return handled;
 }

Look again

Code:view#dispatchtouchevent

/** * 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) {if (minputeventconsistencyverifier!= null) {Minput
    Eventconsistencyverifier.ontouchevent (event, 0); } if (Onfiltertoucheventforsecurity (event)) {//noinspection simplifiableifstatement listenerinfo li = ML
      Istenerinfo; 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; }

This code is relatively simple, view of the event processing is like this: if you set the Ontouchlistener on the call Ontouch, otherwise directly call Ontouchevent, And the onclick is triggered by PerformClick inside the ontouchevent. In simple terms, if the event is ViewGroup blocked or the ontouchevent of the child view returns false, the event is ultimately handled by ViewGroup.

3. Unattended Click events

If a click event, the ontouchevent of the child view returns false, the ontouchevent of the parent view is called directly, and so on. If all view is not processed, it will eventually be handled by the activity, at which point the ontouchevent of the activity will be invoked. This question has been explained in 1 and 2.

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.

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.