栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

Android 手势分发

Java 更新时间:发布时间: 百科书网 趣学号
前言

我们都知道处理Android的手势问题时,都是通过Activity的 onTouchEvent 事件来处理的,那么今天我们来看看手势信息是从哪里传递来的呢


1. 入口

通过我们之前的Android View绘制流程中,我们知道在ViewRootImpl的setView方法中,我们生成了一个WindowInputEventReceiver对象,这个对象就是用来监听输入事件的。

ViewRootImpl类

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
        if (mView == null) {
			......
            if (inputChannel != null) {
                ......
                //注册一个输入事件监听,当有输入事件时,会触发它的onInputEvent事件,见下方
                mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                        Looper.myLooper());
                ......
            }
        }
    }
}
WindowInputEventReceiver 类

@Override
public void onInputEvent(InputEvent event) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
    List processedEvents;
    try {
        processedEvents =
            mInputCompatProcessor.processInputEventForCompatibility(event);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    if (processedEvents != null) {
        if (processedEvents.isEmpty()) {
            // InputEvent consumed by mInputCompatProcessor
            finishInputEvent(event, true);
        } else {
            for (int i = 0; i < processedEvents.size(); i++) {
                //将输入事件插入队列
                enqueueInputEvent(
                        processedEvents.get(i), this,
                        QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
            }
        }
    } else {
        enqueueInputEvent(event, this, 0, true);
    }
}
2. 加入输入事件消息队列
ViewRootImpl类

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    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 {
        //发送异步消息,最终也是调用doProcessInputEvents()
        scheduleProcessInputEvents();
    }
}
3. 派发输入事件
ViewRootImpl类

//派发消息队列的输入事件
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;

        mPendingInputEventCount -= 1;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
        
        //派发队列中的某一个事件
        deliverInputEvent(q);
    }

    // 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);
    }
}

ViewRootImpl类

//派发输入事件
private void deliverInputEvent(QueuedInputEvent q) {
    Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
            q.mEvent.getId());

    if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent src=0x"
                + Integer.toHexString(q.mEvent.getSource()) + " eventTimeNano="
                + q.mEvent.getEventTimeNano() + " id=0x"
                + Integer.toHexString(q.mEvent.getId()));
    }
    try {
        if (mInputEventConsistencyVerifier != null) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "verifyEventConsistency");
            try {
                mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
        //InputStage可以理解为输入事件的各个阶段,下面有介绍,采用的是责任链模式
        InputStage stage;
        //选择责任链的入口
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }

        if (q.mEvent instanceof KeyEvent) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager");
            try {
                mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }

        if (stage != null) {
            handleWindowFocusChanged();
            //分发事件
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

4. InputStage类

关于InputStage有很多子类,如下

责任链模式图如下

用一张图来描述InputStage的处理流程如下


对于屏幕的点击事件,我们是在ViewPostImeInputStage中的onProcess方法处理的

@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);
        }
    }
}

接下来分析processPointerEvent()方法,我们发现调用了DecorView的dispatchPointerEvent()

ViewRootImpl类

private int processPointerEvent(QueuedInputEvent q) {
    final MotionEvent event = (MotionEvent)q.mEvent;
	......
    boolean handled = mView.dispatchPointerEvent(event);
    ......
}

我们在DecorView中没有找到dispatchPointerEvent()方法,进而一步一步从父类中找,终于,在View类中找到了dispatchPointerEvent()方法

View类

public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
        //调用了dispatchTouchEvent,DecorView中覆写了该方法
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}
DecorView类

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final Window.Callback cb = mWindow.getCallback();
    // 将事件派发到了 Window.Callback 接口的实现类,也就是Activity类中
    // (Activity的attach方法中,将Activity对象设置为Window的Callback)
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
5. 分发到Activity类中

接下来分析Activity的dispatchTouchEvent()

Activity类

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    // 传递给Window处理
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}
6. 分发到PhoneWindow类中
PhoneWindow.java

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    //继而又传递给DecorView处理
    return mDecor.superDispatchTouchEvent(event);
}
7. 分发到DecorView类中,最终分发到ViewGroup中
DecorView.java

public boolean superDispatchTouchEvent(MotionEvent event) {
    //继而又传递给了ViewGroup处理
    return super.dispatchTouchEvent(event);
}

至此,我们的手势终于分发到了ViewGroup中。

总结

用一张图总结手势事件的来源

文中部分图片借鉴自Android Framework 输入子系统 InputStage解读这篇文章。

转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/957198.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号