[Android Notes] distribution and consumption of touch events

Source: Internet
Author: User

Methods related to Touch events in Android include dispatchTouchEvent (MotionEvent ev), onInterceptTouchEvent (MotionEvent ev), and onTouchEvent (MotionEvent ev). controls that can respond to these methods include: viewGroup, View, and Activity. Most of the controls that inherit ViewGroup are container controls, such as LinearLayout, while most of the controls that inherit View are display controls such as TextView and ImageView (of course, ViewGroup itself inherits View). The display controls do not have onInterceptTouchEvent. Let's look at some examples.Scenario 1:The Code is as follows:

package com.example.toucheventdemo;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.MotionEvent;public class MainActivity extends Activity{    private static final String TAG = "MainActivity";    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev)    {        switch (ev.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"dispatchTouchEvent--ACTION_UP");            break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event)    {        switch (event.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"onTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"onTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"onTouchEvent--ACTION_UP");            break;        }        return super.onTouchEvent(event);    }}
Log information:
We can see that dispatchTouchEvent is always executed first, and then onTouchEvent is executed ..

Case 2:Change the return value of the above Code dispatchTouchEvent to true. Logs:
We can see that only dispatchTouchEvent is executed, but onTouchEvent is not executed. This indicates that the dispatchTouchEvent is executed before the onTouchEvent in the activity. If the return value of dispatchTouchEvent is set to true, the event is consumed and will not be passed.

Case 3:Add a sub-control using a Custom Button as an example:
package com.example.toucheventdemo;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.Button;public class MyButton extends Button{    private static final String TAG = "MyButton";    public MyButton(Context context)    {        super(context);    }    public MyButton(Context context, AttributeSet attrs)    {        super(context, attrs);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev)    {        switch (ev.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"dispatchTouchEvent--ACTION_UP");            break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event)    {        switch (event.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"onTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"onTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"onTouchEvent--ACTION_UP");            break;        }        return super.onTouchEvent(event);    }}
The mainActivity code is as follows:
package com.example.toucheventdemo;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.View.OnClickListener;public class MainActivity extends Activity{    private static final String TAG = "MainActivity";    private MyButton but = null;    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                but = (MyButton) findViewById(R.id.but);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev)    {        switch (ev.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"dispatchTouchEvent--ACTION_UP");            break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event)    {        switch (event.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"onTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"onTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"onTouchEvent--ACTION_UP");            break;        }        return super.onTouchEvent(event);    }}
Click the Button to view the log:

The execution process is to first capture the ACTION_DWON event from the activity, then call the dispatchTouchEvent of the activity, then bypass the onTouchEvent of the activity to directly pass the event to the Child control, and call the dispatchTouchEvent of MyButton, the onTouchEvent of the control is called later, and the ACTION_UP event is the same process.

Case 4:Similar to scenario 2, change the return value of DispatchTouchEvent of scenario 3 to true. click the Button. Obviously, the touch event will not be distributed to the Button, so the Button log is as follows:
Case 5:Change the return value of DispatchTouchEvent of myButton in case 3 to true. click the button. Obviously, when the touch event is passed to the button, it is first captured by dispatchTouchEvent. Because true is returned, the event is consumed, therefore, the onTouchEvent method of the Button is not called. Log information:
Case 6:Change the onTouchEvent return value of myButton in case 3 to false. Logs:
When the touch event is uploaded to the button, because the onTouchEvent return value is false, the onTouchEvent method of the activity will continue to be executed. Then, the ACTION_UP action will not be passed to the button and will be directly captured by the activity. Case 7:Add onTouchListener and onClickListener to the Activity button.
package com.example.toucheventdemo;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.View.OnClickListener;import android.view.View.OnTouchListener;public class MainActivity extends Activity implements OnClickListener,OnTouchListener{    private static final String TAG = "MainActivity";    private MyButton but = null;    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                but = (MyButton) findViewById(R.id.but);        but.setOnClickListener(this);        but.setOnTouchListener(this);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev)    {        switch (ev.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"dispatchTouchEvent--ACTION_UP");            break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event)    {        switch (event.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"onTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"onTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"onTouchEvent--ACTION_UP");        }        return super.onTouchEvent(event);    }    @Override    public void onClick(View v)    {        Log.i("MyButton","ONCLICK");    }    @Override    public boolean onTouch(View v, MotionEvent event)    {        switch (event.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i("MyButton","onTouch--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i("MyButton","onTouch--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i("MyButton","onTouch--ACTION_UP");            break;        }        return false;    }}
Click the button to print the following information:

