以native为终点的源码分析
Android中点击事件的来源 - 如何从硬件->Activity
先来看下这张图
引导
事件首先产生是在Activity,那么又是谁将事件传给activity
核心的代码是mWindow.setCallback(this); 是PhoneWindow传的 接着我们就继续深入
源头-ViewRootImpl
首先,点击事件是由用户的触摸行为所产生的,因此它必须要通过硬件来捕获,然后点击事件会交给WMS来处理
我们知道Activity启动以后,在它的onResume以后,DecorView才开始attach给WindowManager从而显示出来
接着,系统就会完成添加Window的过程 在这个过程中一个很重要的角色被创建了
root = new ViewRootImpl(view.getContext(), display);
在ViewRootImpl的setView方法中,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用
在这个过程中,ViewRootImpl、DecorView和WMS会彼此向关联,从而将DecorView最终添加到Window上
同时会创建InputChannel、InputQueue和WindowInputEventReceiver来接受点击事件的消息(这些都在ViewRootImpl)
这些类中都有native的dispatchInputEvent接受底层传输的事件
InputEvent有2个子类:KeyEvent和MotionEvent,其中KeyEvent表示键盘事件,而MotionEvent表示触摸事件
最终会通过deliverInputEvent方法 ,事件会经过分发由对应的InputStage处理(stage.deliver)而触摸事件由ViewPostImeInputStage处理
经过processPointerEvent方法 会调用boolean handled = mView.dispatchPointerEvent(event); 这个mView是引用的decorview
中转站-Decorview
decorview本身没有这个方法 调用父类view的方法
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
判断是否是触摸事件或者是一般移动事件,若是触摸事件则执行dispatchTouchEvent(event) 再来看看decorview重写的这个方法
终点-Activity
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
这里mWindow.getCallback()就是我们之前mWindow.setCallback(this)设置的activity,然后执行cb.dispatchTouchEvent(ev) 这样触摸事件就传给了Activity
绕了一个大圈 触摸事件成功从wms获取到给Activity
Activity->ViewGroup
核心方法–dispatchTouchEvent–onTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
// 如果是 down 事件,回调 onUserInteraction()
// onUserInteraction 是空方法,可以用来判断用户是否正在和设备交互
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;//如果有view消费了 经过这里
}
return onTouchEvent(ev);//执行activity的onTouchEvent(ev)
}
跟进getWindow().superDispatchTouchEvent(ev)进入PhoneWindow的superDispatchTouchEvent
return mDecor.superDispatchTouchEvent(event);进入decorview的superDispatchTouchEvent
(framelayout没有dispatchTouchEvent) 成功进入ViewGroup的dispatchTouchEvent
ViewGroup->View
进入ViewGroup的dispatchTouchEvent后
核心代码—onInterceptTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {// disallowIntercept 代表着子View是否禁止让父ViewGroup拦截事件
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
}
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {//循环子view
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, 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)) {//重点
// 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;
}
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
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;
}
}
}
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
// 交给自己处理,调用 ViewGroup 的 super.dispatchTouchEvent 方法
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
}
循环子view 会根据点击坐标是否落在子view范围内以及子view是否正在动画来判断是否接收事件
如果找到了一个子 View 可以接收事件,那么就会调用它的 dispatchTouchEvent 方法。若 dispatchTouchEvent 方法返回 true 的话,说明该 View 确认处理该事件了 ,否则就继续遍历重复之前的流程了。
如果 mFirstTouchTarget 为空,那么有可能没有子 View 或者所有的子 View 都不处理该事件了 那么只能交给 ViewGroup 自己处理了,之后调用了 dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS) 方法
比较下:
子view的方法 dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign) ViewGroup的方法 dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS)
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;
}
很清楚如果子view为null的话执行super.dispatchTouchEvent(event); 由父类执行
如果有子view的话child.dispatchTouchEvent(event); 由子类执行
这里成功把触摸事件从ViewGroup传到子View了
View
进入源码
第一部分:
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
这段代码 如果li.mOnTouchListener没有设置了触摸监听器就不会执行后面的li.mOnTouchListener.onTouch(代码顺序问题)
先执行了mOnTouchListener.onTouch
如果设置了OnTouchListener监听器就不会执行onTouchEvent 反之
假定为false 执行了onTouchEvent
子view可以重写onTouchEvent获取触摸事件
如果不实现会由默认实现
进入源码
第二部分:
//判断该view是否是可点击的
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
case MotionEvent.ACTION_UP:
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
if ((viewFlags & TOOLTIP) == TOOLTIP) {
handleTooltipUp();
}
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
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 (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)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
重点在ACTION_UP的 performClick();
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
li.mOnClickListener != null判断是否设置了监听器
设置了的话执行li.mOnClickListener.onClick(this); 长按同理
执行了我们平常用的xxx.setOnClickListener(listener);
总结
ps:activity没有拦截函数