Event distribution mechanism

Posted by daloss on Wed, 29 May 2019 22:05:58 +0200

discuss Android Before the event delivery mechanism, it is important to identify the two basic control types of android: View and ViewGroup.

View is a common control, no sub-layout, such as Button, TextView. ViewGroup inherits from View;

ViewGroup control, with child controls, such as Linearlayout, Listview, etc.

There are three most important events, MotionEvent:

(1)MotionEvent.ACTION_DOWN press View to start all events

(2)MotionEvent.ACTION_MOVE Slide Event

(3) The end of MotionEvent.ACTION_UP event corresponds to ACTION_DOWN

(1) Main Event Functions of Two Kinds of Controls

1. There are two main correlation functions corresponding to View control events:

Public Boolean dispatch TouchEvent (Motion Event ev) This method is used to distribute TouchEvent

Public Boolean on TouchEvent (Motion Event ev) This method is used to process TouchEvent


2. The corresponding groupView class controls have three main correlation functions:

Public Boolean dispatch TouchEvent (Motion Event ev) This method is used to distribute TouchEvent

Public Boolean on Intercept TouchEvent (Motion Event ev) This method is used to intercept TouchEvent

Public Boolean on TouchEvent (Motion Event ev) This method is used to process TouchEvent

Therefore, we can see that the View control has one less method of intercepting onInterceptTouchEvent(MotionEvent ev) than the control of the groupView class, because the View class has no child control and no statement of intercepting events passed to the subclass, while the groupView has subclasses to determine whether or not events passed to the subclass are intercepted.


3. Explain Motion Event:

The parameter MotionEvent is the object of the encapsulation class of the touch event on the mobile screen, which encapsulates all the information of the event, such as the location of the touch, the type of touch and the time of touch. This object is created when the user touches the screen of the mobile phone.


4. Following is an explanation of the main methods of the three events:

onTouchEvent is a handler for mobile screen events. This method is defined in the View class, and all View subclasses override the method. The application can handle touch events on the mobile screen through this method. The signature of this method is shown below.

public boolean onTouchEvent(MotionEvent event) {

return super.onTouchEvent(event);

}

Return value: The return value mechanism of this method is the same as that of the keyboard response event. It also returns true when the event has been processed completely and other callback methods are not expected to process it again, otherwise it returns false.


public boolean onInterceptTouchEvent(MotionEvent ev) {

return super.onInterceptTouchEvent(ev);

    }

onInterceptTouchEvent() is used to process events and change the direction of the event, and the return value determines the direction of the event.

When returned to false, the event is passed to dispatchTouchEvent() of the child control, which is then distributed by dispatchTouchEvent() of the subclass.

When returned to true, the event is passed to onTouchEvent() of the current control instead of to the child control, which is called Intercept.

And once the event is intercepted, this method will not be called again by the current view (that is, viewgroup).


public boolean dispatchTouchEvent(MotionEvent ev) {

        return super.dispatchTouchEvent(ev);

   }

dispatchTouchEvent() is mainly responsible for distributing onTouchEvent events, and does not handle onTouchEvent events itself. The distribution method is determined by its return value.

When the return value is true, the event is handed over to the onTouchEvent of the current view itself.

When the return value is false, the event is handed over to onInterceptTouchEvent() of the current view itself, which then determines the direction of event delivery.

After clarifying these three methods, we will make a preliminary analysis of the transmission mechanism of events.


(2) Preliminary understanding of the order in which touch events are delivered (better understanding if you have an understanding of the process of creating windows)

A touch event is triggered at the hardware level first, and then passed to the software layer by layer until our app. The details above are generally unknown. The event entry we discuss starts with Activity.
The order of transmission of touch events is:


Touch events are handled in exactly the opposite order (onTouch Event):



A general overview is as follows:

When TouchEvent occurs, first Activity distributes TouchEvent through getWindow (). Super Dispatch TouchEvent (event) to the current active window (Phone Window), then DecorView of the top window, calls DecorView's dispatch TouchEvent, which is inherited from ViewGroup, so it actually enters the dispatch Event at the View Group level, and then it is dispatched by the dispatch TouchEvent. TouchEvent Method is distributed. If dispatchTouchEvent returns true, it is handed over to the onTouchEvent of the current view. If dispatchTouchEvent returns false, it is handed over to the interceptTouchEvent method of the current view to decide whether to intercept the event. If interceptTouchEvent returns true, that is, to intercept the onTouchEvent of the current view, it is handed over to the onTouchEvent of the current view. To process, if interceptTouchEvent returns false, it continues to be passed to the child view The dispatch TouchEvent of the child view starts the distribution of this event again. If an event is passed to the onTouchEvent of a child view at a certain level and the method returns false, the event is passed up from the view and is received by onTouchEvent. If the onTouchEvent passed to the top also returns false, the event will "disappear" and the next event will not be received. (This refers to a series of events between down and up). In Touchevent, the return value is true, indicating that the event was consumed.

