在Android中需要經常對用戶手勢進行判斷,在判斷手勢時需要精細的分清楚每個觸摸事件以及每個View對事件的接收情況,在View,ViewGroup,Activity中都可以接收事件,在對事件進行處理時onInterceptTouchEvent、dispatchTouchEvent及onTouchEvent這三個函數的調用順序及關系需要好好理清楚。原理代碼有點多,如果不對著具體事例,理解起來很難。下面對著代碼進行分析。代碼地址為:https://github.com/huangtianyu/DispatchTouchEvent,記得幫忙點Star
MainActivity.java
[java] view plaincopy
package?com.zqc.dispatchtouchevent;?? ?? import?android.app.Activity;?? import?android.os.Bundle;?? import?android.util.Log;?? import?android.view.MotionEvent;?? import?android.view.View;?? ?? import?static?com.zqc.dispatchtouchevent.Constants.TAG;?? ?? public?class?MainActivity?extends?Activity?implements?View.OnTouchListener?{?? ????private?MyView?myView;?? ????@Override?? ????protected?void?onCreate(Bundle?savedInstanceState)?{?? ????????super.onCreate(savedInstanceState);?? ????????Log.e(TAG,?"MainActivity?onCreate");?? ????????setContentView(R.layout.activity_main);?? ????????myView?=?(MyView)?findViewById(R.id.myview);?? ????????myView.setOnTouchListener(MainActivity.this);?? ????}?? ????@Override?? ????public?boolean?dispatchTouchEvent(MotionEvent?ev)?{?? ????????Log.e(TAG,?"MainActivity?dispatchTouchEvent");?? ????????return?super.dispatchTouchEvent(ev);?? ????}?? ????@Override?? ????public?boolean?onTouchEvent(MotionEvent?event)?{?? ????????Log.e(TAG,?"MainActivity?onTouchEvent");?? ????????switch?(event.getAction())?{?? ????????????case?MotionEvent.ACTION_DOWN:?? ????????????????Log.e(TAG,?"MainActivity?onTouchEvent?ACTION_DOWN");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_MOVE:?? ????????????????Log.e(TAG,?"MainActivity?onTouchEvent?ACTION_MOVE");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_CANCEL:?? ????????????????Log.e(TAG,?"MainActivity?onTouchEvent?ACTION_CANCEL");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_UP:?? ????????????????Log.e(TAG,?"MainActivity?onTouchEvent?ACTION_UP");?? ????????????????break;?? ????????????default:?? ????????????????Log.e(TAG,?"MainActivity?onTouchEvent?"?+?event.getAction());?? ????????????????break;?? ????????}?? ????????return?super.onTouchEvent(event);?? ????}?? ????@Override?? ????protected?void?onResume()?{?? ????????Log.e(TAG,?"MainActivity?onResume");?? ????????super.onResume();?? ????}?? ????@Override?? ????protected?void?onPause()?{?? ????????Log.e(TAG,?"MainActivity?onPause");?? ????????super.onPause();?? ????}?? ????@Override?? ????public?boolean?onTouch(View?v,?MotionEvent?event)?{?? ????????Log.e(TAG,?"MainActivity?onTouch");?? ????????switch?(event.getAction()?&?MotionEvent.ACTION_MASK)?{?? ????????????case?MotionEvent.ACTION_DOWN:?? ????????????????Log.e(TAG,?"MainActivity?onTouch?ACTION_DOWN");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_MOVE:?? ????????????????Log.e(TAG,?"MainActivity?onTouch?ACTION_MOVE");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_CANCEL:?? ????????????????Log.e(TAG,?"MainActivity?onTouch?ACTION_CANCEL");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_UP:?? ????????????????Log.e(TAG,?"MainActivity?onTouch?ACTION_UP");?? ????????????????break;?? ????????????default:?? ????????????????Log.e(TAG,?"MainActivity?onTouchEvent?"?+?event.getAction());?? ????????????????break;?? ????????}?? ????????return?false;?? ????}?? }?? MyView.java
[java] view plaincopy
package?com.zqc.dispatchtouchevent;?? ?? import?android.content.Context;?? import?android.util.AttributeSet;?? import?android.util.Log;?? import?android.view.GestureDetector;?? import?android.view.MotionEvent;?? import?android.widget.TextView;?? ?? import?static?com.zqc.dispatchtouchevent.Constants.MY_GESTURE_TAG;?? import?static?com.zqc.dispatchtouchevent.Constants.TAG;?? ?? ?? public?class?MyView?extends?TextView?{?? ????private?Context?mContext;?? ???? ?? ????public?MyView(Context?context)?{?? ????????this(context,?null);?? ????}?? ?? ????public?MyView(Context?context,?AttributeSet?attrs)?{?? ????????super(context,?attrs);?? ????????Log.e(TAG,?"MyView");?? ????????mContext?=?context;?? ???????? ??????? ????}?? ?? ????@Override?? ????public?boolean?onTouchEvent(MotionEvent?event)?{?? ????????Log.e(TAG,?"MyView?onTouchEvent");?? ????????switch?(event.getAction())?{?? ????????????case?MotionEvent.ACTION_DOWN:?? ????????????????Log.e(TAG,?"MyView?onTouchEvent?ACTION_DOWN");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_MOVE:?? ????????????????Log.e(TAG,?"MyView?onTouchEvent?ACTION_MOVE");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_CANCEL:?? ????????????????Log.e(TAG,?"MyView?onTouchEvent?ACTION_CANCEL");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_UP:?? ????????????????Log.e(TAG,?"MyView?onTouchEvent?ACTION_UP");?? ????????????????break;?? ????????????default:?? ????????????????Log.e(TAG,?"MyView?onTouchEvent?"?+?event.getAction());?? ????????????????break;?? ????????}?? ??????? ????????return?super.onTouchEvent(event);?? ????}?? ?? ????@Override?? ????public?boolean?dispatchTouchEvent(MotionEvent?event)?{?? ????????Log.e(TAG,?"MyView?dispatchTouchEvent");?? ????????return?super.dispatchTouchEvent(event);?? ????}?? }?? MyViewGroup.java
[java] view plaincopy
package?com.zqc.dispatchtouchevent;?? ?? import?android.content.Context;?? import?android.util.AttributeSet;?? import?android.util.Log;?? import?android.view.MotionEvent;?? import?android.widget.RelativeLayout;?? ?? import?static?com.zqc.dispatchtouchevent.Constants.TAG;?? ?? public?class?MyViewGroup?extends?RelativeLayout?{?? ????public?MyViewGroup(Context?context)?{?? ????????this(context,?null);?? ????}?? ?? ????public?MyViewGroup(Context?context,?AttributeSet?attrs)?{?? ????????super(context,?attrs);?? ????????Log.e(TAG,?"MyViewGroup");?? ????}?? ?? ????@Override?? ????public?boolean?onInterceptTouchEvent(MotionEvent?ev)?{?? ????????Log.e(TAG,?"MyViewGroup?onInterceptTouchEvent");?? ????????return?super.onInterceptTouchEvent(ev);?? ????}?? ?? ????@Override?? ????public?boolean?dispatchTouchEvent(MotionEvent?ev)?{?? ????????Log.e(TAG,?"MyViewGroup?dispatchTouchEvent");?? ????????return?super.dispatchTouchEvent(ev);?? ????}?? ?? ????@Override?? ????public?boolean?onTouchEvent(MotionEvent?event)?{?? ????????Log.e(TAG,?"MyViewGroup?onTouchEvent");?? ????????switch?(event.getAction())?{?? ????????????case?MotionEvent.ACTION_DOWN:?? ????????????????Log.e(TAG,?"MyViewGroup?onTouchEvent?ACTION_DOWN");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_MOVE:?? ????????????????Log.e(TAG,?"MyViewGroup?onTouchEvent?ACTION_MOVE");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_CANCEL:?? ????????????????Log.e(TAG,?"MyViewGroup?onTouchEvent?ACTION_CANCEL");?? ????????????????break;?? ????????????case?MotionEvent.ACTION_UP:?? ????????????????Log.e(TAG,?"MyViewGroup?onTouchEvent?ACTION_UP");?? ????????????????break;?? ????????????default:?? ????????????????Log.e(TAG,?"MyViewGroup?onTouchEvent?"?+?event.getAction());?? ????????????????break;?? ????????}?? ????????return?super.onTouchEvent(event);?? ????}?? }?? Contants.java
[java] view plaincopy
package?com.zqc.dispatchtouchevent;?? ?? public?class?Constants?{?? ?? ????public?final?static?String?TAG?=?"MY_LOG";?? ????public?final?static?String?MY_GESTURE_TAG?=?"GESTURE_TAG";?? }?? 在代碼中將每個函數分別列出并加上Log輸出,這樣對著Log日志進行分析,則一目了然。
1.讓所有的onInterceptTouchEvent、dispatchTouchEvent及onTouchEvent均返回super.onTouchEvent即均返回false時,輕輕點擊MyView然后快速抬起,查看相應的Log:
?
通過Log能清楚的查看代碼執行的流程,具體流程如下:
DOWN事件:MainActivity.dispatchTouchEvent->MyViewGroup.dispatchTouchEvet->MyViewGroup.onInterceptTouchEvent->MyView.dispatchTouchEvent->setOnTouchListener.onTouch->MyView.onTouchEvent->MyViewGroup.onTouchEvent->MainActivity.onTouchEvent
UP事件:MainActivity.dispatchTouchEvent->MainActivity.onTouchEvent
從上面流程可以看出,點擊事件最先傳給窗口Activity的dispatchTouchEvent函數進行事件分發,然后對于View類,是先傳給對應的父View的dispatchTouchEvent進行事件分發,然后在傳給里面點擊的View。當down事件沒有被各個view消費時,最終會調用Acitivity的onTouchEvent,并在在Down后續的UP事件不在傳給MyViewGroup和MyView,直接傳給MainAcitivity。所以當事件沒有被窗口中的View消費時,最終都是給了該窗口Activity類中的onTouchEvent事件處理。從Log中也可以看出setOnTouchListener中的onTouch事件是在對應View的onTouchEvent事件之前被執行。
2.當MainAcivity中dispathTouchEvent返回true時,輕輕點擊MyView,查看對應Log:
通過Log可以看到當窗口Activity的dispatchTouchEvent返回true時,DOWN事件沒有往View中傳,也就沒有調用任何的onTouchEvent事件,UP事件也是走到Activity的dispatchTouchEvent時也就結束了。
3.重新置Activity中dispatchTouchEvent返回false,然后置ViewGroup中onInterceptTouchEvent返回true時,輕輕點擊MyView查看對應Log:
這時DOWN事件和UP事件的執行流程如下:
DOWN事件:MainActivity.dipatchTouchEvent->MyViewGroup.dispatchTouchEvent->MyViewGroup.onInterceptTouchEvent->MyViewGroup.onTouchEvent->MainActivity.onTouchEvent.
UP事件:MainActiviy.dispatchTouchEvent->MainActivity.onTouchEvent.
從Log中可以看出,當onInterceptTouchEvent返回true時,事件即被MyViewGroup攔截了,這時事件就直接傳給MyViewGroup.onTouchEvent,不在往子View傳,由于MyViewGroup.onTouchEvent返回的是false,即MyViewGroup并沒有消費事件,這時事件會傳給窗口Activity,UP事件會傳給最后一個接受Down事件的窗口或View。
4.當MyView中onTouchEvent返回true時,即MyView會消費傳給他的事件。輕點MyView查看對應的Log:
繼續分析DOWN事件的流程:
DOWN事件:MainActivity.dispatchTouchEvent->MyViewGroup.dispatchTouchEvet->MyViewGroup.onInterceptTouchEvent->MyView.dispatchTouchEvent->setOnTouchListener.onTouch->MyView.onTouchEvent
UP事件:MainActivity.dispatchTouchEvent->MyViewGroup.dispatchTouchEvet->MyViewGroup.onInterceptTouchEvent->MyView.dispatchTouchEvent->setOnTouchListener.onTouch->MyView.onTouchEvent
從上面的執行流程可以看出當事件被MyView消費后,事件不會在往上傳,后續的UP事件也直接通過dispatchTouchEvent分發給對應的View,這里還是提一下,在MainAcitivy中設置的setOnTouchListener中的onTouch事件是在MyView自身的onTouchEvent事件之前被執行,因而設置的setOnTouchEvent的onTouch函數還是會被執行。
先只分析這幾種場景,MOVE事件和UP事件一樣只要DOWN事件被某個View消耗了,那么MOVE事件也就直接傳到這個View。可以下載代碼運行后,在MyView上面滑動下看下Log,具體Log我也貼一份。
情況1:
情況2:
下面對著Android源碼來具體分析View的觸摸事件到底是怎么執行的。首先根據Log可以最先接收到消息的是Activity的dispatchTouchEvent,在該處設置斷點,然后查看對應的調用方法棧(你會發現在調到MainActivity的dispatchTouchEvent時,前面已經調用了很多方法),如下:
由于Android系統啟動后會先啟動Zygote進程,該進程會在手機開機后一直運行,Android中的幾個系統服務都是由Zygote進程fork出來的,一個應用在啟動時所分配到的進程也是由Zygote進程fork出來的,通常說一個應用的起點是Application里面的onCreate函數,其實真正的起點是ActivityThread里面的main函數,看到這個main函數是不是有種熟悉的感覺啊。在main函數中初始化了應用程序的主線程,同時初始化了主線程的消息隊列,并調用了Looper.loop()函數使主線程不斷的對消息隊列進行循環檢測,有消息則進行處理。點擊事件產生一個消息,該消息傳到InputEventReceiver后,由InputEventReceiver的繼承類WindowInputEventReceiver去處理,WindowInputEventReceiver類是ViewRootImpl類的內部類,查看對應代碼如下:
ViewRootImpl.java
[java] view plaincopy
final?class?WindowInputEventReceiver?extends?InputEventReceiver?{?? ????public?WindowInputEventReceiver(InputChannel?inputChannel,?Looper?looper)?{?? ????????super(inputChannel,?looper);?? ????}?? ?? ????@Override?? ????public?void?onInputEvent(InputEvent?event)?{?? ????????enqueueInputEvent(event,?this,?0,?true);?? ????}?? ?? ????@Override?? ????public?void?onBatchedInputEventPending()?{?? ????????if?(mUnbufferedInputDispatch)?{?? ????????????super.onBatchedInputEventPending();?? ????????}?else?{?? ????????????scheduleConsumeBatchedInput();?? ????????}?? ????}?? ?? ????@Override?? ????public?void?dispose()?{?? ????????unscheduleConsumeBatchedInput();?? ????????super.dispose();?? ????}?? }?? 查看代碼可以當點擊消息過來時,直接調用ViewRootImpl類中的enqueueInputEvent(event,this,0,true)方法:
ViewRootImpl.java
[java] view plaincopy
void?enqueueInputEvent(InputEvent?event,?? ????????InputEventReceiver?receiver,?int?flags,?boolean?processImmediately)?{?? ????adjustInputEventForCompatibility(event);?? ????QueuedInputEvent?q?=?obtainQueuedInputEvent(event,?receiver,?flags);?? ?? ???? ???? ???? ???? ???? ????QueuedInputEvent?last?=?mPendingInputEventTail;?? ????if?(last?==?null)?{?? ????????mPendingInputEventHead?=?q;?? ????????mPendingInputEventTail?=?q;?? ????}?else?{?? ????????last.mNext?=?q;?? ????????mPendingInputEventTail?=?q;?? ????}?? ????mPendingInputEventCount?+=?1;?? ????Trace.traceCounter(Trace.TRACE_TAG_INPUT,?mPendingInputEventQueueLengthCounterName,?? ????????????mPendingInputEventCount);?? ?? ????if?(processImmediately)?{?? ????????doProcessInputEvents();?? ????}?else?{?? ????????scheduleProcessInputEvents();?? ????}?? }?? 由于processImmediately為true,因而是立即處理,即直接調用doProcessInputEvents();
ViewRootImpl.java
[java] view plaincopy
void?doProcessInputEvents()?{?? ???? ????while?(mPendingInputEventHead?!=?null)?{?? ????????QueuedInputEvent?q?=?mPendingInputEventHead;?? ????????mPendingInputEventHead?=?q.mNext;?? ????????if?(mPendingInputEventHead?==?null)?{?? ????????????mPendingInputEventTail?=?null;?? ????????}?? ????????q.mNext?=?null;?? ?? ????????mPendingInputEventCount?-=?1;?? ????????Trace.traceCounter(Trace.TRACE_TAG_INPUT,?mPendingInputEventQueueLengthCounterName,?? ????????????????mPendingInputEventCount);?? ?? ????????long?eventTime?=?q.mEvent.getEventTimeNano();?? ????????long?oldestEventTime?=?eventTime;?? ????????if?(q.mEvent?instanceof?MotionEvent)?{?? ????????????MotionEvent?me?=?(MotionEvent)q.mEvent;?? ????????????if?(me.getHistorySize()?>?0)?{?? ????????????????oldestEventTime?=?me.getHistoricalEventTimeNano(0);?? ????????????}?? ????????}?? ????????mChoreographer.mFrameInfo.updateInputEventTime(eventTime,?oldestEventTime);?? ?? ????????deliverInputEvent(q);?? ????}?? ?? ???? ???? ????if?(mProcessInputEventsScheduled)?{?? ????????mProcessInputEventsScheduled?=?false;?? ????????mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);?? ????}?? }?? z之后調用了deliverInputEvent(q)
ViewRootImpl.java
[java] view plaincopy
private?void?deliverInputEvent(QueuedInputEvent?q)?{?? ????Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,?"deliverInputEvent",?? ????????????q.mEvent.getSequenceNumber());?? ????if?(mInputEventConsistencyVerifier?!=?null)?{?? ????????mInputEventConsistencyVerifier.onInputEvent(q.mEvent,?0);?? ????}?? ?? ????InputStage?stage;?? ????if?(q.shouldSendToSynthesizer())?{?? ????????stage?=?mSyntheticInputStage;?? ????}?else?{?? ????????stage?=?q.shouldSkipIme()???mFirstPostImeInputStage?:?mFirstInputStage;?? ????}?? ?? ????if?(stage?!=?null)?{?? ????????stage.deliver(q);?? ????}?else?{?? ????????finishInputEvent(q);?? ????}?? }?? 在這里初始化了一個InputStage類的實例,然后調用了該類的deliver(q),具體方法如下:
[java] view plaincopy
????abstract?class?InputStage?{?? ????????private?final?InputStage?mNext;?? ?? ????????protected?static?final?int?FORWARD?=?0;?? ????????protected?static?final?int?FINISH_HANDLED?=?1;?? ????????protected?static?final?int?FINISH_NOT_HANDLED?=?2;?? ?? ???????? ????????public?InputStage(InputStage?next)?{?? ????????????mNext?=?next;?? ????????}?? ?? ???????? ????????public?final?void?deliver(QueuedInputEvent?q)?{?? ????????????if?((q.mFlags?&?QueuedInputEvent.FLAG_FINISHED)?!=?0)?{?? ????????????????forward(q);?? ????????????}?else?if?(shouldDropInputEvent(q))?{?? ????????????????finish(q,?false);?? ????????????}?else?{?? ????????????????apply(q,?onProcess(q));?? ????????????}?? ????????}?? ?? ???????? ????????protected?void?finish(QueuedInputEvent?q,?boolean?handled)?{?? ????????????q.mFlags?|=?QueuedInputEvent.FLAG_FINISHED;?? ????????????if?(handled)?{?? ????????????????q.mFlags?|=?QueuedInputEvent.FLAG_FINISHED_HANDLED;?? ????????????}?? ????????????forward(q);?? ????????}?? ?? ???????? ????????protected?void?forward(QueuedInputEvent?q)?{?? ????????????onDeliverToNext(q);?? ????????}?? ?? ???????? ????????protected?void?apply(QueuedInputEvent?q,?int?result)?{?? ????????????if?(result?==?FORWARD)?{?? ????????????????forward(q);?? ????????????}?else?if?(result?==?FINISH_HANDLED)?{?? ????????????????finish(q,?true);?? ????????????}?else?if?(result?==?FINISH_NOT_HANDLED)?{?? ????????????????finish(q,?false);?? ????????????}?else?{?? ????????????????throw?new?IllegalArgumentException("Invalid?result:?"?+?result);?? ????????????}?? ????????}?? ?? ???????? ????????protected?int?onProcess(QueuedInputEvent?q)?{?? ????????????return?FORWARD;?? ????????}?? ?? ???????? ????????protected?void?onDeliverToNext(QueuedInputEvent?q)?{?? ????????????if?(DEBUG_INPUT_STAGES)?{?? ????????????????Log.v(TAG,?"Done?with?"?+?getClass().getSimpleName()?+?".?"?+?q);?? ????????????}?? ????????????if?(mNext?!=?null)?{?? ????????????????mNext.deliver(q);?? ????????????}?else?{?? ????????????????finishInputEvent(q);?? ????????????}?? ????????}?? ?? ????????protected?boolean?shouldDropInputEvent(QueuedInputEvent?q)?{?? ????????????if?(mView?==?null?||?!mAdded)?{?? ????????????????Slog.w(TAG,?"Dropping?event?due?to?root?view?being?removed:?"?+?q.mEvent);?? ????????????????return?true;?? ????????????}?else?if?((!mAttachInfo.mHasWindowFocus?? ????????????????????&&?!q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER))?||?mStopped?? ????????????????????||?(mPausedForTransition?&&?!isBack(q.mEvent)))?{?? ???????????????? ???????????????? ???????????????? ????????????????if?(isTerminalInputEvent(q.mEvent))?{?? ???????????????????? ????????????????????q.mEvent.cancel();?? ????????????????????Slog.w(TAG,?"Cancelling?event?due?to?no?window?focus:?"?+?q.mEvent);?? ????????????????????return?false;?? ????????????????}?? ?? ???????????????? ????????????????Slog.w(TAG,?"Dropping?event?due?to?no?window?focus:?"?+?q.mEvent);?? ????????????????return?true;?? ????????????}?? ????????????return?false;?? ????????}?? ?? ????????void?dump(String?prefix,?PrintWriter?writer)?{?? ????????????if?(mNext?!=?null)?{?? ????????????????mNext.dump(prefix,?writer);?? ????????????}?? ????????}?? ?? ????????private?boolean?isBack(InputEvent?event)?{?? ????????????if?(event?instanceof?KeyEvent)?{?? ????????????????return?((KeyEvent)?event).getKeyCode()?==?KeyEvent.KEYCODE_BACK;?? ????????????}?else?{?? ????????????????return?false;?? ????????????}?? ????????}?? ????}?? 對應方法棧可以看出,進過一些列調用最終會調用到ViewPostImeInputStage類的processPointerEvent方法.
ViewRootImpl.java
[java] view plaincopy
private?int?processPointerEvent(QueuedInputEvent?q)?{?? ????final?MotionEvent?event?=?(MotionEvent)q.mEvent;?? ?? ????mAttachInfo.mUnbufferedDispatchRequested?=?false;?? ????boolean?handled?=?mView.dispatchPointerEvent(event);?? ????if?(mAttachInfo.mUnbufferedDispatchRequested?&&?!mUnbufferedInputDispatch)?{?? ????????mUnbufferedInputDispatch?=?true;?? ????????if?(mConsumeBatchedInputScheduled)?{?? ????????????scheduleConsumeBatchedInputImmediately();?? ????????}?? ????}?? ????return?handled???FINISH_HANDLED?:?FORWARD;?? }?? 在該方法中調用了mView的dispatchPointerEvent,這個mView的初始化可以查看Activity的創建代碼,在Activity創建的時候會給Activity設置一個根布局也就是DecorView,這里的mView就是DecorView,這個DecorView是PhoneWindow的私有內部類,它繼承于FrameLayout并實現了RootViewSurfaceTaker接口,但是該方法是View類的一個final方法,子類無法覆寫,直接查看View中的相應代碼即可。代碼如下:
View.java
[java] view plaincopy
public?final?boolean?dispatchPointerEvent(MotionEvent?event)?{?? ????if?(event.isTouchEvent())?{?? ????????return?dispatchTouchEvent(event);?? ????}?else?{?? ????????return?dispatchGenericMotionEvent(event);?? ????}?? }?? 繼續查看DecorView類中的dispatchTouchEvent方法,代碼如下:
PhoneWindow.java
[java] view plaincopy
@Override?? public?boolean?dispatchTouchEvent(MotionEvent?ev)?{?? ????final?Callback?cb?=?getCallback();?? ????return?cb?!=?null?&&?!isDestroyed()?&&?mFeatureId?<?0???cb.dispatchTouchEvent(ev)?? ????????????:?super.dispatchTouchEvent(ev);?? }?? 這個getCallback也就是當前的Activity,當當前Activity沒有destroy的時候即調用該Activity的dispatchTouchEvent,這里代碼就回到了應用層了,框架層完成了很多操作,這些操作只有查看源碼才知道,這里終于回到了我們編寫代碼的地方了。當然這之后還是會通過框架層將對應的Touch事件傳給對應的ViewGroup和View。下面先看下Activity中dispatchTouchEvent的代碼:
Activity.java
[java] view plaincopy
public?boolean?dispatchTouchEvent(MotionEvent?ev)?{?? ????if?(ev.getAction()?==?MotionEvent.ACTION_DOWN)?{?? ????????onUserInteraction();?? ????}?? ????if?(getWindow().superDispatchTouchEvent(ev))?{ ????????return?true;?? ????} ????return?onTouchEvent(ev);?? }?? 果然這里又回到了框架層,這里getWindow就是PhoneWindow,繼續查看PhoneWindow的代碼:
PhoneWindow.java
[java] view plaincopy
@Override?? public?boolean?superDispatchTouchEvent(MotionEvent?event)?{?? ????return?mDecor.superDispatchTouchEvent(event);?? }?? 這里把事件就傳給了DecorView進行分發。
PhoneWindow.java->DecorView
[java] view plaincopy
public?boolean?superDispatchTouchEvent(MotionEvent?event)?{?? ????return?super.dispatchTouchEvent(event);?? }?? 前面說過DecorView繼承于FrameLayout,這里super.dispatchTouchEvent就是調用了FrameLayout里面的dispatchTouchEvent,而FrameLayout類中并未重寫dispatchTouchEvent,因而直接調用的是ViewGroup中的dispatchTouchEvent。繼續查看代碼:
ViewGroup.java
[java] view plaincopy
@Override?? public?boolean?dispatchTouchEvent(MotionEvent?ev)?{?? ????if?(mInputEventConsistencyVerifier?!=?null)?{?? ????????mInputEventConsistencyVerifier.onTouchEvent(ev,?1);?? ????}?? ?? ???? ???? ????if?(ev.isTargetAccessibilityFocus()?&&?isAccessibilityFocusedViewOrHost())?{?? ????????ev.setTargetAccessibilityFocus(false);?? ????}?? ?? ????boolean?handled?=?false;?? ????if?(onFilterTouchEventForSecurity(ev))?{?? ????????final?int?action?=?ev.getAction();?? ????????final?int?actionMasked?=?action?&?MotionEvent.ACTION_MASK;?? ?? ???????? ????????if?(actionMasked?==?MotionEvent.ACTION_DOWN)?{?? ???????????? ???????????? ???????????? ????????????cancelAndClearTouchTargets(ev);?? ????????????resetTouchState();?? ????????}?? ?? ???????? ????????final?boolean?intercepted;?? ????????if?(actionMasked?==?MotionEvent.ACTION_DOWN?? ????????????????||?mFirstTouchTarget?!=?null)?{?? ????????????final?boolean?disallowIntercept?=?(mGroupFlags?&?FLAG_DISALLOW_INTERCEPT)?!=?0;?? ????????????if?(!disallowIntercept)?{?? ????????????????intercepted?=?onInterceptTouchEvent(ev);?? ????????????????ev.setAction(action);? ????????????}?else?{?? ????????????????intercepted?=?false;?? ????????????}?? ????????}?else?{?? ???????????? ???????????? ????????????intercepted?=?true;?? ????????}?? ?? ???????? ???????? ????????if?(intercepted?||?mFirstTouchTarget?!=?null)?{?? ????????????ev.setTargetAccessibilityFocus(false);?? ????????}?? ?? ???????? ????????final?boolean?canceled?=?resetCancelNextUpFlag(this)?? ????????????????||?actionMasked?==?MotionEvent.ACTION_CANCEL;?? ?? ???????? ????????final?boolean?split?=?(mGroupFlags?&?FLAG_SPLIT_MOTION_EVENTS)?!=?0;?? ????????TouchTarget?newTouchTarget?=?null;?? ????????boolean?alreadyDispatchedToNewTouchTarget?=?false;?? ????????if?(!canceled?&&?!intercepted)?{?? ?? ???????????? ???????????? ???????????? ???????????? ???????????? ????????????View?childWithAccessibilityFocus?=?ev.isTargetAccessibilityFocus()?? ??????????????????????findChildWithAccessibilityFocus()?:?null;?? ?? ????????????if?(actionMasked?==?MotionEvent.ACTION_DOWN?? ????????????????????||?(split?&&?actionMasked?==?MotionEvent.ACTION_POINTER_DOWN)?? ????????????????????||?actionMasked?==?MotionEvent.ACTION_HOVER_MOVE)?{?? ????????????????final?int?actionIndex?=?ev.getActionIndex();? ????????????????final?int?idBitsToAssign?=?split???1?<<?ev.getPointerId(actionIndex)?? ????????????????????????:?TouchTarget.ALL_POINTER_IDS;?? ?? ???????????????? ???????????????? ????????????????removePointersFromTouchTargets(idBitsToAssign);?? ?? ????????????????final?int?childrenCount?=?mChildrenCount;?? ????????????????if?(newTouchTarget?==?null?&&?childrenCount?!=?0)?{?? ????????????????????final?float?x?=?ev.getX(actionIndex);?? ????????????????????final?float?y?=?ev.getY(actionIndex);?? ???????????????????? ???????????????????? ????????????????????final?ArrayList<View>?preorderedList?=?buildOrderedChildList();?? ????????????????????final?boolean?customOrder?=?preorderedList?==?null?? ????????????????????????????&&?isChildrenDrawingOrderEnabled();?? ????????????????????final?View[]?children?=?mChildren;?? ????????????????????for?(int?i?=?childrenCount?-?1;?i?>=?0;?i--)?{?? ????????????????????????final?int?childIndex?=?customOrder?? ??????????????????????????????????getChildDrawingOrder(childrenCount,?i)?:?i;?? ????????????????????????final?View?child?=?(preorderedList?==?null)?? ??????????????????????????????????children[childIndex]?:?preorderedList.get(childIndex);?? ?? ???????????????????????? ???????????????????????? ???????????????????????? ???????????????????????? ????????????????????????if?(childWithAccessibilityFocus?!=?null)?{?? ????????????????????????????if?(childWithAccessibilityFocus?!=?child)?{?? ????????????????????????????????continue;?? ????????????????????????????}?? ????????????????????????????childWithAccessibilityFocus?=?null;?? ????????????????????????????i?=?childrenCount?-?1;?? ????????????????????????}?? ?? ????????????????????????if?(!canViewReceivePointerEvents(child)?? ????????????????????????????????||?!isTransformedTouchPointInView(x,?y,?child,?null))?{?? ????????????????????????????ev.setTargetAccessibilityFocus(false);?? ????????????????????????????continue;?? ????????????????????????}?? ?? ????????????????????????newTouchTarget?=?getTouchTarget(child);?? ????????????????????????if?(newTouchTarget?!=?null)?{?? ???????????????????????????? ???????????????????????????? ????????????????????????????newTouchTarget.pointerIdBits?|=?idBitsToAssign;?? ????????????????????????????break;?? ????????????????????????}?? ?? ????????????????????????resetCancelNextUpFlag(child);?? ????????????????????????if?(dispatchTransformedTouchEvent(ev,?false,?child,?idBitsToAssign))?{?? ???????????????????????????? ????????????????????????????mLastTouchDownTime?=?ev.getDownTime();?? ????????????????????????????if?(preorderedList?!=?null)?{?? ???????????????????????????????? ????????????????????????????????for?(int?j?=?0;?j?<?childrenCount;?j++)?{?? ????????????????????????????????????if?(children[childIndex]?==?mChildren[j])?{?? ????????????????????????????????????????mLastTouchDownIndex?=?j;?? ????????????????????????????????????????break;?? ????????????????????????????????????}?? ????????????????????????????????}?? ????????????????????????????}?else?{?? ????????????????????????????????mLastTouchDownIndex?=?childIndex;?? ????????????????????????????}?? ????????????????????????????mLastTouchDownX?=?ev.getX();?? ????????????????????????????mLastTouchDownY?=?ev.getY();?? ????????????????????????????newTouchTarget?=?addTouchTarget(child,?idBitsToAssign);?? ????????????????????????????alreadyDispatchedToNewTouchTarget?=?true;?? ????????????????????????????break;?? ????????????????????????}?? ?? ???????????????????????? ???????????????????????? ????????????????????????ev.setTargetAccessibilityFocus(false);?? ????????????????????}?? ????????????????????if?(preorderedList?!=?null)?preorderedList.clear();?? ????????????????}?? ?? ????????????????if?(newTouchTarget?==?null?&&?mFirstTouchTarget?!=?null)?{?? ???????????????????? ???????????????????? ????????????????????newTouchTarget?=?mFirstTouchTarget;?? ????????????????????while?(newTouchTarget.next?!=?null)?{?? ????????????????????????newTouchTarget?=?newTouchTarget.next;?? ????????????????????}?? ????????????????????newTouchTarget.pointerIdBits?|=?idBitsToAssign;?? ????????????????}?? ????????????}?? ????????}?? ?? ???????? ????????if?(mFirstTouchTarget?==?null)?{?? ???????????? ????????????handled?=?dispatchTransformedTouchEvent(ev,?canceled,?null,?? ????????????????????TouchTarget.ALL_POINTER_IDS);?? ????????}?else?{?? ???????????? ???????????? ????????????TouchTarget?predecessor?=?null;?? ????????????TouchTarget?target?=?mFirstTouchTarget;?? ????????????while?(target?!=?null)?{?? ????????????????final?TouchTarget?next?=?target.next;?? ????????????????if?(alreadyDispatchedToNewTouchTarget?&&?target?==?newTouchTarget)?{?? ????????????????????handled?=?true;?? ????????????????}?else?{?? ????????????????????final?boolean?cancelChild?=?resetCancelNextUpFlag(target.child)?? ????????????????????????????||?intercepted;?? ????????????????????if?(dispatchTransformedTouchEvent(ev,?cancelChild,?? ????????????????????????????target.child,?target.pointerIdBits))?{?? ????????????????????????handled?=?true;?? ????????????????????}?? ????????????????????if?(cancelChild)?{?? ????????????????????????if?(predecessor?==?null)?{?? ????????????????????????????mFirstTouchTarget?=?next;?? ????????????????????????}?else?{?? ????????????????????????????predecessor.next?=?next;?? ????????????????????????}?? ????????????????????????target.recycle();?? ????????????????????????target?=?next;?? ????????????????????????continue;?? ????????????????????}?? ????????????????}?? ????????????????predecessor?=?target;?? ????????????????target?=?next;?? ????????????}?? ????????}?? ?? ???????? ????????if?(canceled?? ????????????????||?actionMasked?==?MotionEvent.ACTION_UP?? ????????????????||?actionMasked?==?MotionEvent.ACTION_HOVER_MOVE)?{?? ????????????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;?? }?? 代碼有點多,通過調試可知將會調用dispatchTransformedTouchEvent,查看代碼如下:
ViewGroup.java
[java] view plaincopy
private?boolean?dispatchTransformedTouchEvent(MotionEvent?event,?boolean?cancel,?? ????????View?child,?int?desiredPointerIdBits)?{?? ????final?boolean?handled;?? ?? ???? ???? ????final?int?oldAction?=?event.getAction();?? ????if?(cancel?||?oldAction?==?MotionEvent.ACTION_CANCEL)?{?? ????????event.setAction(MotionEvent.ACTION_CANCEL);?? ????????if?(child?==?null)?{?? ????????????handled?=?super.dispatchTouchEvent(event);?? ????????}?else?{?? ????????????handled?=?child.dispatchTouchEvent(event);?? ????????}?? ????????event.setAction(oldAction);?? ????????return?handled;?? ????}?? ?? ???? ????final?int?oldPointerIdBits?=?event.getPointerIdBits();?? ????final?int?newPointerIdBits?=?oldPointerIdBits?&?desiredPointerIdBits;?? ?? ???? ???? ????if?(newPointerIdBits?==?0)?{?? ????????return?false;?? ????}?? ?? ???? ???? ???? ???? ????final?MotionEvent?transformedEvent;?? ????if?(newPointerIdBits?==?oldPointerIdBits)?{?? ????????if?(child?==?null?||?child.hasIdentityMatrix())?{?? ????????????if?(child?==?null)?{?? ????????????????handled?=?super.dispatchTouchEvent(event);?? ????????????}?else?{?? ????????????????final?float?offsetX?=?mScrollX?-?child.mLeft;?? ????????????????final?float?offsetY?=?mScrollY?-?child.mTop;?? ????????????????event.offsetLocation(offsetX,?offsetY);?? ?? ????????????????handled?=?child.dispatchTouchEvent(event);?? ?? ????????????????event.offsetLocation(-offsetX,?-offsetY);?? ????????????}?? ????????????return?handled;?? ????????}?? ????????transformedEvent?=?MotionEvent.obtain(event);?? ????}?else?{?? ????????transformedEvent?=?event.split(newPointerIdBits);?? ????}?? ?? ???? ????if?(child?==?null)?{?? ????????handled?=?super.dispatchTouchEvent(transformedEvent);?? ????}?else?{?? ????????final?float?offsetX?=?mScrollX?-?child.mLeft;?? ????????final?float?offsetY?=?mScrollY?-?child.mTop;?? ????????transformedEvent.offsetLocation(offsetX,?offsetY);?? ????????if?(!?child.hasIdentityMatrix())?{?? ????????????transformedEvent.transform(child.getInverseMatrix());?? ????????}?? ?? ????????handled?=?child.dispatchTouchEvent(transformedEvent);?? ????}?? ?? ???? ????transformedEvent.recycle();?? ????return?handled;?? }?? 在該函數中調用了child.dispatchTouchEvent(),這里便走到了子View的dispatchTouchEvent中。子View也就是MyView,也就走到了TextView的dispathTouchEvent中,由于TextView并未重寫dispathTouchEvent,因而直接進入View的dispatchTouchEvent中,代碼如下:
View.java
[java] view plaincopy
public?boolean?dispatchTouchEvent(MotionEvent?event)?{?? ???? ????if?(event.isTargetAccessibilityFocus())?{?? ???????? ????????if?(!isAccessibilityFocusedViewOrHost())?{?? ????????????return?false;?? ????????}?? ???????? ????????event.setTargetAccessibilityFocus(false);?? ????}?? ?? ????boolean?result?=?false;?? ?? ????if?(mInputEventConsistencyVerifier?!=?null)?{?? ????????mInputEventConsistencyVerifier.onTouchEvent(event,?0);?? ????}?? ?? ????final?int?actionMasked?=?event.getActionMasked();?? ????if?(actionMasked?==?MotionEvent.ACTION_DOWN)?{?? ???????? ????????stopNestedScroll();?? ????}?? ?? ????if?(onFilterTouchEventForSecurity(event))?{?? ???????? ????????ListenerInfo?li?=?mListenerInfo;?? ????????if?(li?!=?null?&&?li.mOnTouchListener?!=?null?? ????????????????&&?(mViewFlags?&?ENABLED_MASK)?==?ENABLED?? ????????????????&&?li.mOnTouchListener.onTouch(this,?event))?{ ????????????result?=?true;?? ????????}?? ?? ????????if?(!result?&&?onTouchEvent(event))?{ ????????????result?=?true;?? ????????}?? ????}?? ?? ????if?(!result?&&?mInputEventConsistencyVerifier?!=?null)?{?? ????????mInputEventConsistencyVerifier.onUnhandledEvent(event,?0);?? ????}?? ?? ???? ???? ???? ????if?(actionMasked?==?MotionEvent.ACTION_UP?||?? ????????????actionMasked?==?MotionEvent.ACTION_CANCEL?||?? ????????????(actionMasked?==?MotionEvent.ACTION_DOWN?&&?!result))?{?? ????????stopNestedScroll();?? ????}?? ?? ????return?result;?? }?? 在該函數中看到了在MainActivity中設置的setOnTouchListener對應的Listener接口,當setListener中的onTouch返回true時,MyView本身的onTouchEvent便不被調用。接下來看下View的onTouchEvent代碼:
View.java
[java] view plaincopy
public?boolean?onTouchEvent(MotionEvent?event)?{?? ????????final?float?x?=?event.getX();?? ????????final?float?y?=?event.getY();?? ????????final?int?viewFlags?=?mViewFlags;?? ????????final?int?action?=?event.getAction();?? ?? ????????if?((viewFlags?&?ENABLED_MASK)?==?DISABLED)?{?? ????????????if?(action?==?MotionEvent.ACTION_UP?&&?(mPrivateFlags?&?PFLAG_PRESSED)?!=?0)?{?? ????????????????setPressed(false);?? ????????????}?? ???????????? ???????????? ????????????return?(((viewFlags?&?CLICKABLE)?==?CLICKABLE?? ????????????????????||?(viewFlags?&?LONG_CLICKABLE)?==?LONG_CLICKABLE)?? ????????????????????||?(viewFlags?&?CONTEXT_CLICKABLE)?==?CONTEXT_CLICKABLE);?? ????????}?? ?? ????????if?(mTouchDelegate?!=?null)?{ ????????????if?(mTouchDelegate.onTouchEvent(event))?{?? ????????????????return?true;?? ????????????}?? ????????}?? ?? ????????if?(((viewFlags?&?CLICKABLE)?==?CLICKABLE?||?? ????????????????(viewFlags?&?LONG_CLICKABLE)?==?LONG_CLICKABLE)?||?? ????????????????(viewFlags?&?CONTEXT_CLICKABLE)?==?CONTEXT_CLICKABLE)?{?? ????????????switch?(action)?{?? ????????????????case?MotionEvent.ACTION_UP:?? ????????????????????boolean?prepressed?=?(mPrivateFlags?&?PFLAG_PREPRESSED)?!=?0;?? ????????????????????if?((mPrivateFlags?&?PFLAG_PRESSED)?!=?0?||?prepressed)?{?? ???????????????????????? ???????????????????????? ????????????????????????boolean?focusTaken?=?false;?? ????????????????????????if?(isFocusable()?&&?isFocusableInTouchMode()?&&?!isFocused())?{?? ????????????????????????????focusTaken?=?requestFocus();?? ????????????????????????}?? ?? ????????????????????????if?(prepressed)?{?? ???????????????????????????? ???????????????????????????? ???????????????????????????? ???????????????????????????? ????????????????????????????setPressed(true,?x,?y);?? ???????????????????????}?? ?? ????????????????????????if?(!mHasPerformedLongPress?&&?!mIgnoreNextUpEvent)?{?? ???????????????????????????? ????????????????????????????removeLongPressCallback();?? ?? ???????????????????????????? ????????????????????????????if?(!focusTaken)?{?? ???????????????????????????????? ???????????????????????????????? ???????????????????????????????? ????????????????????????????????if?(mPerformClick?==?null)?{?? ????????????????????????????????????mPerformClick?=?new?PerformClick();?? ????????????????????????????????}?? ????????????????????????????????if?(!post(mPerformClick))?{?? ????????????????????????????????????performClick();?? ????????????????????????????????}?? ????????????????????????????}?? ????????????????????????}?? ?? ????????????????????????if?(mUnsetPressedState?==?null)?{?? ????????????????????????????mUnsetPressedState?=?new?UnsetPressedState();?? ????????????????????????}?? ?? ????????????????????????if?(prepressed)?{?? ????????????????????????????postDelayed(mUnsetPressedState,?? ????????????????????????????????????ViewConfiguration.getPressedStateDuration());?? ????????????????????????}?else?if?(!post(mUnsetPressedState))?{?? ???????????????????????????? ????????????????????????????mUnsetPressedState.run();?? ????????????????????????}?? ?? ????????????????????????removeTapCallback();?? ????????????????????}?? ????????????????????mIgnoreNextUpEvent?=?false;?? ????????????????????break;?? ?? ????????????????case?MotionEvent.ACTION_DOWN:?? ????????????????????mHasPerformedLongPress?=?false;?? ?? ????????????????????if?(performButtonActionOnTouchDown(event))?{?? ????????????????????????break;?? ????????????????????}?? ?? ???????????????????? ????????????????????boolean?isInScrollingContainer?=?isInScrollingContainer();?? ?? ???????????????????? ???????????????????? ????????????????????if?(isInScrollingContainer)?{?? ????????????????????????mPrivateFlags?|=?PFLAG_PREPRESSED;?? ????????????????????????if?(mPendingCheckForTap?==?null)?{?? ????????????????????????????mPendingCheckForTap?=?new?CheckForTap();?? ????????????????????????}?? ????????????????????????mPendingCheckForTap.x?=?event.getX();?? ????????????????????????mPendingCheckForTap.y?=?event.getY();?? [java] view plaincopy
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away setPressed(true, x, y);
[java] view plaincopy
checkForLongClick(0); } break; case MotionEvent.ACTION_CANCEL: setPressed(false); removeTapCallback(); removeLongPressCallback(); mInContextButtonPress = false; mHasPerformedLongPress = false; mIgnoreNextUpEvent = false; break; case MotionEvent.ACTION_MOVE: drawableHotspotChanged(x, y); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PFLAG_PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); setPressed(false); } } break; } return true; } return false; }這里僅分析下DOWN事件的處理,這里會先處理按鈕自身的一些事件,具體事件見如下代碼:
[java] view plaincopy
protected?boolean?performButtonActionOnTouchDown(MotionEvent?event)?{?? ????if?(event.getToolType(0)?==?MotionEvent.TOOL_TYPE_MOUSE?&&?? ????????(event.getButtonState()?&?MotionEvent.BUTTON_SECONDARY)?!=?0)?{?? ????????showContextMenu(event.getX(),?event.getY(),?event.getMetaState());?? ????????mPrivateFlags?|=?PFLAG_CANCEL_NEXT_UP_EVENT;?? ????????return?true;?? ????}?? ????return?false;?? }?? 然后判斷當前View的父View是否在滾動,如果不在滾動就調用postDelayed:
View.java
[java] view plaincopy
public?boolean?postDelayed(Runnable?action,?long?delayMillis)?{?? ????final?AttachInfo?attachInfo?=?mAttachInfo;?? ????if?(attachInfo?!=?null)?{?? ????????return?attachInfo.mHandler.postDelayed(action,?delayMillis);?? ????}?? ???? ????ViewRootImpl.getRunQueue().postDelayed(action,?delayMillis);?? ????return?true;?? }?? 將action延遲一段時間,用于后續判斷(是否長按事件,后續MOVE事件,UP事件)。
轉載于:https://www.cnblogs.com/Free-Thinker/p/8915919.html
與50位技術專家面對面 20年技術見證,附贈技術全景圖
總結
以上是生活随笔 為你收集整理的Android中onInterceptTouchEvent、dispatchTouchEvent及onTouchEvent的调用顺序及内部原理 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。