ViewRootImpl
连接 DecorView, Window
DecorView -> Activity -> PhoneWindow -> DecorView 的来回绕一圈。
既然触摸事件已经到了 Activity.dispatchTouchEvent() 中了,为什么不直接分发给 DecorView ,而是要通过PhoneWindow 来间接发送呢?因为 Activity 不知道有 DecorView 这种奇怪的东西存在啊!不知道!但是,Activity 持有 PhoneWindow ,而 PhoneWindow 当然知道自己的窗口里有些什么了,所以能够把事件派发给DecorView 。你看,在 Android 中,Activity 并不知道自己的 Window 中有些什么,这样耦合性就很低了。
1 | //Activity |
InputStage 策略
在 RootViewImpl 中的函数通道是各种策略(InputStage)的组合,各策略负责的任务不同,如SyntheticInputStage、ViewPostImeInputStage、NativePostImeInputStage 等等,这些策略以链表结构结构起来,当一个策略者没有消费事件时,就传递个下一个策略者。其中触摸和按键事件由 ViewPostImeInputStage 处理。
ViewPostImeInputStage 是 ViewRootImpl 的内部类,
分发 KeyEvent
ViewPostImeInputStage
-> onProcess (KeyEvent)
-> processKeyEvent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78private int processKeyEvent(QueuedInputEvent q) {
...
// Deliver the key to the view hierarchy.
// mView 即 DecorView
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
...
// Handle automatic focus changes.
if (performFocusNavigation(event)) {
return FINISH_HANDLED;
}
}
private boolean performFocusNavigation(KeyEvent event) {
int direction = 0;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (event.hasNoModifiers()) {
direction = View.FOCUS_LEFT;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (event.hasNoModifiers()) {
direction = View.FOCUS_RIGHT;
}
break;
case KeyEvent.KEYCODE_DPAD_UP:
if (event.hasNoModifiers()) {
direction = View.FOCUS_UP;
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (event.hasNoModifiers()) {
direction = View.FOCUS_DOWN;
}
break;
case KeyEvent.KEYCODE_TAB:
if (event.hasNoModifiers()) {
direction = View.FOCUS_FORWARD;
} else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
direction = View.FOCUS_BACKWARD;
}
break;
}
if (direction != 0) {
View focused = mView.findFocus();
if (focused != null) {
View v = focused.focusSearch(direction);
if (v != null && v != focused) {
// do the math the get the interesting rect
// of previous focused into the coord system of
// newly focused view
focused.getFocusedRect(mTempRect);
if (mView instanceof ViewGroup) {
((ViewGroup) mView).offsetDescendantRectToMyCoords(
focused, mTempRect);
((ViewGroup) mView).offsetRectIntoDescendantCoords(
v, mTempRect);
}
if (v.requestFocus(direction, mTempRect)) {
playSoundEffect(SoundEffectConstants
.getContantForFocusDirection(direction));
return true;
}
}
// Give the focused view a last chance to handle the dpad key.
if (mView.dispatchUnhandledMove(focused, direction)) {
return true;
}
} else {
if (mView.restoreDefaultFocus()) {
return true;
}
}
}
}DecorView
-> dispatchKeyEvent
1
2
3
4
5@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// Let the focused view and/or our descendants get the key first
return super.dispatchKeyEvent(event) || executeKeyEvent(event);
}ViewGroup
-> dispatchKeyEvent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16@Override
public boolean dispatchKeyEvent(KeyEvent event) {
...
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
if (super.dispatchKeyEvent(event)) {
return true;
}
} else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
== PFLAG_HAS_BOUNDS) {
if (mFocused.dispatchKeyEvent(event)) {
return true;
}
}
...
}如果父类(View)处理了(return true),则不往下传。或者 mFocused 不为空,先 mFocused 分发,如果 mFocused 没有处理(返回 true),则 DecorView 最终返回 false,交由 ViewRoomImpl 继续处理。
View
-> dispatchKeyEvent
1
2
3
4
5
6
7
8
9public boolean dispatchKeyEvent(KeyEvent event) {
...
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
return true;
}
...
}这里可以看出,要拦截焦点分发,可以:
- 设置 OnKeyListener 监听
- 重写 dispatchKeyEvent 方法并返回 true
mView 分发焦点之后没有被处理,会先根据 KeyCode 为 direction 赋值,用于后续的寻找焦点。
View focused = mView.findFocus()
会一层一层往下直到返回当前持有焦点的 View,如果 DecorView 的子 view 持有焦点(focused),则调用 focused 的 focusSearch 方法寻找下一个焦点。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//ViewGroup
@Override
public View focusSearch(View focused, int direction) {
if (isRootNamespace()) {
// root namespace means we should consider ourselves the top of the
// tree for focus searching; otherwise we could be focus searching
// into other tabs. see LocalActivityManager and TabHost for more info.
return FocusFinder.getInstance().findNextFocus(this, focused, direction);
} else if (mParent != null) {
return mParent.focusSearch(focused, direction);
}
return null;
}
//View
public View focusSearch(@FocusRealDirection int direction) {
if (mParent != null) {
return mParent.focusSearch(this, direction);
} else {
return null;
}
}可以看到 focusSearch 方法最终调用
FocusFinder.getInstance().findNextFocus(this, focused, direction)
来寻找下一个获取焦点的 View。FocusFinder 会优先通过 View 在 XML 布局设置的下一个焦点的 ID 来查找焦点。