First, the touch event is captured by the activity, the dispatchTouchEvent of the activity is called, and then the dispatchTouchEvent of the Button is called to continue distributing the touch event. Then, the onTouch method is called instead of the onTouchEvent of the button, this is because the button registers the onTouchListener. After the onTouch event is processed, because the returned value is false, the touch event is passed to the onTouchEvent of the button. The ACTION_UP event is similar, but the Onclick method is also called after the onTouchEvent of the Button ends.

Case 8:In case 7 Code, set the onTouch method to true. I believe you have guessed it. After you change it to true, the touch event will not be captured by the onTouchEvent of the button, but will be directly consumed. From the log, we can see that:
However, it is strange that the onClick method is not used for execution. We assume that the onClick method is executed in the onTouchEvent method of the button. This is also true: There is a logic in the onTouchEvent method of view:
case MotionEvent.ACTION_UP:                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {                        // take focus if we don't have it already and we should in                        // touch mode.                        boolean focusTaken = false;                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {                            focusTaken = requestFocus();                        }                        if (prepressed) {                            // The button is being released before we actually                            // showed it as pressed.  Make it show the pressed                            // state now (before scheduling the click) to ensure                            // the user sees it.                            setPressed(true);                       }                        if (!mHasPerformedLongPress) {                            // This is a tap, so remove the longpress check                            removeLongPressCallback();                            // Only perform take click actions if we were in the pressed state                            if (!focusTaken) {                                // Use a Runnable and post this rather than calling                                // performClick directly. This lets other visual state                                // of the view update before click actions start.                                if (mPerformClick == null) {                                    mPerformClick = new PerformClick();                                }                                if (!post(mPerformClick)) {                                    performClick();                                }                            }                        }                        if (mUnsetPressedState == null) {                            mUnsetPressedState = new UnsetPressedState();                        }                        if (prepressed) {                            postDelayed(mUnsetPressedState,                                    ViewConfiguration.getPressedStateDuration());                        } else if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now                            mUnsetPressedState.run();                        }                        removeTapCallback();                    }                    break;
The click operation is executed on the ACTION_UP branch. Specifically, the operation is executed by the merge mclick method:
 public boolean performClick() {        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);        ListenerInfo li = mListenerInfo;        if (li != null && li.mOnClickListener != null) {            playSoundEffect(SoundEffectConstants.CLICK);            li.mOnClickListener.onClick(this);            return true;        }        return false;    }
Case 9:The preceding section describes how to add a display control (TextView, Button, etc.) to an activity. The following section describes how to add a container control (such as LinearLayout) to an activity. These controls inherit ViewGroup, except dispatchTouchEvent and ontouchEvent, there is also the onInterceptTouchEvent method, which is used to intercept touch events. By default, false is returned, indicating no interception. Next we will implement a container control and rewrite onTouchEvent, dispatchTouchEvent and onInterceptTouchEvent:
package com.example.toucheventdemo;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.LinearLayout;public class MyLinearLayout extends LinearLayout{    private static final String TAG = "MyLinearLayout";    public MyLinearLayout(Context context)    {        super(context);    }    public MyLinearLayout(Context context, AttributeSet attrs)    {        super(context, attrs);    }        @Override    public boolean onTouchEvent(MotionEvent event)    {        switch (event.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"onTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"onTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"onTouchEvent--ACTION_UP");        }        return super.onTouchEvent(event);    }        @Override    public boolean dispatchTouchEvent(MotionEvent ev)    {        switch (ev.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"dispatchTouchEvent--ACTION_UP");        }        return super.dispatchTouchEvent(ev);    }        @Override    public boolean onInterceptTouchEvent(MotionEvent ev)    {        switch (ev.getAction())        {        case MotionEvent.ACTION_DOWN:            Log.i(TAG,"onInterceptTouchEvent--ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG,"onInterceptTouchEvent--ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG,"onInterceptTouchEvent--ACTION_UP");        }        return super.onInterceptTouchEvent(ev);    }    }
Click the button to view the log:


