Android View touch event Transfer Mechanism

Source: Internet
Author: User

Android View touch event Transfer Mechanism

PS: from the current perspective, you may feel bad about the previous blogs. You may feel the same if you look at the current blog for a while. So I learned to discover and understand new things every moment.

Introduction

Because the quality of a previously written blog about the sequence of Android event transfer is too poor, it may be because it cannot be understood, so it recently took a lot of time to go back to the Android source code, I have a new understanding, so I plan to recompile this blog. Understanding the Android touch event transfer mechanism helps you develop and customize gesture effects in the future. Note: This blog is analyzed based on the Android2.0 source code. No matter the old version or the new version of Android, its internal touch event transfer mechanism remains unchanged. It is said that the source code of Android2.0 is relatively small, which is easy for readers to understand.

Example

Customize a MyCustomView

public class MyCustomView extends View {    private String TAG = MyButton;    public MyCustomView(Context context) {        super(context);    }    public MyCustomView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, dispatchTouchEvent----->>ACTION_DOWN);                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, dispatchTouchEvent----->>ACTION_MOVE);                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, dispatchTouchEvent----->>ACTION_UP);                break;        }        return super.dispatchTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, onTouchEvent----->>ACTION_DOWN);                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, onTouchEvent----->>ACTION_MOVE);                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, onTouchEvent----->>ACTION_UP);                break;        }        return super.onTouchEvent(event);    }}

Override the dispatchTouchEvent and onTouchEvent methods to add printing logs for three touch events.

The MainActivity call is as follows:

public class MainActivity extends Activity {    private MyCustomView button;    private String TAG = MainActivity;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        button = (MyCustomView) findViewById(R.id.button);        Log.e(TAG, the view is clickable  + button.isClickable());        button.setClickable(true);        button.setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                switch (event.getAction()) {                    case MotionEvent.ACTION_DOWN:                        Log.e(TAG, onTouch------->>ACTION_DOWN);                        break;                    case MotionEvent.ACTION_MOVE:                        Log.e(TAG, onTouch------->>ACTION_MOVE);                        break;                    case MotionEvent.ACTION_UP:                        Log.e(TAG, onTouch------->>ACTION_UP);                        break;                }                return false;            }        });        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Log.e(TAG, onClick------->>onClick);            }        });    }}

Click custom MyCustomView and the result is printed as follows:

07-29 11:03:51.714  20278-20278/com.xjp.toucheventdemo E/MainActivity﹕ the view is clickable false07-29 11:03:54.877  20278-20278/com.xjp.toucheventdemo E/MyButton﹕ dispatchTouchEvent----->>ACTION_DOWN07-29 11:03:54.878  20278-20278/com.xjp.toucheventdemo E/MainActivity﹕ onTouch------->>ACTION_DOWN07-29 11:03:54.878  20278-20278/com.xjp.toucheventdemo E/MyButton﹕ onTouchEvent----->>ACTION_DOWN07-29 11:03:54.929  20278-20278/com.xjp.toucheventdemo E/MyButton﹕ dispatchTouchEvent----->>ACTION_MOVE07-29 11:03:54.929  20278-20278/com.xjp.toucheventdemo E/MainActivity﹕ onTouch------->>ACTION_MOVE07-29 11:03:54.929  20278-20278/com.xjp.toucheventdemo E/MyButton﹕ onTouchEvent----->>ACTION_MOVE07-29 11:03:54.929  20278-20278/com.xjp.toucheventdemo E/MyButton﹕ dispatchTouchEvent----->>ACTION_UP07-29 11:03:54.931  20278-20278/com.xjp.toucheventdemo E/MainActivity﹕ onTouch------->>ACTION_UP07-29 11:03:54.931  20278-20278/com.xjp.toucheventdemo E/MyButton﹕ onTouchEvent----->>ACTION_UP07-29 11:03:54.936  20278-20278/com.xjp.toucheventdemo E/MainActivity﹕ onClick------->>onClick

Analysis: As shown in the preceding figure

There are three main types of touch events: ACTION_DOWN, ACTION_MOVE, and ACTION_UP. That is, execute the action of pressing ACTION_DOWN first. After the action is pressed, the finger may move. when the action is moved, the action of ACTION_MOVE is triggered. When the finger is lifted, the action of ACTION_UP is triggered, at this point, the sequential execution of touch events has ended. Of course, there are more than three types of touch events, but here we mainly analyze these three types. The execution sequence of the touch event process is dispatchTouchEvent, onTouch, and onTouchEvent. Finally, The onClick event is executed. That is, the order should be: dispatchTouchEvent-> onTouch-> onTouchEvent-> onClick click event.

Why are the above three phenomena and situations? Now we can only see the results in the print log, but do not know its internal reasons, to snoop its internal reasons, read the fuck code. View Analysis Based on Android2.0 source code. The View touch event distribution starts from the View # dispatchTouchEvent method. As for why, let's talk about it later.

View # dispatchTouchEvent touch event Distribution
public boolean dispatchTouchEvent(MotionEvent event) {        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&                mOnTouchListener.onTouch(this, event)) {            return true;        }        return onTouchEvent(event);    }

