Analysis of Android Event Delivery Mechanism

Keywords: Android Mobile

"Huanxi Sha"

Always limited body, such as leisure leave easy soul-stirring, wine and song no words.
It's better to take pity on the people in front of you.

Two things need to be done before going straight to the subject:
1 Popularize the knowledge of Android MotionEvent event handling
It is well known that we click on the screen with our hands and release our hands. There are three processes (finger pressing - finger sticking on the screen moving - finger lifting), corresponding to three Motion Event gestures (ACTION_DOWN, ACTION_MOVE, ACTION_UP) in Android. A MotionEvent object is created when the user touches the screen. MotionEvent contains detailed information about the location and time of the touch. MotionEvent objects are passed to the appropriate methods in the program. In these methods, we can analyze which of the three gesture actions MotionEvent objects are to determine the operation to be performed.

2 Open the following portal and understand the wave by yourself.

Popular Explanation of Android Touch Event Transfer Mechanism

You just need to get a general idea of these two points. Let me start with them.~~

Above are three parts of our mobile screen. A means Activity, B means ViewGroup in A, and C means ViewGroup in B. When we click on C area with our hands, we all know that C is clicked, but B and A do not respond, but it is clear that AB includes C, AB should be clicked, but why does AB not respond?
That's because there's an event delivery mechanism inside Android.
Please take a close look at the picture below! ___________
Please take a close look at the picture below! ___________
Please take a close look at the picture below! ___________

There are three methods in the figure:

dispatchTouchEvent() is used to distribute touch events and return true to indicate that the event is not passed by consumption.
onInterceptTouchEvent () is used to intercept touch events and return true to indicate that the event is intercepted and not passed down, but to call the onTouchEvent method of this component

onTouchEvent () Used for consumer touch events returns true to indicate that the event is not passed by the consumer

Let me give a popular example to explain this process.
"A man was a friend of the governor who wanted to work with the governor. The governor promised that he would not do specific work although he had great power. So he handed over the specific execution process to our good mayor (B). The mayor saw the task and thought that I could do it but it was too troublesome. Whether I wanted to do it by myself or not? What about it? Where hesitates (onTouch Event). Finally, the mayor feels too much trouble to let his subordinates do it. So he gives the task to my good county chief (C), who is the lowest level. If C tries to do it by himself, the whole thing ends, and if C can't do it, he reacts to his superior B. If B finds that he can't do it himself, he will continue to respond to A's saying that he can't do it. Finally, this thing is returned to A to let A do it by himself.

Because the real knowledge comes from practice, I will explain it to you with code and screenshots.

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.liang.MainActivity">
   <com.liang.MyGroup
       android:id="@+id/group"
       android:layout_width="200dp"
       android:layout_height="200dp"
       android:orientation="horizontal"
       android:weightSum="1"
       android:layout_centerVertical="true"
       android:layout_centerHorizontal="true">

       <com.liang.MyTextView
           android:id="@+id/View"
           android:layout_centerInParent="true"
           android:gravity="center"
           android:layout_height="100dp"
           android:layout_width="100dp"
           android:text="Hello World!"/>
   </com.liang.MyGroup>
</RelativeLayout>
package com.liang;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.RelativeLayout;

/**
 * Created by Administrator on 2017/3/5.
 */

public class MyGroup extends RelativeLayout {
    public MyGroup(Context context) {
        super(context);
    }

    public MyGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("TAG","viewGroup dispatchTouchEvent "+TestUtils.whichAction(ev));
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("TAG","viewGroup onInterceptTouchEvent "+TestUtils.whichAction(ev));
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("TAG","viewGroup onTouchEvent "+TestUtils.whichAction(event));
        return super.onTouchEvent(event);
    }
}
package com.liang;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

/**
 * Created by Administrator on 2017/3/5.
 */

public class MyTextView extends android.support.v7.widget.AppCompatTextView {
    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i("TAG","view dispatchTouchEvent "+TestUtils.whichAction(event));
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("TAG","view onTouchEvent "+TestUtils.whichAction(event));
        return super.onTouchEvent(event);
    }
}
package com.liang;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private RelativeLayout mRelativeLayout;
    private TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRelativeLayout = (RelativeLayout) findViewById(R.id.group);
        mTextView = (TextView) findViewById(R.id.View);
        mRelativeLayout.setOnClickListener(this);
        mTextView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.group:
                Toast.makeText(this, "Layout is clicked", Toast.LENGTH_SHORT).show();
                break;
            case R.id.View:
                Toast.makeText(this, "View clicked", Toast.LENGTH_SHORT).show();
                break;
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
             Log.i("TAG","activity dispatchTouchEvent "+TestUtils.whichAction(ev));
            return super.dispatchTouchEvent(ev);
    }

}

The whole process of event transmission is roughly finished. Finally, I leave a question. The log information running in my actual project is as follows:

Why was this event passed twice? Are ACTION_DOWN and ACTION_UP executed the same number of times each time?

For the answer to this question, see here:

Details of the distribution mechanism

Posted by gjdunga on Thu, 11 Apr 2019 22:24:31 -0700