Android View event distribution mechanism - source code parsing

In order to better study View events forward, our custom to inherit a MyButton Button, then spread with the events related to replication method, and then add on the log:

Then our customizable buttons added to the main layout file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <myview.MyLinerLayout
        android:id="@+id/ll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center">
        <myview.MyButton
            android:id="@+id/btn"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="Button" />
    </myview.MyLinerLayout>
</LinearLayout>

 

Button set up in Activity in Touch event:

public class MainActivity extends Activity
{
    protected static final String TAG = "MyButton";
    private Button mButton ;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mButton = (Button) findViewById(R.id.id_btn);
        mButton.setOnTouchListener(new OnTouchListener()
        {
            @Override
            public boolean onTouch(View v, MotionEvent event)
            {
                int action = event.getAction();
 
                switch (action)
                {
                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;
                default:
                    break;
                }
                
                return false;
            }
        });
    }
    
}

 View the print result after clicking the button:

E/MyButton(879): dispatchTouchEvent ACTION_DOWN
 E/MyButton(879): onTouch ACTION_DOWN
 E/MyButton(879): onTouchEvent ACTION_DOWN
 E/MyButton(879): dispatchTouchEvent ACTION_MOVE
 E/MyButton(879): onTouch ACTION_MOVE
 E/MyButton(879): onTouchEvent ACTION_MOVE
 E/MyButton(879): dispatchTouchEvent ACTION_UP
 E/MyButton(879): onTouch ACTION_UP
 E/MyButton(879): onTouchEvent ACTION_UP


You can see, whether it is DOWN, MOVE, UP will be executed in the following order

1、dispatchTouchEvent

2, setOnTouchListener of onTouch

3 onTouchEvent

 

First look at the touch event entry function:

public  Boolean dispatchTouchEvent (the MotionEvent Event) {
         IF (! onFilterTouchEventForSecurity (Event)) {
             return  to false ; 
        } 
 
        IF (! mOnTouchListener = null && (mViewFlags & ENABLED_MASK) == && the ENABLED 
                mOnTouchListener.onTouch ( the this , Event)) {
             return  to true ; 
        } 
        return onTouchEvent (Event); 
    }
can be seen that if mOnTouchListener not empty, and the view is ENABLED, and mOnTouchListener.onTouch (this, event) return value == true, then
dispatchTouchEvent return value is to true, onTouchEvent method will not get executed.
 
public  void setOnTouchListener (OnTouchListener L) { 
        mOnTouchListener = L; 
    } 
if set view
monitor setOnTouchListener, then mOnTouchListener! = null on the establishment, the general view is ENABLED, so onTouch return value of the function determines the onTouchEvent function can be implemented.
 

Look at the source code onTouchEvent function:

public  boolean onTouchEvent (MotionEvent Event) {
         Final  int ViewFlags = mViewFlags; 
        // If the current View is Disabled state and is clickable event will be consumed (return true); can be ignored, not our focus 
        IF ((ViewFlags & ENABLED_MASK) == DISABLED) {
        
            return (((ViewFlags & Clickable) == Clickable || 
                    (ViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); 
        } 
 
        IF (! mTouchDelegate = null ) {
             IF (mTouchDelegate.onTouchEvent (Event)) {
                 return  to true ; 
            }  
        }
         // 
        IF (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
                    if ((mPrivateFlags & 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 (!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) {
                            mPrivateFlags |= PRESSED;
                            refreshDrawableState();
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }
                        removeTapCallback();
                    }
                    break;
 
                case MotionEvent.ACTION_DOWN:
                    if (mPendingCheckForTap == null) {
                        mPendingCheckForTap = new CheckForTap();
                    }
                    mPrivateFlags |= PREPRESSED;
                    mHasPerformedLongPress = false;
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    break;
 
                case MotionEvent.ACTION_CANCEL:
                    mPrivateFlags &= ~PRESSED;
                    refreshDrawableState();
                    removeTapCallback();
                    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 = mTouchSlop;
                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||
                            (y < 0 - slop) || (y >= getHeight() + slop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();
 
                            // Need to switch from pressed to not pressed
                            mPrivateFlags &= ~PRESSED;
                            refreshDrawableState();
                        }
                    }
                    break;
            }
            return true;
        }
 
        return false;
    }
首先分析down事件:
You can see down time will
mPrivateFlags set a PREPRESSED said set mHasPerformedLongPress = false; represents a long press event is not yet triggered
this is a 115ms delay asynchronous tasks, that is, will be executed CheckForTap classes run after 115ms () method.
postDelayed (mPendingCheckForTap, ViewConfiguration.getTapTimeout ()) ;
Private  Final  class CheckForTap the implements the Runnable {
         public  void RUN () { 
            mPrivateFlags & = ~ PREPRESSED; 
            mPrivateFlags | = * PRESSED; 
            refreshDrawableState (); 
            IF ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { 
                postCheckForLongClick (ViewConfiguration.getTapTimeout ()); 
            } 
        } 
    } 
the mPrivateFlags PRESSED set
which also includes a postCheckForLongClick () method, look at postCheckForLongClick () method:
 Private  void postCheckForLongClick ( int DelayOffset) { 
        mHasPerformedLongPress = to false ; 
 
        IF (mPendingCheckForLongPress == null ) { 
            mPendingCheckForLongPress = new new CheckForLongPress (); 
        } 
        mPendingCheckForLongPress.rememberWindowAttachCount (); 
// perform a 500-115ms delay asynchronous tasks postDelayed (mPendingCheckForLongPress, ViewConfiguration. getLongPressTimeout ()
- DelayOffset); }