This may be a bit abstract. Let's change our minds here.

If the click event is a problem, the problem is eventually assigned to an engineer supervisor (event distribution), but the supervisor is too busy to deal with it (intercept Touch Event returns false, not intercept), the supervisor assigns the task to the engineer, and the engineer fails (onTouch Event returns false), so it can only be handled by the engineer-supervisor (above). Level onTouchEvent is called. If the supervisor hasn't done it, it can only be handed over to the superior who is going up again. In this way, it is thrown up one layer at a time. So event delivery processing is probably the same process.

(3) After preliminary understanding of the transmission sequence of touch events, we further analyze the above event transmission process through examples.

First create an EventButton class that inherits Button:

  1. package com.zejian.android_event;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.widget.Button;  
  8.   
  9. /** 
  10.  * @author : zejian 
  11.  * @time : 2016 January 7, 2001, 10:10:34 p.m. 
  12.  * @email : shinezejian@163.com 
  13.  * @description : 
  14.  */  
  15. public class EventButton extends Button {  
  16.     public EventButton(Context context) {  
  17.         super(context);  
  18.     }  
  19.     public EventButton(Context context, AttributeSet attrs) {  
  20.         super(context, attrs);  
  21.     }  
  22.     public EventButton(Context context, AttributeSet attrs, int defStyleAttr) {  
  23.         super(context, attrs, defStyleAttr);  
  24.     }  
  25.     @Override  
  26.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  27.         String str  = "ACTION_DOWN";  
  28.         switch (ev.getAction()){  
  29.             case MotionEvent.ACTION_DOWN:  
  30.                 str = "ACTION_DOWN";  
  31.                 break ;  
  32.             case MotionEvent.ACTION_MOVE:  
  33.                 str = "ACTION_MOVE";  
  34.                 break;  
  35.             case MotionEvent.ACTION_UP:  
  36.                 str = "ACTION_UP";  
  37.                 break;  
  38.         }  
  39.         Log.v("wzj""=====View-----=====dispatchTouchEvent---========"+str) ;  
  40.         return super.dispatchTouchEvent(ev);  
  41.     }  
  42.     @Override  
  43.     public boolean onTouchEvent(MotionEvent event) {  
  44.         String str  = "ACTION_DOWN";  
  45.         switch (event.getAction()){  
  46.             case MotionEvent.ACTION_DOWN:  
  47.                 str = "ACTION_DOWN";  
  48.                 break ;  
  49.             case MotionEvent.ACTION_MOVE:  
  50.                 str = "ACTION_MOVE";  
  51.                 break;  
  52.             case MotionEvent.ACTION_UP:  
  53.                 str = "ACTION_UP";  
  54.                 break;  
  55.         }  
  56.         Log.v("wzj","=====View-----=====onTouchEvent---------========"+str) ;  
  57. //        return super.onTouchEvent(event);  
  58.         return true;  
  59.     }  
  60. }  
There is no complex logic in this class. We just rewrite the dispatchTouchEvent () method and onTouchEvent () method.


