前面咱們看了key事件的處理流程,相信你們對此已經有了新的認識,這篇文章我打算帶領你們來看看稍微複雜些的touchhtml
事件的處理流程。說它複雜是由於key事件自己就key down,up,long pressed這幾種,而touch事件支持多指觸摸,給人的app
感受好像同時在發生多個touch事件同樣,因此要處理的手指是多個而不是固定的一個,邏輯上固然也就複雜些了。不過本質ide
上還都是down、up、long pressed,touch的話還有move事件。接下來讓咱們直接進入本文的正題。post
咱們選擇直接從touch事件被分發到view層次結構的根節點DecorView開始提及,代碼以下:ui
@Override public boolean dispatchTouchEvent(MotionEvent ev) { final Callback cb = getCallback(); // 和key事件相似,優先派發給Callback,不然直接交給view層次結構處理 return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }
在前面介紹key事件的處理流程時咱們說過,Callback接口的實現通常是Activity或Dialog,這裏咱們看看Activity的實現:this
/** * Called to process touch screen events. You can override this to * intercept all touch screen events before they are dispatched to the * window. Be sure to call this implementation for touch screen events * that should be handled normally. * * @param ev The touch screen event. * * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); // 一開始down事件的時候,調用此callback } if (getWindow().superDispatchTouchEvent(ev)) { // 交給了此Activity關聯的window處理,實際是派發到view層次結構中 return true; // 若是被window處理掉了(消費了)則返回true, } return onTouchEvent(ev); // 不然調用Activity本身的onTouchEvent進行最後的處理 }
緊接着,咱們看看交給window處理的邏輯,這裏咱們不繞彎了,直接看最終調用到的代碼:spa
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); // window直接交給了DecorView處理 } public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); // DecorView直接交給了其super,即ViewGroup處理 }
接下來ViewGroup對touch事件的處理纔是重中之重,不過在看容器類對touch事件處理以前,我以爲有必要先來看看單一的View類.net
對touch事件的處理,畢竟簡單些,並且ViewGroup自身也依賴這些實現,代碼以下:rest
/** * Pass the touch screen motion event down to the target view, or this * view if it is the target. * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. */ public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { // 通常都成立 //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { // 先在ENABLED狀態下嘗試調用onTouch方法 return true; // 若是被onTouch處理了,則直接返回true } // 從這裏咱們能夠看出,當你既設置了OnTouchListener又設置了OnClickListener,那麼當前者返回true的時候,
// onTouchEvent沒機會被調用,固然你的OnClickListener也就不會被觸發;另外還有個區別就是onTouch裏能夠
// 收到每次touch事件,而onClickListener只是在up事件到來時觸發。 if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; // 上面的都沒處理,則返回false } /** * Implement this method to handle touch screen motion events. * <p> * If this method is used to detect click actions, it is recommended that * the actions be performed by implementing and calling * {@link #performClick()}. This will ensure consistent system behavior, * including: * <ul> * <li>obeying click sound preferences * <li>dispatching OnClickListener calls * <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when * accessibility features are enabled * </ul> * * @param event The motion event. * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { // View對touch事件的默認處理邏輯 final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { // DISABLED的狀態下 if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); // 復原,若是以前是PRESSED狀態 } // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (((viewFlags & CLICKABLE) == CLICKABLE || // CLICKABLE或LONG_CLICKABLE的view標記爲對事件處理了, (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); // 只不過是以do nothing的方式處理了。 } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { // 若是有TouchDelegate的話,優先交給它處理 return true; // 處理了返回true,不然接着往下走 } } if (((viewFlags & CLICKABLE) == CLICKABLE || // View能對touch事件響應的前提要麼是CLICKABLE要麼是LONG_CLICKABLE (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: // UP事件
// 若是外圍有能夠滾動的parent的話,當按下時會設置這個標誌位 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;
// 這行代碼就是咱們上篇博客中說的,設置了FocusableInTouchMode後,View在點擊的時候就會
// 嘗試requestFocus(),並將focusToken設置爲true if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); // 能進來這個if,通常都會返回true } 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.
// 在前面down事件的時候咱們延遲顯示view的pressed狀態 setPressed(true); // 直到up事件到來的時候才顯示pressed狀態 } if (!mHasPerformedLongPress) { // 若是沒有長按發生的話 // This is a tap, so remove the longpress check removeLongPressCallback(); // 移除長按callback // Only perform take click actions if we were in the pressed state if (!focusTaken) { // 看到沒,focusTaken是false纔會進入下面的if語句 // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start.
// 也就是說在touch mode下,不take focus的view第一次點擊的時候纔會觸發onClick事件 if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { // 若是post失敗了,則直接調用performClick()方法 performClick(); // 這2行代碼會觸發onClickListener } } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); // unset按下狀態的 } if (prepressed) { postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: // DOWN事件 mHasPerformedLongPress = false; if (performButtonActionOnTouchDown(event)) { break; } // 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) { // 若是是在能夠滾動的container裏面的話 mPrivateFlags |= PFLAG_PREPRESSED; // 設置PREPRESSED標誌位 if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } // 延遲pressed feedback postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away setPressed(true); // 不然直接顯示pressed feedback checkForLongClick(0); // 並啓動長按監測 } break; case MotionEvent.ACTION_CANCEL: // 針對CANCEL事件的話,恢復各類狀態,移除各類callback setPressed(false); removeTapCallback(); removeLongPressCallback(); break; case MotionEvent.ACTION_MOVE: // MOVE事件 final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { // 若是移動到view的邊界以外了, // Outside button removeTapCallback(); // 則取消Tap callback,這樣當你鬆手的時候onClick不會被觸發 if ((mPrivateFlags & PFLAG_PRESSED) != 0) { // 當已是按下狀態的話 // Remove any future long press/tap checks removeLongPressCallback(); // 移除長按callback setPressed(false); // 恢復按下狀態 } } break; } return true; // 最後返回true,表示對touch事件處理過了,消費了 } return false; // 既不能單擊也不能長按的View,返回false,表示不處理touch事件 }
其中涉及到好多小的方法,都很是簡單,再也不一一例出。code
在開始介紹ViewGroup對touch事件的處理以前,咱們還得先看看ViewGroup的一個內部類TouchTarget,由於它描述的就是被
touch的view和touch的手指相關的信息,代碼以下:
/* Describes a touched view and the ids of the pointers that it has captured. * * This code assumes that pointer ids are always in the range 0..31 such that * it can use a bitfield to track which pointer ids are present. * As it happens, the lower layers of the input dispatch pipeline also use the * same trick so the assumption should be safe here... */ private static final class TouchTarget { private static final int MAX_RECYCLED = 32; private static final Object sRecycleLock = new Object[0]; private static TouchTarget sRecycleBin; // 回收再利用的鏈表頭 private static int sRecycledCount; public static final int ALL_POINTER_IDS = -1; // all ones // The touched child view. public View child; // The combined bit mask of pointer ids for all pointers captured by the target. public int pointerIdBits; // The next target in the target list. public TouchTarget next; private TouchTarget() { }
// 看到這個有沒有很眼熟?是的Message裏也有相似的實現,咱們在以前介紹Message的文章裏詳細地分析過 public static TouchTarget obtain(View child, int pointerIdBits) { final TouchTarget target; synchronized (sRecycleLock) { if (sRecycleBin == null) { // 沒有能夠回收的目標,則new一個返回 target = new TouchTarget(); } else { target = sRecycleBin; // 重用當前的sRecycleBin sRecycleBin = target.next; // 更新sRecycleBin指向下一個 sRecycledCount--; // 重用了一個,可回收的減1 target.next = null; // 切斷next指向 } } target.child = child; // 找到合適的target後,賦值 target.pointerIdBits = pointerIdBits; return target; } public void recycle() { // 基本是obtain的反向過程 synchronized (sRecycleLock) { if (sRecycledCount < MAX_RECYCLED) { next = sRecycleBin; // next指向舊的可回收的頭 sRecycleBin = this; // update舊的頭指向this,表示它本身如今是可回收的target(第一個) sRecycledCount += 1; // 多了一個可回收的 } else { next = null; // 沒有next了 } child = null; // 清空child字段 } } }
理解了TouchTarget,如今是時候直面ViewGroup的dispatchTouchEvent()方法了,代碼以下:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { // view沒有被遮罩,通常都成立 final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == MotionEvent.ACTION_DOWN) { // 一堆touch事件(從按下到鬆手)中的第一個down事件 // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. cancelAndClearTouchTargets(ev); resetTouchState(); // 做爲新一輪的開始,reset全部相關的狀態 } // Check for interception. final boolean intercepted; // 檢查是否要攔截 if (actionMasked == MotionEvent.ACTION_DOWN // down事件 || mFirstTouchTarget != null) { // 或者以前的某次事件已經經由此ViewGroup派發給children後被處理掉了 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { // 只有容許攔截才執行onInterceptTouchEvent方法 intercepted = onInterceptTouchEvent(ev); // 默認返回false,不攔截 ev.setAction(action); // restore action in case it was changed } else { intercepted = false; // 不容許攔截的話,直接設爲false } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. // 在這種狀況下,actionMasked != ACTION_DOWN && mFirstTouchTarget == null // 第一次的down事件沒有被此ViewGroup的children處理掉(要麼是它們本身不處理,要麼是ViewGroup從一 // 開始的down事件就開始攔截),則接下來的全部事件 // 也沒它們的份,即不處理down事件的話,那表示你對後面接下來的事件也不感興趣 intercepted = true; // 這種狀況下設置ViewGroup攔截接下來的事件 } // Check for cancelation. final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; // 此touch事件是否取消了 // Update list of touch targets for pointer down, if needed. // 是否拆分事件,3.0(包括)以後引入的,默認拆分 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; TouchTarget newTouchTarget = null; // 接下來ViewGroup判斷要將此touch事件交給誰處理 boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { // 沒取消也不攔截,便是個有效的touch事件 if (actionMasked == MotionEvent.ACTION_DOWN // 第一個手指down || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) // 接下來的手指down || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { final int actionIndex = ev.getActionIndex(); // always 0 for down final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { // 基本都成立 final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; final boolean customOrder = isChildrenDrawingOrderEnabled(); // 從最後一個向第一個找 for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = children[childIndex]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { continue; // 不知足這2個條件直接跳過,看下一個child } // child view能receive touch事件並且touch座標也在view邊界內 newTouchTarget = getTouchTarget(child);// 查找child對應的TouchTarget if (newTouchTarget != null) { // 好比在同一個child上按下了多跟手指 // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; // newTouchTarget已經有了,跳出for循環 } resetCancelNextUpFlag(child); // 將此事件交給child處理 // 有這種狀況,一個手指按在了child1上,另外一個手指按在了child2上,以此類推 // 這樣TouchTarget的鏈就造成了 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); // 若是處理掉了的話,將此child添加到touch鏈的頭部 // 注意這個方法內部會更新 mFirstTouchTarget newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; // down或pointer_down事件已經被處理了 break; // 能夠退出for循環了。。。 } } } // 本次沒找到newTouchTarget但以前的mFirstTouchTarget已經有了 if (newTouchTarget == null && mFirstTouchTarget != null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } // while結束後,newTouchTarget指向了最初的TouchTarget newTouchTarget.pointerIdBits |= idBitsToAssign; } } } // 非down事件直接從這裏開始處理,不會走上面的一大堆尋找TouchTarget的邏輯 // Dispatch to touch targets. if (mFirstTouchTarget == null) { // 沒有children處理則派發給本身處理 // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { // Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. Cancel touch targets if necessary. TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { // 遍歷TouchTarget造成的鏈表 final TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; // 已經處理過的再也不讓其處理事件 } else { // 取消child標記 final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; // 若是ViewGroup從半路攔截了touch事件則給touch鏈上的child發送cancel事件 // 若是cancelChild爲true的話 if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; // TouchTarget鏈中任意一個處理了則設置handled爲true } if (cancelChild) { // 若是是cancelChild的話,則回收此target節點 if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; // 至關於從鏈表中刪除一個節點 } target.recycle(); // 回收它 target = next; continue; } } predecessor = target; // 訪問下一個節點 target = next; } } // Update list of touch targets for pointer up or cancel, if needed. if (canceled || actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { // 取消或up事件時resetTouchState resetTouchState(); } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { // 當某個手指擡起時,將其相關的信息移除 final int actionIndex = ev.getActionIndex(); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; // 返回處理的結果 }
接下來咱們看看關於攔截的方法,代碼以下:
/** * Implement this method to intercept all touch screen motion events. This * allows you to watch events as they are dispatched to your children, and * take ownership of the current gesture at any point. * * <p>Using this function takes some care, as it has a fairly complicated * interaction with {@link View#onTouchEvent(MotionEvent) * View.onTouchEvent(MotionEvent)}, and using it requires implementing * that method as well as this one in the correct way. Events will be * received in the following order: * * <ol> * <li> You will receive the down event here. * <li> The down event will be handled either by a child of this view * group, or given to your own onTouchEvent() method to handle; this means * you should implement onTouchEvent() to return true, so you will * continue to see the rest of the gesture (instead of looking for * a parent view to handle it). Also, by returning true from * onTouchEvent(), you will not receive any following * events in onInterceptTouchEvent() and all touch processing must * happen in onTouchEvent() like normal. * <li> For as long as you return false from this function, each following * event (up to and including the final up) will be delivered first here * and then to the target's onTouchEvent(). * <li> If you return true from here, you will not receive any * following events: the target view will receive the same event but * with the action {@link MotionEvent#ACTION_CANCEL}, and all further * events will be delivered to your onTouchEvent() method and no longer * appear here. * </ol> * * @param ev The motion event being dispatched down the hierarchy. * @return Return true to steal motion events from the children and have * them dispatched to this ViewGroup through onTouchEvent(). * The current target will receive an ACTION_CANCEL event, and no further * messages will be delivered here. */ public boolean onInterceptTouchEvent(MotionEvent ev) { return false; // 其默認直接返回false,表示不攔截 }
系統的ScrollView重載了此方法,以便檢測是滾動的時候直接攔截此事件本身處理而不是一味的下發給其children。最後爲了完整性,
我這裏把TouchTarget相關的方法都列一下以便你們參考:
/** * Resets all touch state in preparation for a new cycle. */ private void resetTouchState() { clearTouchTargets(); resetCancelNextUpFlag(this); mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } /** * Resets the cancel next up flag. * Returns true if the flag was previously set. */ private static boolean resetCancelNextUpFlag(View view) { if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; return true; } return false; } /** * Clears all touch targets. */ private void clearTouchTargets() { TouchTarget target = mFirstTouchTarget; if (target != null) { do { TouchTarget next = target.next; target.recycle(); target = next; } while (target != null); mFirstTouchTarget = null; } } /** * Cancels and clears all touch targets. */ private void cancelAndClearTouchTargets(MotionEvent event) { if (mFirstTouchTarget != null) { boolean syntheticEvent = false; if (event == null) { final long now = SystemClock.uptimeMillis(); event = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); event.setSource(InputDevice.SOURCE_TOUCHSCREEN); syntheticEvent = true; } for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { resetCancelNextUpFlag(target.child); dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits); } clearTouchTargets(); if (syntheticEvent) { event.recycle(); } } } /** * Gets the touch target for specified child view. * Returns null if not found. */ private TouchTarget getTouchTarget(View child) { for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) { if (target.child == child) { return target; } } return null; } /** * Adds a touch target for specified child to the beginning of the list. * Assumes the target child is not already present. */ private TouchTarget addTouchTarget(View child, int pointerIdBits) { TouchTarget target = TouchTarget.obtain(child, pointerIdBits); target.next = mFirstTouchTarget; mFirstTouchTarget = target; return target; } /** * Removes the pointer ids from consideration. */ private void removePointersFromTouchTargets(int pointerIdBits) { TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; if ((target.pointerIdBits & pointerIdBits) != 0) { target.pointerIdBits &= ~pointerIdBits; if (target.pointerIdBits == 0) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } // 相似從鏈表中刪除某個特定的節點 private void cancelTouchTarget(View view) { TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; if (target.child == view) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); final long now = SystemClock.uptimeMillis(); MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); event.setSource(InputDevice.SOURCE_TOUCHSCREEN); view.dispatchTouchEvent(event); event.recycle(); return; } predecessor = target; target = next; } }
當遞歸調用一層層向上返回的時候,若是view層次結構沒有處理某個touch事件則最終交給了Activity.onTouchEvent方法,這裏
咱們一併看下:
/** * Called when a touch screen event was not handled by any of the views * under it. This is most useful to process touch events that happen * outside of your window bounds, where there is no view to receive it. * * @param event The touch screen event being processed. * * @return Return true if you have consumed the event, false if you haven't. * The default implementation always returns false. */ public boolean onTouchEvent(MotionEvent event) { // 沒啥須要多說的,好好看方法doc吧 if (mWindow.shouldCloseOnTouch(this, event)) { finish(); return true; } return false; }
至此全部touch事件的處理流程就分析完畢了,最後也推薦你們看看園友的一篇同主題文章:
http://www.cnblogs.com/frydsh/archive/2012/11/08/2760408.html
這篇文章主要是結論性的,適合有個宏觀上的認識,做爲開始你能夠先看看,很是容易理解。還有一篇我以爲也很不錯的文章:
http://blog.csdn.net/dr8737010/article/details/17056821。
固然若是你對內部的實現原理很感興趣,最終仍是要去看源碼來得到更深層次的認識,enjoy。。。