In the first article, click another event, for example, introduce event Learning. Then, in the second article, find out where the click event is triggered, and find that it is in the onTouchEvent method, the third and fourth sections summarize the common attributes of the onTouchEvent parameter MotionEvent object, getAction (), getX (), and getY ().
The first few articles lay the foundation. Now we can analyze the View based on this knowledge. onTouchEvent is the trigger of the click event found in the second article. The View class is the base class of all views, that is, if the subclass does not override this method, all touch-screen events are forwarded to the View. onTouchEvent processing.
The source code of the analysis is Andorid 4.0 (Android 14). It has been explained before that this feature will not change much because of various versions. View. the onTouchEvent contains 130 rows, which are certainly not directly pasted into the blog and then explained in the form of a line. This article is not so long as I want to write it. It is really a lot of source code of the method itself ,.
I. Overall onTouchEvent Structure
/*** Implement this method to handle touch screen motion events. ** @ param event The motion event. * @ return True if the event was handled, false otherwise. */public boolean onTouchEvent (MotionEvent event) {final int viewFlags = mViewFlags; // if (viewFlags & ENABLED_MASK) = DISABLED) {if (event. getAction () = MotionEvent. ACTION_UP & (mPrivateFlags & PRESSED )! = 0) {// if you hold your finger up, clear the pressed mPrivateFlags & = ~ PRESSED; // The current display is still PRESSED, so refreshDrawableState ();} // It should be the current view that handles the current touch event, but because it is specified as disabled, // Therefore, the current event is still consumed, but no processing is performed. // This process is logical because the current view is touched by the user, although it is set to not trigger any processing. // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (viewFlags & CLICKABLE) = CLICKABLE | (viewFlags & LONG_CLICKABLE) = LONG_CLICKABLE);} // submit the events of the current view to other views for processing. If (mTouchDelegate! = Null) {// although the event is passed to the current view, // if other views increase the touch area by setting mTouchDelegate and the current touch point is within the extended area of other views. If (mTouchDelegate. onTouchEvent (event) {// handle it by the view in the expanded touch area // and consume this touch event return true ;}} // if the current view is click or long-pressed, if (viewFlags & CLICKABLE) = CLICKABLE | (viewFlags & LONG_CLICKABLE) = LONG_CLICKABLE) {switch (event. getAction () {// click and hold the important processing point, and then focus on analyzing this part ......} // click or long press. return true for the current method feedback and processing user operations;} // The current view does not process, the event transfer mechanism searches for other matching views. // After how to find the matching view, it analyzes return false ;}
Before judging and processing various actions, sort out the above source code structure:
1. Whether the current view is disabled (if you are raising your finger, clear the press status)
2. Whether it is within the extended range of other views (implemented through TouchDelegate)
3. If neither of the above conditions is true and the current view is in the click or long press status
4. If none of the above three meets the conditions, false is returned, indicating that the current view does not consume this touch event
The above Source Code involves the following knowledge. You can view the comments of these variables and will not post the source code one by one:
1. mViewFlags is a global variable used to store view status information.
2. TouchDelegate sets the click area of the view (you can increase or decrease the click Area)
3. The current view is DISABLED. Set View to disabled by using View. setEnabled (false)
4. CLICKABLE. Set by using View. setClickable (true)
5. LONG_CLICKABLE can be long-pressed. Set by using View. setLongClickable (true)
2. Click and long-pressed gestures
In the third article "Android Touch event Learning 3 differentiate basic knowledge of various gestures", Android uses various actions to differentiate user behavior, now, you need to determine whether to click or press the actions provided by the system. Based on previous experience, the actions are executed in sequence: ACTION_DOWN, ACTION_MOVE, ACTION_UP, ACTION_CANCEL, when the user's finger is pressed, The ACTION_DOWN is triggered. When the finger is lifted, the ACTION_UP is triggered. When the finger is moved, the ACTION_CANCEL is triggered, when the finger movement is triggered, ACTION_MOVE is executed 0 to multiple times based on the finger movement event. The View. onTouchEvent does not place the source code in this order, but the following parts are explained in sequence.
3. ACTION_DOWN
Case MotionEvent. ACTION_DOWN: mHasPerformedLongPress = false; // 1. trueif (optional mbuttonactionontouchdown (event) {break;} // 2. whether the current view can be rolled (for example, if the current view is ScrollView, true is returned) // Walk up the hierarchy to determine if we're inside a scrolling container. boolean isInScrollingContainer = isInScrollingContainer (); // For views inside a scrolling container, delay the pressed feedback for // a short period in case this is a scroll. if (isInScrollingContainer) {// in the rolling view, do not set it to the pressed state first, because the user may then be a rolling operation // not the focus of this analysis, if you are interested, you can learn about mPrivateFlags | = PREPRESSED; if (mPendingCheckForTap = null) {mPendingCheckForTap = new CheckForTap ();} postDelayed (mPendingCheckForTap, ViewConfiguration. getTapTimeout ();} else {// Not inside a scrolling container, so show the feedback right away // if Not in the scroll view, the following status is reported immediately: mPrivateFlags | = PRESSED; // refresh to the following status refreshDrawableState (); // 3. and 4. checkForLongClick (0);} break;
Note 1, 2, 3, 4. If you have understood the annotation, you can directly ignore the following source code comments and directly jump to the next action ACTION_MOVE.
1. performButtonActionOnTouchDown method source code
/*** Performs button-related actions during a touch down event. ** @ param event The event. * @ return True if the down was consumed. ** @ hide */protected boolean extends mbuttonactionontouchdown (MotionEvent event) {// if you are right-clicking, the first button of the PEN (for details, see the BUTTON_SECONDARY constant annotation) if (event. getButtonState () & MotionEvent. BUTTON_SECONDARY )! = 0) {if (showContextMenu (event. getX (), event. getY (), event. getMetaState () {return true ;}} return false ;}
2. isInScrollingContainer method source code
/** * @hide */ public boolean isInScrollingContainer() { ViewParent p = getParent(); while (p != null && p instanceof ViewGroup) { if (((ViewGroup) p).shouldDelayChildPressedState()) { return true; } p = p.getParent(); } return false; }
Whether it is in the rolling container. For details, see ViewGroup. shouldDelayChildPressedState (). Note that the method returns true by default if it is incompatible, but not in all non-scrollable subclasses.
For example, all views such as LinearLayout that do not scroll will override this method and return false.
3. checkForLongClick method source code
Private void checkForLongClick (int delayOffset) {// The current view can perform the following operations: if (mViewFlags & LONG_CLICKABLE) = LONG_CLICKABLE) {mhas#medlongpress = false; if (mPendingCheckForLongPress = null) {// 4 and 5 mPendingCheckForLongPress = new CheckForLongPress ();} // 4. mPendingCheckForLongPress. remember#wattachcount (); // Add runnable to the Message Queue postDelayed (mPendingCheckForLongPress, ViewConfiguration. getLongPressTimeout ()-delayOffset );}}
ViewConfiguration. getLongPressTimeout () Android 4.0 source code value is 500
4. CheckForLongPress class source code
Class CheckForLongPress implements Runnable {private int mOriginalWindowAttachCount; public void run () {if (isPressed () & (mParent! = Null) & moriginal?wattachcount = m=wattachcount) {// 5. trigger CEO by event if (descrimlongclick () {mHasPerformedLongPress = true ;}} public void remember#wattachcount () {moriginal#wattachcount = m#wattachcount ;}}
It's easy to figure it out here, but it's still true.
Moriginal#wattachcount = m?wattachcount
In CheckForLongPress. the value is assigned in rememberWindowAttachCount (). It is explained that mOriginalWindowAttachCount is an internal variable of CheckForLongPress, while m1_wattachcount is a global variable, and the object of CheckForLongPress is delayed and sent to the message queue, that is to say, if the mWindowAttachCount condition cannot be changed during the delay period, where can this variable be changed? If you do not paste the source code, it will accumulate in the View. dispatchAttachedToWindow method. This method will be called in ViewGroup. addView, that is, adding a View during the second time will not meet the conditions.
5. performLongClick method source code
/*** Call this view's OnLongClickListener, if it is defined. invokes the context menu if the * OnLongClickListener did not consume the event. ** @ return True if one of the above receivers consumed the event, false otherwise. */public boolean invoke mlongclick () {sendAccessibilityEvent (AccessibilityEvent. TYPE_VIEW_LONG_CLICKED); boolean handled = false; if (mOnLongClickListener! = Null) {// trigger the CEO by event handled = mOnLongClickListener. onLongClick (View. this);} if (! Handled) {handled = showContextMenu ();} if (handled) {~mhapticfeedback (HapticFeedbackConstants. LONG_PRESS);} return handled ;}
Iv. ACTION_MOVE
Case MotionEvent. ACTION_MOVE: final int x = (int) event. getX (); final int y = (int) event. getY (); // Be lenient about moving outside of buttons // 1. whether the touch point is in the current view if (! PointInView (x, y, mTouchSlop) {// Outside button // If the finger is removed from the view area // 2. remove the view from the touch state removeTapCallback (); // if the current status is PRESSED if (mPrivateFlags & PRESSED )! = 0) {// Remove any future long press/tap checks // 3. remove unexecuted long-press and touch detection removeLongPressCallback (); // Need to switch from pressed to not pressed // remove the pressed status mPrivateFlags & = ~ PRESSED; // refresh the view refreshDrawableState () ;}} break;
As before, the following are the methods used to process the call by ACTION_MOVE. If you already know, you can ignore the following methods and annotations and directly view the analysis of ACTION_UP.
1. pointInView method source code
/*** Utility method to determine whether the given point, in local coordinates, * is inside the view, where the area of the view is expanded by the slop factor. * This method is called while processing touch-move events to determine if the event * is still within the view. */private boolean pointInView (float localX, float localY, float slop) {// compare the x and y of the touch point with the top, bottom, and left of the current view, check whether the slop length is increased in the top, bottom, and left of the view area, the slop area added outside the view is also clicked into the view. // The goal is to increase the clickable area of the current view to avoid being at the view boundary, even if you move a tiny bit, the system may think that the return localX> =-slop & localY> =-slop & localX <(mRight-mLeft) + slop) & localY <(mBottom-mTop) + slop);} // initialize mTouchSlop in the View constructor. mTouchSlop = ViewConfiguration. get (context ). getScaledTouchSlop ();
2. removeTapCallback source code
/*** Remove the tap detection timer. */private void removeTapCallback () {// Remove the touch detection timer // This Runnable will be created if (mPendingCheckForTap! = Null) {// remove the pre-press status mPrivateFlags & = ~ PREPRESSED; // Delete mPendingCheckForTap removeCallbacks (mPendingCheckForTap) from the Message Queue );}}
3. removeLongPressCallback method source code
/*** Remove the longpress detection timer. */private void removeLongPressCallback () {// Remove long-pressed probe timer if (mPendingCheckForLongPress! = Null) {removeCallbacks (mPendingCheckForLongPress );}}
5. ACTION_UP
Case MotionEvent. ACTION_UP: boolean prepressed = (mPrivateFlags & PREPRESSED )! = 0; if (mPrivateFlags & PRESSED )! = 0 | prepressed) {// The current view is in the pre-pressed or pressed status // if the focus is lost, obtain the focus status // take focus if we don't have it already and we shoshould 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. // if it is in the PRESSED State, set it to the PRESSED state mPrivateFlags | = PRESSED; refreshDrawableState ();} // if no CEO presses if (! MHasPerformedLongPress) {// remove long-pressed probe timer // This is a tap, so remove the longpress checkremoveLongPressCallback (); // Only perform take click actions if we were in the pressed state // click the event if (! FocusTaken) {// Use a Runnable and post this rather than calling // specify mclick directly. this lets other visual state // of the view update before click actions start. if (mPerformClick = null) {// used to perform the click operation mPerformClick = new batch mclick ();} // The purpose of sending a post to runnable to the message queue is: // The message queue is executed in sequence. The current runnable will be executed after the previous post is executed to the runnable of the queue, so that if (! Post (mPerformClick) {// if the execution fails, you must ensure that the click event is triggered. // Therefore, you can directly call the trigger event Method javasmclick ();}}} if (mUnsetPressedState = null) {// 1. clear the pressed state mUnsetPressedState = new UnsetPressedState ();} if (prepressed) {// if it is a pre-pressed state, after a segment event is sent to the Message Queue postDelayed (mUnsetPressedState, ViewConfiguration. getPressedStateDuration ();} else if (! Post (mUnsetPressedState) {// If the post failed, unpress right now // If the execution fails, ensure that the view will not always be in the pressed state // run the mUnsetPressedState once directly. run () ;}// clear the touch removeTapCallback () ;} break;
1. UnsetPressedState class source code
private final class UnsetPressedState implements Runnable { public void run() { setPressed(false); } }
6. ACTION_CANCEL
Case MotionEvent. ACTION_CANCEL: // clear the pressed mPrivateFlags & = ~ PRESSED; // refresh refreshDrawableState (); // clear the touch state removeTapCallback (); break;
ViewConfiguration is a system configuration. mobile phone manufacturers may modify these parameters according to their mobile phone features.
VII. Summary
1. meet certain prerequisites. For example, the current view is not disabled, the current view can be clicked or long-pressed (for details, see 1. Overall onTouchEvent structure)
Then, it is determined by the system feedback.
2. ACTION_DOWN: whether the current view is a rolling view. If not, the current view is displayed as a pressed view, and the operation is performed by the Chief Executive after 500 milliseconds. (For details, see "3. ACTION_DOWN)
3. ACTION_MOVE: If the finger moves out of the current view range, all the statuses of the preceding settings are cleared. If the long press is not executed, it will not be triggered. (For details, see ACTION_MOVE)
4. ACTION_UP)
5. ACTION_CANCEL: Clear all statuses