本不打算开,画时序图的时候看了看之前写的(1-8),还是重开一个以绝后患好了。。。。
反正这个最简单!!!
先从Activity开始说,然后看怎么到Activity的。。。。
终于理完了。。。。
换个图玩。。。。
2019年05月18日00:02:48
继续2019年05月18日15:24:18
1,Activity分发事件到ViewGroup
事件分发到Activity之后,从Activity.dispatchTouchEvent(ev)开始处理
当事件为ACTION_DOWN时,会经过一个空方法
变量mDecor
这里便实现了 Activity传递事件到ViewGroup,便到了2.
2,ViewGroup分发事件到View
又是一个老长的方法
这个方法,
首先检查是否拦截,然后检查是否取消,
如果既没有拦截,也没有取消,那么{循环所有的子View,找到点击的是哪个子View并进行分发得到返回结果}。
然后判断是否找到了可分发的子View,如果没有找到,分发到ViewGroup:View,否则判断携带取消参数分发;
分发到View时,根据是否取消及是否有子View情况进行分发,此处到View.dispatchTouchEvent(ev);
3,View分发事件到View.onTouchEvent(ev)
View判断焦点,没有焦点就return false;
然后根据
当前是否拖动;
用户实现了View.onTouch()接口并返回结果;
如果都没有,那么进入View.onTouchEvent(ev)方法并返回结果;
以上三项有任何一项return true那么方法return true;
4,View.onTouchEvent(ev)中分发点击及长按等事件
View.onTouchEvent(ev)中,会对MotionEvent的几个事件进行case,并区分具体是什么事件,从而分发到具体实现。
按下的时候,启动计时器,用来判断是否是长按;
抬起的时候,停止长按计时器,分发点击事件;
长按事件的流向
但是,如果一直return false怎么办
一直跟着return向回看,一直到了Activity,进行了Activity.onTouchEvent(ev)处理;
5,从View.onTouchEvent(ev)到Activity.onTouchEvent(ev)
跟着return,最终回到了Activity的分发开始方法
6,感觉还少点什么
return的时候,View.onTouchEvent(ev)—>View.dispatchTouchEvent(ev)—>ViewGroup.dispatchTouchEvent(ev)
此时,如果一直没有处理,ViewGroup是View的子类,所以会走ViewGroup:View的流程。
7,android事件分发图
画个简单的,新版ViewGroup超出预期。。。。
备用图床
# 下面开始理事件是如何分发到Activity的
8,到Activity之前的分发流程,从消息分发到InputEventReceiver
抓个日志
9,InputEventReceiver建立管道接收事件
根据8中的信息,实际走的是他的子类
10,ViewRootImpl.enqueueInputEvent按顺序增加Looper.loop过来的事件(触摸等事件)
11,队列中取出事件,使用责任链模式,分派处理
12,处理触摸事件
stage是一个抽象类,子类实现处理对应输入事件;
责任链途径好长啊。。。。
13,DecorView:View分发到Activity
DecorView没有重写,调用父类View方法
14,在Activity之前的流程
1)触摸屏幕时,ActivityThread里的Looper从MessageQueue中loop了一条Message,通过InputEventReceiver的管道给了他的子类WindowInputEventReceiver;,
2)ViewRootImpl.WindowInputEventReceiver调用ViewRootImpl.enqueueInputEvent方法,将输入事件加入输入事件队列,队列处理事件;
3)责任链中的ViewPostImeInputStage收到输入事件,将事件传递给DecorView.dispatchPointerEvent()方法,DecorView没有重写,实际调用View相应方法;
4)View.dispatchPointEvent()方法调用dispatchTouchEvent方法,通过回调Window.Callback,回调到Activity.dispatchTouchEvent(ev);
5)进入Activity.dispatchTouchEvent(ev)流程。
15,android事件分发机制流程图Framework层
明天继续2019年05月19日00:21:40
反正这个最简单!!!
先从Activity开始说,然后看怎么到Activity的。。。。
终于理完了。。。。
换个图玩。。。。
2019年05月18日00:02:48
继续2019年05月18日15:24:18
1,Activity分发事件到ViewGroup
事件分发到Activity之后,从Activity.dispatchTouchEvent(ev)开始处理
/** 事件分发1,入口 * 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.如果消耗此事件,则返回true。 */ public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { //按下 onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev);//事件分发13:前面都没有return true,去执行activity.onTouchEvent(ev); }
当事件为ACTION_DOWN时,会经过一个空方法
/** 实现屏保功能; * 当activity在栈顶时,触屏点按home,back,menu等都会触发此方法 * Called whenever a key, touch, or trackball event is dispatched to the * activity. Implement this method if you wish to know that the user has * interacted with the device in some way while your activity is running. * This callback and {@link #onUserLeaveHint} are intended to help * activities manage status bar notifications intelligently; specifically, * for helping activities determine the proper time to cancel a notfication. * * <p>All calls to your activity's {@link #onUserLeaveHint} callback will * be accompanied by calls to {@link #onUserInteraction}. This * ensures that your activity will be told of relevant user activity such * as pulling down the notification pane and touching an item there. * * <p>Note that this callback will be invoked for the touch down action * that begins a touch gesture, but may not be invoked for the touch-moved * and touch-up actions that follow. * * @see #onUserLeaveHint() */ public void onUserInteraction() { }
之后去getWindow().superDispatchTouchEvent(ev)
其中getWindow()取当前window对象private Window mWindow;/** * Retrieve the current {@link android.view.Window} for the activity. * This can be used to directly access parts of the Window API that * are not available through Activity/Screen. * * @return Window The current window, or null if the activity is not * visual. */ public Window getWindow() { return mWindow; }
值来自于Activity的Internal API
所以,getWindow : abstract class Window 实际调用的是他的子类,PhoneWindow.superDispatchTouchEvent(ev)final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); } mUiThread = Thread.currentThread(); mMainThread = aThread; mInstrumentation = instr; mToken = token; mIdent = ident; mApplication = application; mIntent = intent; mReferrer = referrer; mComponent = intent.getComponent(); mActivityInfo = info; mTitle = title; mParent = parent; mEmbeddedID = id; mLastNonConfigurationInstances = lastNonConfigurationInstances; if (voiceInteractor != null) { if (lastNonConfigurationInstances != null) { mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor; } else { mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this, Looper.myLooper()); } } mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; mWindow.setColorMode(info.colorMode); setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled()); enableAutofillCompatibilityIfNeeded(); }
//事件分发2,activity.getWindow() : [PhoneWindow extends abstract Window] .superDispatchTouchEvent(event) @Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }
变量mDecor
到了DecorView.superDispatchTouchEvent(ev)// This is the top-level view of the window, containing the window decor.这是窗口的顶层视图,包含窗口装饰。 private DecorView mDecor;
在这里,DecorView向super分发,它继承于FrameLayout 又继承于 ViewGroup//事件分发3:phoneWindow.mDecor.superDispatchTouchEvent(event) public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event);//super:FrameLayout extends ViewGroup }
在类FrameLayout中,并没有重写dispatchTouchEvent(ev),所以到了ViewGroup。public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {}
这里便实现了 Activity传递事件到ViewGroup,便到了2.
2,ViewGroup分发事件到View
又是一个老长的方法
这个方法,
首先检查是否拦截,然后检查是否取消,
如果既没有拦截,也没有取消,那么{循环所有的子View,找到点击的是哪个子View并进行分发得到返回结果}。
然后判断是否找到了可分发的子View,如果没有找到,分发到ViewGroup:View,否则判断携带取消参数分发;
分发到View时,根据是否取消及是否有子View情况进行分发,此处到View.dispatchTouchEvent(ev);
//事件分发4:从activity经过PhoneWindow:Window到DecorView到FrameLayout到ViewGroup @Override public boolean dispatchTouchEvent(MotionEvent ev) { 。。。 boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; 。。。 // Check for interception. 检查拦截 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); // restore action in case it was changed } else { intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; } 。。。 // Check for cancelation.检查是否取消 final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; 。。。 if (!canceled && !intercepted) { 。。。 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.从前往后扫描所有子View final ArrayList<View> preorderedList = buildTouchDispatchChildList(); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; //mChildren为这个ViewGroup里包含的所有子View for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); // If there is a view that has accessibility focus we want it如果有一个具有辅助功能焦点的视图,我们需要它 // to get the event first and if not handled we will perform a 首先获得活动,如果没有处理,我们将执行 // normal dispatch. We may do a double iteration but this is正常派遣。 我们可以进行双重迭代,但这是 // safer given the timeframe.考虑到时间表,更安全。 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 (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { //分发到子View并return true // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); if (preorderedList != null) { // childIndex points into presorted list, find original index 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; } 。。。 }//end for i 。。。 } 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; } newTouchTarget.pointerIdBits |= idBitsToAssign; } 。。。 } //只要分发到子View,就直接return 子View.dispatchTouchEvent(ev)返回;没有分发到子View,return 此ViewGroup:View.dispatchTouchEvent(ev)的值; // Dispatch to touch targets.发送触摸目标。 if (mFirstTouchTarget == null) { // 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) { 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)) { //分发到子View成功 handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } 。。。 } 。。。 return handled; }
判断是否拦截
分发到子View方法/** 事件分发5:实现此方法以拦截所有触摸屏运动事件 * 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) { if (ev.isFromSource(InputDevice.SOURCE_MOUSE) && ev.getAction() == MotionEvent.ACTION_DOWN && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY) && isOnScrollbarThumb(ev.getX(), ev.getY())) { return true; } return false; }
然后,到了View.dispatchTouchEvent(ev),到了3./** 事件分发6:分发到子View * Transforms a motion event into the coordinate space of a particular child view,将运动事件转换为特定子视图的坐标空间, * filters out irrelevant pointer ids, and overrides its action if necessary.过滤掉不相关的指针ID,并在必要时覆盖其操作。 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.如果child为null,则假定MotionEvent将被发送到此 */ private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; // Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. 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; } // Calculate the number of pointers to deliver. final int oldPointerIdBits = event.getPointerIdBits(); final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; // If for some reason we ended up in an inconsistent state where it looks like we // might produce a motion event with no pointers in it, then drop the event. if (newPointerIdBits == 0) { return false; } // If the number of pointers is the same and we don't need to perform any fancy // irreversible transformations, then we can reuse the motion event for this // dispatch as long as we are careful to revert any changes we make. // Otherwise we need to make a copy. final MotionEvent transformedEvent; if (newPointerIdBits == oldPointerIdBits) { if (child == null || child.hasIdentityMatrix()) { if (child == null) { handled = super.dispatchTouchEvent(event);//没有子View接收,分发给此ViewGroup的View } else { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; event.offsetLocation(offsetX, offsetY); handled = child.dispatchTouchEvent(event);//分发到子View event.offsetLocation(-offsetX, -offsetY); } return handled; } transformedEvent = MotionEvent.obtain(event); } else { transformedEvent = event.split(newPointerIdBits); } // Perform any necessary transformations and dispatch. 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); } // Done. transformedEvent.recycle(); return handled; }
3,View分发事件到View.onTouchEvent(ev)
View判断焦点,没有焦点就return false;
然后根据
当前是否拖动;
用户实现了View.onTouch()接口并返回结果;
如果都没有,那么进入View.onTouchEvent(ev)方法并返回结果;
以上三项有任何一项return true那么方法return true;
其中,onTouch是一个接口/** 事件分发7:从ViewGroup分发到View * 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 the event should be handled by accessibility focus first.如果事件应首先由可访问性焦点处理。 if (event.isTargetAccessibilityFocus()) { // We don't have focus or no virtual descendant has it, do not handle the event.我们没有焦点或没有虚拟后代拥有它,不处理事件。 if (!isAccessibilityFocusedViewOrHost()) { return false; } // We have focus and got the event, then use normal event dispatch.我们有焦点并得到了事件,然后使用正常的事件发送。 event.setTargetAccessibilityFocus(false); } boolean result = false; 。。。 final int actionMasked = event.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { // Defensive cleanup for new gesture新手势的防御性清理 stopNestedScroll(); } if (onFilterTouchEventForSecurity(event)) { if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { //事件分发8:这里消耗ACTION_MOVE拖动事件 result = true; } //noinspection SimplifiableIfStatement 检查声明 ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { //事件分发9:这里到了用户自实现接口onTouchListener result = true; } if (!result && onTouchEvent(event)) { //事件分发10:上面不成立,到了View。onTouchEvent(ev) result = true; } } 。。。 return result; }
然后到了View.onTouchEvent(ev);分开写,到4./** * Interface definition for a callback to be invoked when a touch event is * dispatched to this view. The callback will be invoked before the touch * event is given to the view. */ public interface OnTouchListener { /** * Called when a touch event is dispatched to a view. This allows listeners to * get a chance to respond before the target view. * * @param v The view the touch event has been dispatched to. * @param event The MotionEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onTouch(View v, MotionEvent event); }
4,View.onTouchEvent(ev)中分发点击及长按等事件
View.onTouchEvent(ev)中,会对MotionEvent的几个事件进行case,并区分具体是什么事件,从而分发到具体实现。
按下的时候,启动计时器,用来判断是否是长按;
抬起的时候,停止长按计时器,分发点击事件;
不管是同步执行点击还是异步执行点击,经过的方法是一样的/** 事件分发10: * 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) { final float x = event.getX(); final float y = event.getY(); final int viewFlags = mViewFlags; final int action = event.getAction(); final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; if ((viewFlags & ENABLED_MASK) == DISABLED) { if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; // A disabled view that is clickable still consumes the touch可点击的禁用视图仍然会消耗触摸 // events, it just doesn't respond to them.事件,它只是没有响应它们 return clickable; } if (mTouchDelegate != null) { //物理事件 if (mTouchDelegate.onTouchEvent(event)) { return true; } } if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) { switch (action) { case MotionEvent.ACTION_UP: 。。。 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; if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } 。。。 if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { // This is a tap, so remove the longpress check 长按竟然用了Handler跟计时器 removeLongPressCallback(); // Only perform take click actions if we were in the pressed state如果我们处于按下状态,则仅执行点击操作 if (!focusTaken) { // Use a Runnable and post this rather than calling使用Runnable并发布此而不是调用 // performClick directly. This lets other visual state直接执行。 这让其他视觉状态 // of the view update before click actions start.在单击操作开始之前更新视图 if (mPerformClick == null) { mPerformClick = new PerformClick(); //异步执行点击 } if (!post(mPerformClick)) { performClickInternal(); //同步执行点击 } } } 。。。 } 。。。 break; case MotionEvent.ACTION_DOWN: 。。。 if (!clickable) { checkForLongClick(0, x, y);//事件分发12:检查长按事件 break; } 。。。 // For views inside a scrolling container, delay the pressed feedback for // a short period in case this is a scroll. if (isInScrollingContainer) { mPrivateFlags |= PFLAG_PREPRESSED; if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } mPendingCheckForTap.x = event.getX(); mPendingCheckForTap.y = event.getY(); postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away setPressed(true, x, y); checkForLongClick(0, x, y); } break; case MotionEvent.ACTION_CANCEL: 。。。 break; case MotionEvent.ACTION_MOVE: 。。。 break; } return true; //可点击就必定为true } return false; }
private final class PerformClick implements Runnable { @Override public void run() { performClickInternal(); } }
判断用户是否实现onClickListener,如果实现,进行分发private boolean performClickInternal() { // Must notify autofill manager before performing the click actions to avoid scenarios where // the app has a click listener that changes the state of views the autofill service might // be interested on. notifyAutofillManagerOnClick(); return performClick(); }
/** * Call this view's OnClickListener, if it is defined. Performs all normal * actions associated with clicking: reporting accessibility event, playing * a sound, etc. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ // NOTE: other methods on View should not call this method directly, but performClickInternal() // instead, to guarantee that the autofill manager is notified when necessary (as subclasses // could extend this method without calling super.performClick()). public boolean performClick() { // We still need to call this method to handle the cases where performClick() was called // externally, instead of through performClickInternal() notifyAutofillManagerOnClick(); final boolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); //事件分发11:分发到用户自实现点击事件 result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); notifyEnterOrExitForAutoFillIfNeeded(true); return result; }
长按事件的流向
然后,这里就已经分发到具体事件了private void checkForLongClick(int delayOffset, float x, float y) { if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) { mHasPerformedLongPress = false; if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } mPendingCheckForLongPress.setAnchor(x, y); mPendingCheckForLongPress.rememberWindowAttachCount(); mPendingCheckForLongPress.rememberPressedState(); postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout() - delayOffset); } }/** * <p>Causes the Runnable to be added to the message queue, to be run * after the specified amount of time elapses. * The runnable will be run on the user interface thread.</p> * * @param action The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @return true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- * if the looper is quit before the delivery time of the message * occurs then the message will be dropped. * * @see #post * @see #removeCallbacks */ public boolean postDelayed(Runnable action, long delayMillis) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.postDelayed(action, delayMillis); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().postDelayed(action, delayMillis); return true; }
但是,如果一直return false怎么办
一直跟着return向回看,一直到了Activity,进行了Activity.onTouchEvent(ev)处理;
5,从View.onTouchEvent(ev)到Activity.onTouchEvent(ev)
跟着return,最终回到了Activity的分发开始方法
然后发现,这里调用mWindow边界判断,直接销毁了。。。。/** 事件分发1,入口 * 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.如果消耗此事件,则返回true。 */ public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { //按下 onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev);//事件分发13:前面都没有return true,去执行activity.onTouchEvent(ev); }
Window类自己实现了这个方法/** 事件分发13 * 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.如果你已经消耗了这个事件,则返回true,否则返回false。 * The default implementation always returns false.默认实现始终返回false。 */ public boolean onTouchEvent(MotionEvent event) { if (mWindow.shouldCloseOnTouch(this, event)) { finish(); //事件分发15:万物有始有终 return true; } return false; }
//事件分发14:主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等 /** @hide */ public boolean shouldCloseOnTouch(Context context, MotionEvent event) { final boolean isOutside = event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event) || event.getAction() == MotionEvent.ACTION_OUTSIDE; if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) { return true; } return false; }
6,感觉还少点什么
return的时候,View.onTouchEvent(ev)—>View.dispatchTouchEvent(ev)—>ViewGroup.dispatchTouchEvent(ev)
此时,如果一直没有处理,ViewGroup是View的子类,所以会走ViewGroup:View的流程。
7,android事件分发图
画个简单的,新版ViewGroup超出预期。。。。
备用图床
# 下面开始理事件是如何分发到Activity的
8,到Activity之前的分发流程,从消息分发到InputEventReceiver
抓个日志
看这个堆栈,事件分发是从ActivityThread里的消息分发Looper.loop出来的,消息队列里nativePollOnce()到了InputEventReceiver.dispatchInputEvent()05-18 20:34:02.604 3462-3462/dc.test.surname I/System.out: CaoActivity dispatchTouchEvent 0 05-18 20:34:02.604 3462-3462/dc.test.surname W/System.err: java.lang.Throwable: stack dump 05-18 20:34:02.618 3462-3462/dc.test.surname W/System.err: at java.lang.Thread.dumpStack(Thread.java:490) at dc.test.surname.cao.CaoActivity.dispatchTouchEvent(CaoActivity.java:26) at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69) at com.android.internal.policy.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2364) at android.view.View.dispatchPointerEvent(View.java:9520) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4230) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4096) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3642) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3695) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3661) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3787) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3669) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3844) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3642) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3695) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3661) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3669) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3642) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5922) 05-18 20:34:02.619 3462-3462/dc.test.surname W/System.err: at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5896) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5857) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6025) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185) at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:323) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:102)
9,InputEventReceiver建立管道接收事件
这是一个抽象类。/** 为应用程序提供接收输入事件的低级机制。 * Provides a low-level mechanism for an application to receive input events. * @hide */ public abstract class InputEventReceiver { private static final String TAG = "InputEventReceiver"; private final CloseGuard mCloseGuard = CloseGuard.get(); private long mReceiverPtr; // We keep references to the input channel and message queue objects here so that // they are not GC'd while the native peer of the receiver is using them. private InputChannel mInputChannel; private MessageQueue mMessageQueue; // Map from InputEvent sequence numbers to dispatcher sequence numbers. private final SparseIntArray mSeqMap = new SparseIntArray(); private static native long nativeInit(WeakReference<InputEventReceiver> receiver, InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(long receiverPtr); private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled); private static native boolean nativeConsumeBatchedInputEvents(long receiverPtr, long frameTimeNanos); /** 创建绑定到指定输入通道的输入事件接收器。 * Creates an input event receiver bound to the specified input channel. * * @param inputChannel The input channel. * @param looper The looper to use when invoking callbacks. */ public InputEventReceiver(InputChannel inputChannel, Looper looper) { if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null"); } if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this), inputChannel, mMessageQueue);//管道的创建 mCloseGuard.open("dispose"); } @Override protected void finalize() throws Throwable { try { dispose(true); } finally { super.finalize(); } } /** * Disposes the receiver.掐死水管 */ public void dispose() { dispose(false); } private void dispose(boolean finalized) { if (mCloseGuard != null) { if (finalized) { mCloseGuard.warnIfOpen(); } mCloseGuard.close(); } if (mReceiverPtr != 0) { nativeDispose(mReceiverPtr); mReceiverPtr = 0; } mInputChannel = null; mMessageQueue = null; } /** 事件分发F2:这里不走,走子类了 * Called when an input event is received.收到输入事件时调用。 * The recipient should process the input event and then call {@link #finishInputEvent}收件人应处理输入事件,然后调用{@link #finishInputEvent} * to indicate whether the event was handled. No new input events will be received表明事件是否得到处理。 不会收到任何新的输入事件 * until {@link #finishInputEvent} is called.直到{@link #finishInputEvent}被调用。 * * @param displayId The display id on which input event triggered.触发输入事件的显示ID。 * @param event The input event that was received.收到的输入事件。 */ public void onInputEvent(InputEvent event, int displayId) { finishInputEvent(event, false); } /** * Called when a batched input event is pending. * * The batched input event will continue to accumulate additional movement * samples until the recipient calls {@link #consumeBatchedInputEvents} or * an event is received that ends the batch and causes it to be consumed * immediately (such as a pointer up event). */ public void onBatchedInputEventPending() { consumeBatchedInputEvents(-1); } /** * Finishes an input event and indicates whether it was handled.完成输入事件并指示是否已处理。 * Must be called on the same Looper thread to which the receiver is attached.必须在接收器所连接的同一个Looper线程上调用。 * * @param event The input event that was finished. * @param handled True if the event was handled. */ public final void finishInputEvent(InputEvent event, boolean handled) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } if (mReceiverPtr == 0) { Log.w(TAG, "Attempted to finish an input event but the input event " + "receiver has already been disposed."); } else { int index = mSeqMap.indexOfKey(event.getSequenceNumber()); if (index < 0) { Log.w(TAG, "Attempted to finish an input event that is not in progress."); } else { int seq = mSeqMap.valueAt(index); mSeqMap.removeAt(index); nativeFinishInputEvent(mReceiverPtr, seq, handled); } } event.recycleIfNeededAfterDispatch(); } /** * Consumes all pending batched input events. * Must be called on the same Looper thread to which the receiver is attached. * * This method forces all batched input events to be delivered immediately. * Should be called just before animating or drawing a new frame in the UI. * * @param frameTimeNanos The time in the {@link System#nanoTime()} time base * when the current display frame started rendering, or -1 if unknown. * * @return Whether a batch was consumed */ public final boolean consumeBatchedInputEvents(long frameTimeNanos) { if (mReceiverPtr == 0) { Log.w(TAG, "Attempted to consume batched input events but the input event " + "receiver has already been disposed."); } else { return nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos); } return false; } //事件分发F1:Looper分发消息到了这里,实际是他的子类ViewRootImpl.WindowInputEventReceiver.dispatchInputEvent,但子类没重写此方法 // Called from native code. @SuppressWarnings("unused") private void dispatchInputEvent(int seq, InputEvent event, int displayId) { mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event, displayId); } // Called from native code. @SuppressWarnings("unused") private void dispatchBatchedInputEventPending() { onBatchedInputEventPending(); } public static interface Factory { public InputEventReceiver createInputEventReceiver( InputChannel inputChannel, Looper looper); } }
根据8中的信息,实际走的是他的子类
但子类没有重写dispatchInputEvent,所以调用了父类InputEventReceiver.dispatchInputEvent(),然后流向子类WindowInputEventReceiver.onInputEvent()方法final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } //事件分发F3 @Override public void onInputEvent(InputEvent event, int displayId) { enqueueInputEvent(event, this, 0, true); } @Override public void onBatchedInputEventPending() { if (mUnbufferedInputDispatch) { super.onBatchedInputEventPending(); } else { scheduleConsumeBatchedInput(); } } @Override public void dispose() { unscheduleConsumeBatchedInput(); super.dispose(); } }
10,ViewRootImpl.enqueueInputEvent按顺序增加Looper.loop过来的事件(触摸等事件)
//事件分发F4 void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { adjustInputEventForCompatibility(event); //将event事件,receiver,flags包装成一个QueuedInputEvent //QueuedInputEvent表示一个在队列中等待处理的输入事件,这个类有个next属性可以指向下一个事件 QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); // Always enqueue the input event in order, regardless of its time stamp.无论时间戳如何,始终按顺序将输入事件排入队列。 // We do this because the application or the IME may inject key events我们这样做是因为应用程序或IME可能会注入关键事件 // in response to touch events and we want to ensure that the injected keys响应触摸事件,我们希望确保注入的键 // are processed in the order they were received and we cannot trust that按照收到的顺序处理,我们不能相信 // the time stamp of injected events are monotonic.注入事件的时间戳是单调的。 QueuedInputEvent last = mPendingInputEventTail;//获得等待队列的最后一个输入事件(Pending的意思是等待的,Tail的意思是尾部) //下面的意思就是将事件加入到队列中 if (last == null) {//如果没有最后一个,就说明队列是空的,那么第一个是该事件,最后一个也是该事件 mPendingInputEventHead = q; mPendingInputEventTail = q; } else {//如果有最后一个,那么就将该事件设置成最后一个 last.mNext = q; mPendingInputEventTail = q; } mPendingInputEventCount += 1;//队列数量加1 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, mPendingInputEventCount); //如果事件需要立即处理,则执行doProcessInputEvents(), if (processImmediately) { ////事件分发F3过来的固定是true doProcessInputEvents(); } else { scheduleProcessInputEvents(); } }
11,队列中取出事件,使用责任链模式,分派处理
//事件分发F5 void doProcessInputEvents() { // Deliver all pending input events in the queue.处理队列中所有的输入事件 while (mPendingInputEventHead != null) { //下面这段代码是取出事件队列中的第一个,若有第二个,将其置为第一个 QueuedInputEvent q = mPendingInputEventHead; mPendingInputEventHead = q.mNext; if (mPendingInputEventHead == null) { mPendingInputEventTail = null; } q.mNext = null; //队列数量减1 mPendingInputEventCount -= 1; //跟踪事件 Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, mPendingInputEventCount); //下面的代码是获得当前事件的发生时间,以及此事件与上一个事件间隔间隔时间 //通过Choreographer,协调动画、输入和绘图的时间 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); } // 处理完了所有的输入事件,将处理事件等待标记设为false // We are done processing all input events that we can process right now我们已经完成了处理我们现在可以处理的所有输入事件 // so we can clear the pending flag immediately.所以我们可以立即清除待处理的标志。 if (mProcessInputEventsScheduled) { mProcessInputEventsScheduled = false; mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); } } //事件分发F6 private void deliverInputEvent(QueuedInputEvent q) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent", q.mEvent.getSequenceNumber()); //一致性验证,不用管,一致性验证就是比如说判断ACTION_DOWN和ACTION_UP是否成对出现 if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0); } InputStage stage; //责任链模式 if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } if (q.mEvent instanceof KeyEvent) { mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); } if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); } else { finishInputEvent(q); } }
12,处理触摸事件
stage是一个抽象类,子类实现处理对应输入事件;
根据之前8的堆栈日志,触摸事件由ViewPostImeInputStage处理。/** * Base class for implementing a stage in the chain of responsibility实施责任链阶段的基类 * for processing input events. 用于处理输入事件。 * <p> * Events are delivered to the stage by the {@link #deliver} method. The stage * then has the choice of finishing the event or forwarding it to the next stage. * </p> */ abstract class InputStage {}
责任链途径好长啊。。。。
/** * Delivers post-ime input events to the view hierarchy.将post-ime输入事件传递给视图层次结构。 */ final class ViewPostImeInputStage extends InputStage { public ViewPostImeInputStage(InputStage next) { super(next); } //事件分发F7,InputStage的责任链模式,触摸事件由此类处理 @Override protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q);//按键事件,比如回退键 } else { final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q);//普通的触摸点事件 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { return processTrackballEvent(q);//轨迹球事件 } else { return processGenericMotionEvent(q);//滚轮事件 } } } 。。。 //事件分发F8:处理触屏事件,分发给View private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; mAttachInfo.mUnbufferedDispatchRequested = false; mAttachInfo.mHandlingPointerEvent = true; boolean handled = mView.dispatchPointerEvent(event);//分发给了View,应该是个DecorView maybeUpdatePointerIcon(event); maybeUpdateTooltip(event); mAttachInfo.mHandlingPointerEvent = false; if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) { mUnbufferedInputDispatch = true; if (mConsumeBatchedInputScheduled) { scheduleConsumeBatchedInputImmediately(); } } return handled ? FINISH_HANDLED : FORWARD; } 。。。 }
13,DecorView:View分发到Activity
DecorView没有重写,调用父类View方法
然后到了DecorView.dispatchTouchEvent(ev);/** 事件分发F9 * Dispatch a pointer event.分发一个触摸事件 * <p> * Dispatches touch related pointer events to {@link #onTouchEvent(MotionEvent)} and all * other events to {@link #onGenericMotionEvent(MotionEvent)}. This separation of concerns * reinforces the invariant that {@link #onTouchEvent(MotionEvent)} is really about touches * and should not be expected to handle other pointing device features. * </p> * * @param event The motion event to be dispatched. * @return True if the event was handled by the view, false otherwise. * @hide */ public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } }
这里面 Activity实现了Window.Callback,最后的判断中,调用cb.dispatchTouchEvent(ev)实际调用的就是Activity.dispatchTouchEvent(ev);//事件分发F10 @Override public boolean dispatchTouchEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback();//Window.Callback包含窗体的各种回调接口,Activity进行了实现 //mFeatureId :面板的特征ID,如果这是应用程序的DecorView就为-1,在初始化时设置 //mWindow就是与Activity关联的PhoneWindow对象,DecorView对象持有PhoneWindow对象引用 //当调用cb.dispatchTouchEvent(ev)时,实际调用Activity.dispatchTouchEvent(ev). return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }
14,在Activity之前的流程
1)触摸屏幕时,ActivityThread里的Looper从MessageQueue中loop了一条Message,通过InputEventReceiver的管道给了他的子类WindowInputEventReceiver;,
2)ViewRootImpl.WindowInputEventReceiver调用ViewRootImpl.enqueueInputEvent方法,将输入事件加入输入事件队列,队列处理事件;
3)责任链中的ViewPostImeInputStage收到输入事件,将事件传递给DecorView.dispatchPointerEvent()方法,DecorView没有重写,实际调用View相应方法;
4)View.dispatchPointEvent()方法调用dispatchTouchEvent方法,通过回调Window.Callback,回调到Activity.dispatchTouchEvent(ev);
5)进入Activity.dispatchTouchEvent(ev)流程。
15,android事件分发机制流程图Framework层
明天继续2019年05月19日00:21:40
备用图床
2019年05月19日14:23:24
--
senRsl
2019年05月17日21:32:39
senRsl
2019年05月17日21:32:39
没有评论 :
发表评论