系统入门(16):Android 触摸消息处理
来源:互联网 发布:贾雷德.戴蒙德 知乎 编辑:程序博客网 时间:2024/04/29 16:46
转自:http://blog.csdn.net/siobhan/article/details/8257334
1. WindowInputEventReceiver.onInputEvent() ----ViewRootImpl.java
从InputDispatch中publish一个Eent事件后,会由WindowInputEventReceiver.onInputEvent作为回调函数被调用。
- @Override
- public void onInputEvent(InputEvent event) {
- enqueueInputEvent(event, this, 0, true);
- }
1.2. ViewRootImpl.enqueueInputEvent()
把event作为一个QueuedInputEvent放到列表队列最后,如果processImmediately == true那么就直接执行doProcessInputEvents()去立即处理这个event事件,如果不是就调用scheduleProcessInputEvents把Eent通过hander放入到主线程的Looper中。
- 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();
- }
- }
2. ViewRootImpl.deliverInputEvent()
最后都会调用deliverInputEvent去分发事件,如果是KeyEent就调用deliverKeyEent(),同理如果是Touch,Pointer Event就调用deliverPointerEvent。
- private void deliverInputEvent(QueuedInputEvent q) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
- try {
- if (q.mEvent instanceof KeyEvent) {
- deliverKeyEvent(q);
- } else {
- 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);
- }
- }
2.1 ViewRootImpl.deliverPointerEvent()
1. 如果mView == null || !mAdded, 就直接调用fininshInputEvent去告诉InputDispatcher;
2. 如果是ActionDown就是通过ensureTouchMode(true)告诉WMS去设置对应WindowState的touch mode,并且调用ensureTouchModeLocally来handle the change
3. 如果是touchevent就是用mLastTouchPoint去记录此次的Point的Position用于possible drag-initiation
4. mView.dispatchPointerEvent(event); 通过DecorView去dispatchPointerEvent;
5. 无论Event是否被处理,都会调用finishInputEvent(q, true);去告诉InputDispatcher。
- 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) {
- 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) {
- 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.
- 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);
- }
2.1.4 DecorView.dispatchPointerEvent
DecorView继承FrameLayout也就间接继承了ViewGroup,View。
1. 调用父类View的dispatchPointerEvent(), 然后去调用自己的dispatchTouchEvent;
2. dispatchTouchEvent中调用callback.dispatchTouchEvent,这里的Callback就是Activity对象。
- public final boolean dispatchPointerEvent(MotionEvent event) {
- if (event.isTouchEvent()) {
- return dispatchTouchEvent(event);
- } else {
- return dispatchGenericMotionEvent(event);
- }
- }
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- final Callback cb = getCallback();
- return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
- : super.dispatchTouchEvent(ev);
- }
2.1.4.2 Activity.dispatchtouchEvent
1. onUserInteraction(), 在dispatch之前做一些操作
2. getwindow().superDispatchtouchEvent就是调用PhoneWindow中的superDispatchTouchEvent。而PhoneWindow也是直接调用mDecorView的对应的方法。而DoverView的superDispatchTouchEvent方法中是去调用了父类ViewGroup的dispatchTouchEvent。
3. 当Activity中所有的View都不处理Event的时候,就用由Activity的onTouchEvent()来处理。
这里的调用顺序从DoverView---->Activity-->PhoneWindow--->DocerView---->ViewGroup
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- onUserInteraction();
- }
- if (getWindow().superDispatchTouchEvent(ev)) {
- return true;
- }
- return onTouchEvent(ev);
- }
2.1.4.2.1 ViewGroupdispatchTouchEvent(MotionEvent ev)
1. 如果是Action_Down事件,那么把之前的TouchTargets和TouchState都clear掉,mFirstTouchTarget = null
2. onInterceptTouchEvent(),通过这个函数去告诉当前的View是否拦截掉这个Event,如果return就不会把这个event往下dispatch了
3. 如果不去Intercept当前的Event,就通过遍历自己的child views去找到处在Touch所在区域的view,找到之后通过getTouchTarget(View)去查找View是否在TouchTarget中了;如果不在,则调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign),把Event分发给child view。
4. 如果child没有消耗掉event事件,那么mFirstTouchTarget == null),这时候就会调用handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS)自己去处理此次Event; 参数中child为null。
如果ViewGroup也没处理,就会以此回溯上去给父View处理。
- 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.
- 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) {
- intercepted = onInterceptTouchEvent(ev);
- ev.setAction(action); // restore action in case it was changed
- } else {
- intercepted = false;
- }
- } else {
- ... ...
- }
- // 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);
- for (int i = childrenCount - 1; i >= 0; i--) {
- final View child = children[i];
- if (!canViewReceivePointerEvents(child)
- || !isTransformedTouchPointInView(x, y, child, null)) {
- continue;
- }
- newTouchTarget = getTouchTarget(child); //通过getTouchTarget去查找View是否在TouchTarget中了。
- 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);
- if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
- // Child wants to receive touch within its bounds.
- mLastTouchDownTime = ev.getDownTime();
- mLastTouchDownIndex = i;
- 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) {
- // 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)) {
- 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;
- }
2.1.4.2.1.3 ViewGroup.dispatchTransformedTouchEvent()
1. 主要是调用child.dispatchTouchEvent(transformedEvent);把事件递归传下去,如果child还是一个ViewGroup那么步骤和上面有一样,如果是View就调用View.dispatchTouchEvent(MotionEvent event)
- private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
- View child, int desiredPointerIdBits) {
- final boolean handled;
- // Calculate the number of pointers to deliver.
- final int oldPointerIdBits = event.getPointerIdBits();
- final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
- // 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);
- } 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);
- }
- // 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;
- }
2.1.4.2.1.3.1View.dispatchTouchEvent(MotionEvent event)
1. 如果有TouchListener就去调用注册过的TouchListener的回调函数onTouch事件并直接返回。这就是为什么我们想接收一个touch event的时候只要写一个listener的原故。
2. 如果没有TouchListener,则调用默认的onTouchEvent(event)事件
- 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)) {
- return true;
- }
- if (onTouchEvent(event)) {
- return true;
- }
- }
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
- }
- return false;
- }
2.1.4.2.1.4 Activity.onTouchEvent(MotionEvent event)
如果所有的View都不处理TouchEvent,最后由Activity来处理。
判断一下mWindow是否因此此Event要close掉,如果不close,就return false。
最后调用FinishInputEvent(false), 返回这个没有处理的Event。
- public boolean onTouchEvent(MotionEvent event) {
- if (mWindow.shouldCloseOnTouch(this, event)) {
- finish();
- return true;
- }
- return false;
- }
- 系统入门(16):Android 触摸消息处理
- Android 触摸消息处理
- Android消息处理系统
- android触摸消息处理和传递:onInterceptTouchEvent与onTouchEvent
- Android View系统源码分析(一)——概述&触摸事件总体处理流程
- [学习笔记]Android系统功能之多点触摸交互处理
- android处理触摸(touchEvent)详细流程
- Android处理触摸(touchEvent)详细流程
- android处理触摸(touchEvent)详细流程
- (2)android触摸事件处理流程
- Android:处理触摸事件
- android触摸消息的分发
- Android消息处理系统原理简要概述
- Android系统Handler消息处理机制(一)
- Android系统Handler消息处理机制(二)
- 系统入门(14):Android入门之从输入设备中获取消息
- Android 触摸响应处理逻辑
- android的触摸事件处理
- 百世集团2015暑期实习研发工程师笔试题
- 【转】ACM比赛经验
- solr4.5 schema.xml配置文件
- 【LeetCode】Anagrams
- android使用volley的GET访问服务器乱码
- 系统入门(16):Android 触摸消息处理
- 字符集
- 数据库读取错误:ran out of memory retrieving query results
- 【软考】:海明校验码(计算机组成原理)
- 如何阅读源代码
- EXTJS如何设置ComboBox的值
- POJ 3659 树形DP
- 约瑟夫问题c++
- Javascript中的继承机制总结