As you can see, because a container control is added, after the activity executes the dispatchTouchEvent, it distributes the touch event to the Container Control MyLinearLayout, instead of directly passing the touch event to the button, instead, the onInterceptTouchEvent is executed first. This method returns false and does not intercept the touch event. Therefore, the touch event is passed to the button.

Case 10:Set the return value of the onInterceptTouchEvent method of MyLinearLayout to true to intercept the touch event so that it will not be passed down. click the button to view the log:
As you can see, after the touch event is intercepted, it will not be passed to the button, but will be received by the onTouchEvent of MyLinearLayout, and then received and consumed by the onTouchEvent of MainActivity. The ACTION_UP event will be processed directly by the Activity.
Summary: 1. Two Methods of event transmission:
Tunnel mode: pass down from the root element until the sub-element at the inmost layer or stop the transfer due to a certain condition in a certain mona1 element in the middle. Bubble mode: transfers the child elements from the inmost layer out of sequence until the root element or a mona1 element in the middle stops transmission due to a certain condition. 2. android's distribution logic for Touch events is that the View is distributed from the upper layer to the lower layer (the dispatchTouchEvent function) is similar to the tunnel mode, and then the lower layer first processes the Event (mOnTouchListener and then onTouchEvent) and return the processing status (boolean value) up. If true is returned, the upper layer will not process it again, similar to the bubble mode.
3. touch event analysis: Event Distribution: Public boolean dispatchTouchEvent (MotionEvent ev)
DispatchTouchEvent (MotionEvent ev) of the Activity when the Touch event occurs) the method will be in the tunnel mode (pass down from the root element until the inmost layer sub-element or stop passing because of a certain condition in the middle of a mona1 element) the event is passed to the dispatchTouchEvent (MotionEvent ev) method of the outermost View, and the dispatchTouchEvent (MotionEvent ev) method of the View distributes the event. The event distribution logic of dispatchTouchEvent is as follows:
If return true, the event is distributed to the current View and consumed by the dispatchTouchEvent method. At the same time, the event stops being passed down. If return false is returned, event distribution is divided into two types: if the event retrieved by the current View directly comes from the Activity, the event is returned to the onTouchEvent of the Activity for consumption. If the event obtained by the current View comes from the outer parent control, the event is returned to the onTouchEvent of the parent View for consumption. If the system returns the default super. dispatchTouchEvent (ev), the event is automatically distributed to the onInterceptTouchEvent method of the current View.
Event Interception: Public boolean onInterceptTouchEvent (MotionEvent ev)
When the dispatchTouchEvent (MotionEvent ev) method of the outer View returns the system's default super. dispatchTouchEvent (ev), the event is automatically distributed to the onInterceptTouchEvent method of the current View. The event interception logic of onInterceptTouchEvent is as follows:
If onInterceptTouchEvent returns true, the event is intercepted and the intercepted event is handed over to the onTouchEvent of the current View for processing. If onInterceptTouchEvent returns false, the event is released, the event on the current View will be passed to the sub-View, and then the dispatchTouchEvent of the sub-View will start distributing the event; If onInterceptTouchEvent is returned, super. onInterceptTouchEvent (ev), the event is intercepted by default, and the intercepted event is handed over to the onTouchEvent of the current View for processing. Event Response: Public boolean onTouchEvent (MotionEvent ev)
When dispatchTouchEvent returns super. dispatchTouchEvent (ev) and onInterceptTouchEvent returns true or super. onInterceptTouchEvent (ev), onTouchEvent is called. The Event Response logic of onTouchEvent is as follows:
If the event is passed to the onTouchEvent method of the current View, and the method returns false, the event will be passed up from the current View and received by the onTouchEvent of the upper View, If the onTouchEvent passed to the above method also returns false, the event will "disappear" and the next event will not be received.If true is returned, the event is received and consumed. If super. onTouchEvent (ev) is returned, the default event processing logic is the same as when false is returned.




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.