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.