在ViewRootImpl的setView方法中。用户的触摸按键消息是体如今窗体上的。而windowManagerService则是管理这些窗体,它一旦接收到用户对窗体的一些触摸按键消息,会进行对应的动作,这样的动作是须要体如今详细的view上面。在Android中。一个详细的界面是由一个Activity呈现的,而Activity中则包括了一个window,此window中又包括了一个phoneWindow。这个phoneWindow才是真正意义上的窗体。它把一个框架布局进行了一定的包装。并提供了详细的窗体操作接口,phoneWindow中包括了一个DecorView,这个view才是包括整个Activity的ui,它将被attach到Activity主窗体中。所以说用户触摸按键的消息是由windowManagerService捕捉到然后交给phoneWindow中的DecorView进行对应的处理。而连接两者的桥梁则是一个ViewRoot类,ViewRoot类由windowManagerService创建,其内部有一个W类。这个W类是一个binder,负责WindowManagerService的ipc调用。W接收到windowManagerService发送过来的消息后。把消息传递给ViewRoot,进而传递给ActivityThread解析做出处理,

1、在ViewRootImpl.java类的setView方法中:

<span style="font-size:14px;"><span style="white-space:pre">	</span>res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mInputChannel);</span>

在这里把mWindow传递给了window,这个mWindow就是W类的一个实例

  2、当详细的触摸按键消息发生后,会由ViewRootImpl类中WindowInputEventReceiver这个类的onInputEvent方法负责接收。事实上也就是个回调。
<span style="font-size:14px;"><span style="white-space:pre">	</span>public void onInputEvent(InputEvent event) {
<span style="white-space:pre"> </span> enqueueInputEvent(event, this, 0, true);
<span style="white-space:pre"> </span>}</span>

<span style="font-size:14px;">void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
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
// 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 = mFirstPendingInputEvent;
if (last == null) {
mFirstPendingInputEvent = q;
} else {
while (last.mNext != null) {
last = last.mNext;
}
last.mNext = q;
} if (processImmediately) {
// 马上处理事件
doProcessInputEvents();
} else {
// 将事件放到队列的最后
scheduleProcessInputEvents();
}
}</span>

 在enqueueInputEvent方法中。把event增加到队列的最后面,假设processImmediately为true,则直接调用doProcessInputEvents方法。否则scheduleProcessInputEvents被调用,这两个方法最后都调用了deliverInputEvent方法。用于分发输入事件。在这种方法中推断假设是按键事件,比方说back、home等,就会调用deliverKeyEvent分发事件,一些一般的移动事件调用deliverGenericMotionEvent方法。
<span style="font-size:14px;"> private void deliverInputEvent(QueuedInputEvent q) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
try {
if (q.mEvent instanceof KeyEvent) {
// 假设是按键事件,也就是back、home等按键
deliverKeyEvent(q);
} else {
// touch事件
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
deliverPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
deliverTrackballEvent(q);
} else {
deliverGenericMotionEvent(q);
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}</span>

在deliverPointerEvent方法中,假设view对象不存在,或者没有被加入,则这个event不会被处理,直接finishInputEvent。接着推断action是否是MotionEvent.ACTION_DOWN。假设是。则表示触摸方式改变了,须要告诉windowManager在本地进行处理,由于每一次的按下操作都代表了一个新的event事件的到来。然后记录触摸的位置,这个位置就代表了应该是哪一个view来接收这个事件,然后直接调用mView.dispatchPointerEvent(event)来分发这个事件。假设这个事件被分发下去了,则结束事件。

<span style="font-size:14px;">  private void deliverPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
final boolean isTouchEvent = event.isTouchEvent();
if (mInputEventConsistencyVerifier != null) {
if (isTouchEvent) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
} else {
mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
}
} // If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
// view对象为空。或者没有被加入,这个事件就不会被处理
finishInputEvent(q, false);
return;
} // Translate the pointer event for compatibility, if needed.
if (mTranslator != null) {
mTranslator.translateEventInScreenToAppWindow(event);
} // Enter touch mode on down or scroll.
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
// 假设是MotionEvent.ACTION_DOWN
// 假设是假设触摸方式改变,告诉wm。在本地进行处理
// 每一次的按下操作就是一个触摸事件的改变
ensureTouchMode(true);
} // Offset the scroll position.
if (mCurScrollY != 0) {
event.offsetLocation(0, mCurScrollY);
}
if (MEASURE_LATENCY) {
lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano());
} // Remember the touch position for possible drag-initiation.
// 有可能拖拽開始。记录触摸的位置
if (isTouchEvent) {
mLastTouchPoint.x = event.getRawX();
mLastTouchPoint.y = event.getRawY();
} // Dispatch touch to view hierarchy.
// 给view的层级上view分发事件
// DecorView继承FrameLayout也就间接继承了ViewGroup,View
// DoverView---->Activity-->PhoneWindow--->DocerView---->ViewGroup
boolean handled = mView.dispatchPointerEvent(event);
if (MEASURE_LATENCY) {
lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
}
if (handled) {
// 结束事件
finishInputEvent(q, true);
return;
} // Pointer event was unhandled.
// 暗示事件已经被处理
finishInputEvent(q, false);
</span>