class CheckForLongPress implements Runnable {
 
        private int mOriginalWindowAttachCount;
 
        public void run() {
            if (isPressed() && (mParent != null)
                    && mOriginalWindowAttachCount == mWindowAttachCount) {
                if (performLongClick()) {
                    mHasPerformedLongPress = true;
                }
            }
        }
If performLongClick () returns true then mHasPerformedLongPress = true.
public  Boolean performLongClick () {   
    sendAccessibilityEvent (AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);   
  
    Boolean the Handled = to false ;  
     IF (! mOnLongClickListener = null ) {  
         // callback user to achieve a long-press operation of the monitor function (OnLongClickListener)    
        the Handled = mOnLongClickListener.onLongClick (View. the this ) ;   
    }   
    IF (! the Handled) {  
         // If the onLongClick OnLongClickListener returns to false   
         // is required to continue processing the long-press event, there is a context menu,    
        the Handled = ShowContextMenu ();   
    }   
    IF(the Handled) {  
         // long-press event has been processed, then the user should be given a haptic feedback    
        performHapticFeedback (HapticFeedbackConstants.LONG_PRESS);   
    }   
    return the Handled;   
} 
can see the user's long press event is implemented in this method which callback, and returns a boolean value.

Summary The following is:

When the user presses the first set will be identified as PREPRESSED, if after the 115, does not lift, the View will be identified and removed PREPRESSED PRESSED set identifier, and a task detection delay issuing a long press, the delay is: ViewConfiguration.getLongPressTimeout () - delayOffset (500ms -115ms), this is just 115ms is detected PREPRESSED time; that is, the user triggers the start date from DOWN, if not lifted within 500ms is considered a long press trigger event.

 

In up look at the event:

If you include PRESSED or PREPRESSED proceeds of execution, that is, whether it is within 115ms or will enter the lift after the execution, if mHasPerformedLongPress == false, enter the IF, or directly out.

If mPerformClick is null, initialization of an instance, and then immediately added to the message queue handler by the tail, if performed directly PerformClick addition failed (); been added, is performed PerformClick () method of mPerformClick run method.

 public  Boolean PerformClick () { 
        sendAccessibilityEvent (AccessibilityEvent.TYPE_VIEW_CLICKED); 
 
        IF (mOnClickListener =! null ) { 
            playSoundEffect (SoundEffectConstants.CLICK); 
            mOnClickListener.onClick ( the this );
             return  to true ; 
        } 
 
        return  to false ; 
    } 
can see that this method which will execution of the user's onClick () callback, and returns true, indicating that the consumption of this event.

Next up event summary: If you set up a press callback and returned true, then performClick () method does not perform, then our onClick () method will not be executed.

 

, Then returns true summarize the whole onTouchEvent method as long as the view has onClick or onLongClick, otherwise false. After this is set Button can not click or press callback, and need to set up a callback TextView

The reason will enter inside the if () body:

    public  void setOnClickListener (@Nullable OnClickListener to L) {
         IF (! {isClickable ()) 
            setClickable ( to true ); 
        } 
        . getListenerInfo () mOnClickListener = L; 
    } 
After calling this method, if the click is not clicking will be provided.

View the entire event forwarding process is:

View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

In dispatchTouchEvent will judge OnTouchListener if OnTouchListener not null and returns true, the event is consumption, onTouchEvent will not be executed; otherwise execution onTouchEvent.

 

Guess you like

Origin www.cnblogs.com/lianzhen/p/11276268.html