Analysis: The method implementation is very simple. if the if condition is met, the true exit method is returned. if the condition is not met, the onTouchEvent method is executed and the return value of this method is returned.
1. Under what circumstances will mOnTouchListener be satisfied! = Null condition? View the source code and call the following method:

public void setOnTouchListener(OnTouchListener l) {        mOnTouchListener = l;    }

After the developer sets the View # setOnTouchListener touch event for the corresponding View, mOnTouchListener! The = null condition is true.
2. View is enabled by default, so the second condition is true.
3. Currently, both conditions are true. Execute the third condition interface method mOnTouchListener. onTouch (this, event ). Determines whether the if condition is true based on the return value of this method. This method is implemented when the developer sets the View # setOnTouchListener touch event. When the onTouch method returns false, the dispatchTouchEvent method executes the onTouchEvent method. Otherwise, the onTouchEvent method is not executed.

Summary:
1. the return value of the onTouch interface method determines whether to execute the onTouchEvent method.
2. As long as the onTouch interface method returns true, the dispatchTouchEvent method returns true. Otherwise, the value of dispatchTouchEvent is determined based on the onTouchEvent method return value.

View # onTouchEvent

OnTouchEvent method:

public boolean onTouchEvent(MotionEvent event) {        final int viewFlags = mViewFlags;     ................        if (((viewFlags & CLICKABLE) == CLICKABLE ||                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {            switch (event.getAction()) {                case MotionEvent.ACTION_UP:                    if ((mPrivateFlags & PRESSED) != 0) {                        // 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 (!mHasPerformedLongPress) {                            // This is a tap, so remove the longpress check                            if (mPendingCheckForLongPress != null) {                                removeCallbacks(mPendingCheckForLongPress);                            }                            // Only perform take click actions if we were in the pressed state                            if (!focusTaken) {                                performClick();                            }                        }                        if (mUnsetPressedState == null) {                            mUnsetPressedState = new UnsetPressedState();                        }                        if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now                            mUnsetPressedState.run();                        }                    }                    break;                case MotionEvent.ACTION_DOWN:                    mPrivateFlags |= PRESSED;                    refreshDrawableState();                    if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {                        postCheckForLongClick();                    }                    break;                case MotionEvent.ACTION_CANCEL:                    mPrivateFlags &= ~PRESSED;                    refreshDrawableState();                    break;                case MotionEvent.ACTION_MOVE:                    final int x = (int) event.getX();                    final int y = (int) event.getY();                    // Be lenient about moving outside of buttons                    int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||                            (y < 0 - slop) || (y >= getHeight() + slop)) {                        // Outside button                        if ((mPrivateFlags & PRESSED) != 0) {                            // Remove any future long press checks                            if (mPendingCheckForLongPress != null) {                                removeCallbacks(mPendingCheckForLongPress);                            }                            // Need to switch from pressed to not pressed                            mPrivateFlags &= ~PRESSED;                            refreshDrawableState();                        }                    } else {                        // Inside button                        if ((mPrivateFlags & PRESSED) == 0) {                            // Need to switch from not pressed to pressed                            mPrivateFlags |= PRESSED;                            refreshDrawableState();                        }                    }                   break;            }            return true;        }        return false;    }

Analysis: the code is a bit long. First, this method is used to determine if the if condition is true? If the View is clickable or can be long-pressed, The if condition is true and the if condition is displayed. Execute the ACTION_UP branch.
1. Line 2 of the Code calls the semi mclick method to execute the View click event.

public boolean performClick() {        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);        if (mOnClickListener != null) {            playSoundEffect(SoundEffectConstants.CLICK);            mOnClickListener.onClick(this);            return true;       }        return false;    }

This method determines if mOnClickListener! = If the null condition is true, mOnClickListener. onClick (this) is executed. When is the condition true? When the click listening event is set for the current View, the condition is true. Therefore, the onClick method of the interface is called. The View class has the following methods:

public void setOnClickListener(OnClickListener l) {        if (!isClickable()) {            setClickable(true);        }        mOnClickListener = l;    }

First, determine whether the current View can be clicked? If you cannot click, set it to clickable first, and then assign values to mOnClickListener. Conclusion: you only need to set the setOnClickListener click listener event for any View, regardless of whether the View is clickable or not, and finally set it to clickable status.

2. Only when the current View is in the clickable or long-pressed status will the if condition be judged, then the corresponding gesture operation will be executed, and true will be returned. That is to say, as long as the View is clickable, The onTouchEvent method returns true, and thus the dispatchTouchEvent method returns true.

3. As long as the current View is not clickable or long-pressed, if the condition is not true and no operation is executed, false is returned directly. That is to say, when a View cannot be clicked, The onTouchEvent method returns false, and the dispatchTouchEvent method returns false.

4. The onClick method is executed in the ACTION_UP gesture, that is, the onClick method is executed only when the gesture is lifted.

At this point, the Android View touch event transfer has been analyzed. If all the conditions are met, the entire touch event transfer process is: dispatchTouchEvent-> onTouch-> onTouchEvent-> onClick. Now let's verify the following situations:

The onTouch method returns true.

Changed to the following:

 button.setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                switch (event.getAction()) {                    case MotionEvent.ACTION_DOWN:                        Log.e(TAG, onTouch------->>ACTION_DOWN);                        break;                    case MotionEvent.ACTION_MOVE:                        Log.e(TAG, onTouch------->>ACTION_MOVE);                        break;                    case MotionEvent.ACTION_UP:                        Log.e(TAG, onTouch------->>ACTION_UP);                        break;                }                return true;            }        });

Click View to print it as follows:

07-29 14:42:22.969    2964-2964/com.xjp.toucheventdemo E/MyButton﹕ dispatchTouchEvent----->>ACTION_DOWN07-29 14:42:22.970    2964-2964/com.xjp.toucheventdemo E/MainActivity﹕ onTouch------->>ACTION_DOWN07-29 14:42:22.987    2964-2964/com.xjp.toucheventdemo E/MyButton﹕ dispatchTouchEvent----->>ACTION_MOVE07-29 14:42:22.987    2964-2964/com.xjp.toucheventdemo E/MainActivity﹕ onTouch------->>ACTION_MOVE07-29 14:42:22.987    2964-2964/com.xjp.toucheventdemo E/MyButton﹕ dispatchTouchEvent----->>ACTION_UP07-29 14:42:22.988    2964-2964/com.xjp.toucheventdemo E/MainActivity﹕ onTouch------->>ACTION_UP

When the onTouch method returns true, the onTouchEvnet method is not executed, so the onClick event is not executed. It can be understood that at this time, onTouch has consumed the touch event and will not continue to transmit the touch event. Therefore, if you do not want your View to execute the onTouchEvent method, you can set the onTouch event and return true.

View unclickable status

In the MainActivity at the beginning of the article, I added a line of code button. setClickable (true); the purpose is to make the current View clickable, but by default, most of the View Controls except the Button and TextView controls are not clickable by default, unless you have set View # setClickable (true) or View # setOnClickListener. Now, I remove the button. setClickable (true); line of code in MainActivity without setting the setOnClickListener event,

public class MainActivity extends Activity {    private MyCustomView button;    private String TAG = MainActivity;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        button = (MyCustomView) findViewById(R.id.button);        Log.e(TAG, the view is clickable  + button.isClickable());//        button.setClickable(true);        button.setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                switch (event.getAction()) {                    case MotionEvent.ACTION_DOWN:                        Log.e(TAG, onTouch------->>ACTION_DOWN);                        break;                    case MotionEvent.ACTION_MOVE:                        Log.e(TAG, onTouch------->>ACTION_MOVE);                        break;                    case MotionEvent.ACTION_UP:                        Log.e(TAG, onTouch------->>ACTION_UP);                        break;                }                return false;            }        });//        button.setOnClickListener(new View.OnClickListener() {//            @Override//            public void onClick(View v) {//                Log.e(TAG, onClick------->>onClick);//            }//        });    }}

The Log printing Log is as follows:

07-29 14:57:03.656    4896-4896/com.xjp.toucheventdemo E/MyButton﹕ dispatchTouchEvent----->>ACTION_DOWN07-29 14:57:03.658    4896-4896/com.xjp.toucheventdemo E/MainActivity﹕ onTouch------->>ACTION_DOWN07-29 14:57:03.658    4896-4896/com.xjp.toucheventdemo E/MyButton﹕ onTouchEvent----->>ACTION_DOWN

Don't you know what you found? The print here shows that only the ACTION_DOWN finger operation is performed. What about other gesture operations? No execution. Why?
This is the case: When the onTouch method returns false, the dispatchTouchEvent method will execute the onTouchEvent method. However, because the View cannot be clicked, The onTouchEvent will not execute the if condition body, that is, the onTouchEvent method returns false, which causes the dispatchTouchEvent method to return false. Because the dispatchTouchEvent method returns false, the subsequent gesture operations ACTION_MOVE and ACTION_UP cannot be executed.

Summary:If we divide a gesture operation into three processes: ACTION_DOWN, ACTION_MOVE, and ACTION_UP. The system performs the gesture operation after the corresponding process only when the dispatchTouchEvent method returns true.

Summary

So far, the Android View touch event transfer mechanism has been analyzed and is now reflected in a flowchart:

Sequence of touch events: dispatchTouchEvent-> onTouch-> onTouchEvent-> onClick. OnTouch and onTouchEvent are different: the two methods are called successively in dispatchTouchEvent. The onTouch method is executed only when the View is set with the touch event View # setOnTouchListener; The onTouch method's return value determines whether to execute the onTouchEvent method. The sequence of gesture operations is ACTION_DOWN, ACTION_MOVE, and ACTION_UP. Only the gesture following true is executed when the dispatchTouchEvent method returns true. The onClick method is called in the ACTION_UP gesture of the onTouchEvent, that is, when the gesture is lifted, The onClick method is called only after the gesture operation ends.

 

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.