在DecorView中没有dispatchPointerEvent方法。所以调用的是View.java的dispatchPointerEvent方法中推断详细的是哪一类的事件,然后又调用了自身的dispatchTouchEvent。

<span style="font-size:14px;">public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
// 假设是触摸事件
return dispatchTouchEvent(event);
} else {
// 假设是一般的移动事件
return dispatchGenericMotionEvent(event);
}
}</span>

在DecorView.java的dispatchTouchEvent方法中,假设Activity不为空,而且没有被销毁。则调用Activity的dispatchTouchEvent方法,否则调用父类View的dispatchTouchEvent方法。
<span style="white-space:pre">	</span>@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// callback就是Activity本身
final Callback cb = getCallback();
// 假设Activity不为空,而且没有被销毁,则调用Activity的dispatchTouchEvent
// 否则调用父类的dispatchTouchEvent
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}

在Activity的dispatchTouchEvent方法中。调用的是PhoneWindow的superDispatchTouchEvent的方法去分发事件。假设到最后Activity中的全部的view都不去处理这个事件时,就有Activity的OnTouchEvent来处理。

public boolean  dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 在dispatch之前做一些操作,事实上什么也没做
onUserInteraction();
}
// 调用PhoneWindow中的superDispatchTouchEvent
// PhoneWindow中superDispatchTouchEvent 直接调用了mDecor的superDispatchTouchEvent
// mDecore的superDispatchTouchEvent方法中直接调用super.dispatchOnTouchEvent
// 也就是開始进入了viewGroup中的dispatchOnTouchEvent方法
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
// 当Activity中全部的View都不处理Event的时候,就用由Activity的onTouchEvent()来处理
// 通知window关闭这个touch事件
return onTouchEvent(ev);
}
 public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
} return false;
}

在PhoneWindow的superDispatchTouchEvent中。调用了DecorView的superDispatchTouchEvent方法,进而调用了super.dispatchTouchEvent(event)方法,也就是进入了viewGroup中開始事件的分发。

 @Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
 public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}

ViewGroup的dispatchTouchEvent中,处理例如以下,假设action是MotionEvent.ACTION_DOWN,则须要重置触摸的状态。

继而推断是否拦截此事件,然后遍历全部的孩子以便找到一个能够接收此事件的孩子,假设child不存在TouchTarget中,则把事件分发给子view,这个寻找是依据view的区域来寻找的。假设childView没有消费掉此事件,则
自己处理的事件,假设自己也没有处理。回溯至父view处理,否则viewGroup把事件一级一级的递归传递,假设child是一个viewGroup,则反复上述的步骤,假设是view,直接调用dispatchTouchEvent方法。

 public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
} boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down.
if (actionMasked == MotionEvent.ACTION_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.
// ACTION_DOWN意味着touch事件的改变,所以须要把之前的TouchTargets和TouchState都clear掉。mFirstTouchTarget = null
cancelAndClearTouchTargets(ev);
// 重置触摸的状态
resetTouchState();
} // Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 拦截事件,默认返回false,表示不拦截,假设拦截了就不继续往以下运行了
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; // Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_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 (childrenCount != 0) {
// Find a child that can receive the event.
// Scan children from front to back.
// 遍历全部的孩子,以便找到一个能够接收这个事件的孩子
// 某个区域内的孩子
final View[] children = mChildren;
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex); 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;
}
// 推断child是否在TouchTarget中
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// 存在
// 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;
} resetCancelNextUpFlag(child);
// child不存在TouchTarget中,则调用dispatchTransformedTouchEvent
// 把event分发给子view,这里并非做处理。应该就是找到touch区域
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();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
} 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;
}
}
} // Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// mFirstTouchTarget 为空。表示childview没有将此事件消费掉,则自己处理这个event
// 假设viewGroup自己也没有处理。则回溯到父view进行处理
// 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;
// viewGroup把事件递归传递,假设child是一个gourp。则反复上述步骤
// 假设是view,则直接调用dispatchTouchEvent方法
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;
}
} // Update list of touch targets for pointer up or cancel, if needed.
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;
}
在View.java中的 dispatchTouchEvent方法中,假设已经注冊了listener监听器而且是enable的。而且监听器的onTouch返回true,则onTouchEvent不会被调用。
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)) {
// 要想运行onTouchEvent方法上述三个条件仅仅要一个不满足就能够了
//
return true;
} if (onTouchEvent(event)) {
return true;
}
// 假设全部的View都不处理TouchEvent,最后由Activity来处理
} if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
return false;
}