Then we create a GroupViewEvent that inherits from Linear Layout:

  1. <span style="font-size:14px;">package com.zejian.android_event;  
  2.   
  3. import android.annotation.SuppressLint;  
  4. import android.content.Context;  
  5. import android.util.AttributeSet;  
  6. import android.util.Log;  
  7. import android.view.MotionEvent;  
  8. import android.widget.LinearLayout;  
  9.   
  10. /** 
  11.  * @author : zejian 
  12.  * @time : 2016 January 7, 2001, 10:11:45 p.m. 
  13.  * @email : shinezejian@163.com 
  14.  * @description : 
  15.  */  
  16. public class GroupViewLayout extends LinearLayout {  
  17.   
  18.     public GroupViewLayout(Context context) {  
  19.         super(context);  
  20.     }  
  21.   
  22.     public GroupViewLayout(Context context, AttributeSet attrs) {  
  23.         super(context, attrs);  
  24.     }  
  25.   
  26.     @SuppressLint("NewApi")  
  27.     public GroupViewLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
  28.         super(context, attrs, defStyleAttr);  
  29.     }  
  30.   
  31.     @Override  
  32.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  33.         boolean handle = false;  
  34.         String str = "ACTION_DOWN";  
  35.         switch (ev.getAction()) {  
  36.         case MotionEvent.ACTION_DOWN:  
  37.             str = "ACTION_DOWN";  
  38.             // handle = true ;  
  39.             break;  
  40.         case MotionEvent.ACTION_MOVE:  
  41.             str = "ACTION_MOVE";  
  42.             // handle = true ;  
  43.             break;  
  44.         case MotionEvent.ACTION_UP:  
  45.             str = "ACTION_UP";  
  46.             handle = true;  
  47.             break;  
  48.         }  
  49.         Log.v("wzj""=====ViewGroup=====onInterceptTouchEvent========" + str);  
  50. //      if (handle) {  
  51. //          return handle;  
  52. //      }  
  53.         return super.onInterceptTouchEvent(ev);  
  54.     }  
  55.   
  56.     @Override  
  57.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  58.         boolean handle = false;  
  59.         String str = "ACTION_DOWN";  
  60.         switch (ev.getAction()) {  
  61.         case MotionEvent.ACTION_DOWN:  
  62.             str = "ACTION_DOWN";  
  63.             // handle = true ;  
  64.             break;  
  65.         case MotionEvent.ACTION_MOVE:  
  66.             str = "ACTION_MOVE";  
  67.             // handle = true ;  
  68.             break;  
  69.         case MotionEvent.ACTION_UP:  
  70.             str = "ACTION_UP";  
  71.             // handle = true ;  
  72.             break;  
  73.         }  
  74.         Log.v("wzj""=====ViewGroup=====dispatchTouchEvent---========" + str);  
  75.         // if(handle){  
  76.         // return handle ;  
  77.         // }  
  78.         return super.dispatchTouchEvent(ev);  
  79.     }  
  80.   
  81.     @Override  
  82.     public boolean onTouchEvent(MotionEvent event) {  
  83.         String str = "ACTION_DOWN";  
  84.         boolean handle = false;  
  85.         switch (event.getAction()) {  
  86.         case MotionEvent.ACTION_DOWN:  
  87.             str = "ACTION_DOWN";  
  88.             handle = true;  
  89.             break;  
  90.         case MotionEvent.ACTION_MOVE:  
  91.             str = "ACTION_MOVE";  
  92.             // handle = true ;  
  93.             break;  
  94.         case MotionEvent.ACTION_UP:  
  95.             str = "ACTION_UP";  
  96.             // handle = true ;  
  97.             break;  
  98.         }  
  99.         Log.v("wzj""=====ViewGroup=====onTouchEvent---------========" + str);  
  100.         // if(handle){  
  101.         // return handle ;  
  102.         // }  
  103.         return super.onTouchEvent(event);  
  104.     }  
  105. }</span>  

This is a layout class that stores the two custom EventButton and EventTextView controls above.

The xml layout file is as follows:

  1. <com.zejian.android_event.GroupViewLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:orientation="vertical"  
  5.     android:layout_gravity="center_horizontal"  
  6. >  
  7.     <com.zejian.android_event.EventButton  
  8.         android:layout_width="120dp"  
  9.         android:layout_height="60dp"  
  10.         android:text="eventBut"/>  
  11. </com.zejian.android_event.GroupViewLayout>  


