Android Event Distribution

The event distribution mechanism of Android is a very important knowledge point, a core and a difficult one. It is a concept that Android developers must understand. If we learn it, we can solve problems such as sliding conflict. This requires an understanding of the event distribution mechanism, which is often associated with views, Viewgroups, and activities, forming a complex mechanism.

We know that when one or more fingers touch the screen, there are usually four types of events:

  1. ACTION_DOWN: Occurs when the screen is pressed, indicating that the touch event starts. It is the first event to occur.
  2. ACTION_UP: The finger is raised, indicating that the touch event is over.
  3. ACTION_MOVE: The finger presses the screen and the distance the finger moves exceeds a certain threshold
  4. ACTION_CANCEL: Event cancelled (not caused by user action)

All these information are included in MotionEvent. There is only one ACTION_DOWN and ACTION_UP, while there may be multiple ACTION_MOVE events. The so-called event distribution is the process of distributing MotionEvent events. It’s finding a View that can handle the MotionEvent, it starts with the Activity, it goes to the ViewGroup, it goes to the View, but the response goes from the bottom up, from the child to the parent, and that makes sense. When we click a button, The button may be included in a ViewGroup, and the event will be distributed from the Activity. If the Activity is to be handled by itself, then the button will not get the event information. If the Activity is to be distributed, then the ViewGroup that wraps the button will be handled. Likewise, if the ViewGroup tries to intercept, the button will not respond, and the event will eventually be consumed by the button.

Event distribution is controlled by three important methods defined in the Activity, View, and ViewGroup. They are:

  1. Activity
Public Boolean dispatchTouchEvent(MotionEvent EV); public boolean onTouchEvent(MotionEvent ev);Copy the code
  1. ViewGroup:
public boolean dispatchTouchEvent(MotionEvent ev);public boolean onInterceptTouchEvent(MotionEvent ev);

public boolean onTouchEvent(MotionEvent ev);
Copy the code
  1. View:
public boolean dispatchTouchEvent(MotionEvent ev);public boolean onTouchEvent(MotionEvent ev);
Copy the code

You can see that there is no onInterceptTouchEvent in the Activity or View. The event distribution on Android needs to understand these three methods, which are analyzed in the following.

dispatchTouchEvent

If the event is passed to the current View, then this method will be called, and it will return a result indicating whether the current event is consumed. False means the event is allowed to continue distribution. True means the event is no longer distributed. It could be the onTouchEvent of the current View or the dispatchTouchEvent of a child View.

I don’t need to tell you that when a click happens, it starts with the Activity’s dispatchTouchEvent method, and then it’s passed to the sub-views, and the Activity’s dispatchTouchEvent method is very simple, and it says, If so, onUserInteraction is called (although this method does nothing), and then the superDispatchTouchEvent method is passed through the layers to the View or ViewGroup’s dispatchTouchEvent.

If superDispatchTouchEvent returns true, the event is over, indicating that a View has been consumed. If false, it will be passed to its own onTouchEvent method for consumption, indicating that all views’ onTouchEvents returned false. There is no one to deal with this matter, but to deal with it.

 public boolean dispatchTouchEvent(MotionEvent ev) {
     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
         onUserInteraction();
     }
     if (getWindow().superDispatchTouchEvent(ev)) {
         return true;
     }
     return onTouchEvent(ev);
 }
Copy the code

To explain in bold: If you want to dig deeper, you’ll need to look at the DecorView, but that’s not much.

So if you imagine in here, you have a ViewGoup in your Activity, and its dispatchTouchEvent method is true, and when you take that ViewGoup alone, then you’re done here, and if you return false, So that’s going to go to the Activity’s onTouchEvent method.

If it returns Super.DispatchTouchEvent (ev), then the problem is that onInterceptTouchEvent returns true. If onInterceptTouchEvent returns true, then it intercepts the current event. His onTouchEvent will be called. If false is returned, the current event will not be intercepted and will continue to be passed to its child elements, whose dispatchTouchEvent method will be called, and the process will continue until the event is finally processed.

If the View is set to OnTouchListener, then the onTouchListener. onTouch method will be called. How the event is handled depends on the return value of onTouch. In an onTouchEvent, if an OnClickListener is set, its onClick will be called.

onInterceptTouchEvent

This method is called within dispatchTouchEvent to determine whether the current event is being intercepted. If the current View intercepts an event, then this method will not be called again in the same sequence of events. It returns false by default and true for interception.

So, in the last picture

onTouchEvent

Also called in dispatchTouchEvent to handle click events. If true is returned, the current View consumed the event.

Common solutions

  1. ScrollView nested ScrollView

So let’s say I have two ScrollViews, and each ScrollView has to slide up and down, and if I don’t solve that, that’s what it looks like.

You only need to customize a ScrollView and write this in onInterceptTouchEvent. RequestDisallowInterceptTouchEvent used to request the parent view don’t intercept Touch events, also is to let the parent view don’t tube onInterceptTouchEvent method, the direct execution distribute events to child view logic. You can also use this method with ListView nested ListView, ScrollView nested ListView,

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    getParent().requestDisallowInterceptTouchEvent(true);
    return super.onInterceptTouchEvent(ev);
}
Copy the code

  1. Put away the soft keyboard

Now that you have an EditText and a Button in your Activity, what would you do if you were asked to fold up the soft keyboard for a single Button or a blank space?

The first thing to make clear is that if we don’t do something, if we hit EditText to make the soft keyboard pop up and click on any other side, the soft keyboard won’t go back, and once we know that the event is being distributed, we can use dispatchTouchEvent in our Activity to handle that, If the event is ACTION_DOWN, get the View with the current focus and hide the soft keyboard.

 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
     if (ev.getAction()==MotionEvent.ACTION_DOWN){
         View v = getCurrentFocus();
         InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
         if(imm ! =null&& v! =null) {
             imm.hideSoftInputFromWindow(v.getWindowToken(), 0); }}return super.dispatchTouchEvent(ev);
 }
Copy the code