假设Activity中全部的view都不处理这个事件。则由Activity自己处理
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
} return false;
}

从源代码解释Android事件分发机制的更多相关文章

  1. Android 事件分发机制具体解释

    很多其它内容请參照我的个人网站: http://stackvoid.com/ 网上非常多关于Android事件分发机制的解释,大多数描写叙述的都不够清晰,没有吧来龙去脉搞清晰,本文将带你从Touch事 ...

  2. Android事件分发机制(上)

    Android事件分发机制这个问题不止一个人问过我,每次我的回答都显得模拟两可,是因为自己一直对这个没有很好的理解,趁现在比较闲对这个做一点总结 举个例子: 你当前有一个非常简单的项目,只有一个Act ...

  3. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  4. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  5. 【自己定义控件】android事件分发机制

    自己定义Viewgrou中我们或许会常常碰到这种情况,2个子控件的事件冲突导致滑动没实用了.滑动反应非常慢,点击没用了,要划非常多次才移动一点点等等.或许我们第一反应就是百度,google去搜索下答案 ...

  6. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  7. Android事件分发机制二:viewGroup与view对事件的处理

    前言 很高兴遇见你~ 在上一篇文章 Android事件分发机制一:事件是如何到达activity的? 中,我们讨论了触摸信息从屏幕产生到发送给具体 的view处理的整体流程,这里先来简单回顾一下: 触 ...

  8. 本以为精通Android事件分发机制,没想到被面试官问懵了

    文章中出现的源码均基于8.0 前言 事件分发机制不仅仅是核心知识点更是难点,并且还是View的一大难题滑动冲突解决方法的理论基础,因此掌握好View的事件分发机制是十分重要的. 一.基本认识 1. 事 ...

  9. Android事件分发机制(下)

    这篇文章继续讨论Android事件分发机制,首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子 ...

随机推荐

  1. pms前端结构

    后台采用.net MVC框架,前端采用requirejs.整个系统页面布局基本不变,每个页面只改变Main_Content部分. 模板页cshtml: <!DOCTYPE html> &l ...

  2. Android 开源库和项目 3

    Android 开源库和项目 Android 开源库和项目 2 1.Matisse Android 图片选择器 -- 知乎开源 github 地址:https://github.com/zhihu/M ...

  3. using 和try/catch区别和注意点

    书上解释: using: 在C#和其他托管语言中,没有自动.决定性的析构方式,而是有一个垃圾收集器,它会在未来的某个时刻释放资源.它是非决定性的,因为我们不能确定这个过程在什么时候发生.忘记关闭数据库 ...

  4. MAYA逼真手枪制作视频教程 中文字幕

    下载地址 更多中文字幕教程请关注微镜映画网,有各类CG教程提供

  5. "System.OutOfMemoryException" exception when you execute a query in SQL Server Management Studio (转自MSDN)

    Symptoms When you use Microsoft SQL Server Management Studio (SSMS) to run an SQL query that returns ...

  6. 认识与入门 MarkDown 标记语言

    一.MarkDown 概念: 一种轻量级的标记语言,标记符号不超过十个,现有很多支持MarkDown语法的编辑器以及网站.Markdown从写作到完成,导出格式随心所欲,可以导出HTML,也可以导出P ...

  7. net.exe use命令的使用

    net.exe use 查看当前的连接 net.exe use * /del /y 断开所有连接 net.exe use \\server\share "password" /us ...

  8. How to add hyperlink in JLabel

    You can do this using a JLabel, but an alternative would be to style a JButton. That way, you don't ...

  9. 15 Top Paying IT Certifications In 2016: AWS Certified Solutions Architect Leads At $125K

    Each of the five Amazon Web Services (AWS) certifications brings in an average salary of more than $ ...

  10. Windows XP添加硬盘后系统不能识别(没有任何反应)

    解决方法: 1.右键我的电脑--管理--设备管理器--IDE ATA/ATAPI控制器,启用次要IDE通道和主要IDE通道,打开属性,在高级设置里,将设备类型设置为自动检测,重启. 2.硬盘格式为GP ...