Finally, the MainActivity class:

  1. package com.zejian.android_event;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7.   
  8. public class MainActivity extends Activity {  
  9.       
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);       
  14.     }  
  15.     /** 
  16.      * The main responsibility is to distribute ontouch events, and ontouch events are not handled by itself. The distribution method is determined by its return value. 
  17.      *  Return value: 
  18.      *  When the return value is true, the event is handed over to the onTouchEvent of the current view itself. 
  19.      *  When the return value is false, the event is handed over to onInterceptTouchEvent() of the current view itself. 
  20.      *  Then it decides the direction of event transmission. 
  21.      */  
  22.     @Override  
  23.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  24.         String tag = "ACTION_DOWN";  
  25.         switch (ev.getAction()) {  
  26.         case MotionEvent.ACTION_DOWN:  
  27.             tag = "ACTION_DOWN";  
  28.             break;  
  29.         case MotionEvent.ACTION_MOVE:  
  30.             tag = "ACTION_MOVE";  
  31.             break;  
  32.         case MotionEvent.ACTION_UP:  
  33.             tag = "ACTION_UP";  
  34.             break;  
  35.         }  
  36.         Log.v("wzj","=====Activity =====dispatchTouchEvent---========"+tag) ;  
  37.         return super.dispatchTouchEvent(ev);  
  38.     }  
  39.       
  40.       
  41.      @Override  
  42.       public boolean onTouchEvent(MotionEvent event) {  
  43.         String tag  = "ACTION_DOWN";  
  44.         boolean handle = false ;  
  45.         switch (event.getAction()){  
  46.           case MotionEvent.ACTION_DOWN:  
  47.               tag  = "ACTION_DOWN";  
  48.             break ;  
  49.           case MotionEvent.ACTION_MOVE:  
  50.               tag = "ACTION_MOVE";  
  51.             handle = true ;  
  52.             break;  
  53.           case MotionEvent.ACTION_UP:  
  54.               tag  = "ACTION_UP";  
  55.             break;  
  56.         }  
  57.         Log.v("wzj","=====Activity =====onTouchEvent---------========"+tag ) ;  
  58.         return super.onTouchEvent(event);  
  59.       }  
  60.       
  61. }  

Then we run it to see the printed logs (events are not consumed):



Events of dispatch TouchEvent of Activity are distributed to ViewGroup (temporarily ignoring the transfer of windows Phone Window and DecorView). When dispatch TouchEvent returns to false, ViewGroup intercepts onIntercept TouchEvent by event (only). ViewGroup has this method) to determine whether to intercept an event, if it returns false, that is, if it does not intercept, it distributes the event to dispatchTouchEvent, the distribution mechanism of the child View; when the child View gets the event, it starts calling the onTouchEvent method, and if the onTouchEvent of the View returns false, that is, it does not consume the event, it passes the event to the parent ViewGroup. OnTouchEvent, similarly, ViewGroup does not consume the event and continues to pass it down to Activity's onTouchEvent event processing. (Of course, this is just an analysis of ACTION_DOWM events, the remaining ACTION_MOVE Similar to ACTION_UP, in order to better understand the following I give the illustration:


From the above figure, we can draw the following conclusions:

1. Android event delivery is hierarchical.

2. dispatch TouchEvent is transferred from the bottom to the top, while onTouchEvent is just the opposite.

3. When onInterceptTouchEvent returns to true, the same level of onTouchEvent will be executed, while dispatchTouchEvent and onTouchEvent will terminate the transfer of events when they return to true.

4. If the event is not consumed at the end of view (so-called consumption means that view does not process the event), then it will be handed over to activity.

Well, after that, let's ask a more detailed question: the last two lines of information log above, and then let's take a look at a set of debugging information:


In fact, this is the ViewGroup in the code. And View's onTouchEvent event in action When returning false for ACTION_DOWN, that is, the event is not consumed, so eventually it will be returned to Activity, so you can see that only in ACTION_DOWN ACTION_DOWN is executed according to the flow chart we analyzed, while ACTION_MOVE and ACTION_UP only execute dispatch TouchEvent and onTouchEvent of Activity, but why? In fact, the process of the above chart is not a problem, only some events are intercepted or consumed.

ViewGroup and View's dispatch TouchEvent distribution events are not intercepted when action is ACTION_DOWN and onTouchEvent's processing events are not consumed when action is ACTION_DOWN. When action is ACTION_MOVE or ACTION_UP, then ViewGroup and View's dispatch TouchEvent and onTouchEvent will not be invoked again, as in the case of superiors handing the problem over to engineers. As a result, if the engineer fails to solve the problem, the problem will not be handed over to the engineer again in the short term. It's easy to understand that he will eventually lead himself in dealing with this sequence of problems, so dispatch Touch Event and onTouch Event are the only ones. The activity is called. Remember that ACTION_DOWN, ACTION_MOVE and ACTION_UP are generally sequential operations.


Let's look at a set of log information, (EventButton)View's onTouchEvent event in action To return true for ACTION_DOWN is to consume the event:


Therefore, now we can see that whether ACTION_DOWN or ACTION_MOVE or ACTION_UP are in the order of the above graph, the only difference is that onTouchEvent consumes events when the subclass View is in place, so there is no need to go back to the parent class, so Activity or GroupView naturally will not call onTouchEvent method. We all have a clear understanding. Okay, here's the end of the introduction to event mechanism. In the next article, we will start with the analysis of the whole process of event monitoring mechanism from the perspective of source code.

Topics: Android supervisor Mobile Windows