3.1 按键事件处理全流程
事件传递链路

核心源码解析
csharp
// ViewRootImpl.java
public void processKeyEvent(KeyEvent event) {
// 1. 获取当前焦点视图
View focused = mView.findFocus();
// 2. 分发事件到焦点视图
if (focused != null && focused.dispatchKeyEvent(event)) {
return; // 事件已被处理
}
// 3. 系统级按键处理
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_RIGHT:
handleDirectionalKey(FOCUS_RIGHT);
break;
// 其他方向键处理...
}
}
}
private void handleDirectionalKey(int direction) {
View focused = mView.findFocus();
if (focused != null) {
View next = focused.focusSearch(direction);
if (next != null && next.requestFocus(direction)) {
// 焦点成功转移
playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
}
}
}
3.2 焦点查找流程
View.focusSearch 实现
kotlin
// View.java
public View focusSearch(int direction) {
if (mParent != null) {
return mParent.focusSearch(this, direction); // 委托给父容器
}
return null;
}
// ViewGroup.java
@Override
public View focusSearch(View focused, int direction) {
// 1. 检查用户指定焦点
View userSetNext = focused.findUserSetNextFocus(this, direction);
if (userSetNext != null) return userSetNext;
// 2. 使用FocusFinder查找
return FocusFinder.getInstance().findNextFocus(this, focused, direction);
}
3.3 焦点请求与变更流程
焦点请求调用链
java
// View.java
public final boolean requestFocus() {
return requestFocus(View.FOCUS_DOWN);
}
public boolean requestFocus(int direction) {
return requestFocusNoSearch(direction, null);
}
private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
// 1. 检查焦点获取条件
if (!canTakeFocus()) return false;
// 2. 处理焦点获取
handleFocusGainInternal(direction, previouslyFocusedRect);
return true;
}
void handleFocusGainInternal(@FocusRealChange boolean gainFocus, int direction, Rect previouslyFocusedRect) {
if (gainFocus) {
// 设置焦点标志位
mPrivateFlags |= PFLAG_FOCUSED;
// 触发焦点变更回调
onFocusChanged(true, direction, previouslyFocusedRect);
// 刷新视图状态
refreshDrawableState();
// 通知监听器
if (mOnFocusChangeListener != null) {
mOnFocusChangeListener.onFocusChange(this, true);
}
// 通知父容器
if (mParent != null) {
mParent.requestChildFocus(this, this);
}
}
}
ViewGroup 焦点处理
scss
// ViewGroup.java
@Override
public void requestChildFocus(View child, View focused) {
// 1. 更新焦点子视图
if (mFocused != child) {
if (mFocused != null) {
mFocused.unFocus(); // 清除原焦点
}
mFocused = child; // 设置新焦点子视图
}
// 2. 处理滚动容器逻辑
if (mParent != null) {
mParent.requestChildFocus(this, focused);
}
// 3. 处理自动滚动
if (mScrollY != 0) {
scrollTo(mScrollX, 0);
}
}
3.4 焦点变更事件流
完整事件序列

焦点丢失处理
typescript
// View.java
void unFocus() {
handleFocusGainInternal(false);
}
private void handleFocusGainInternal(boolean gainFocus, ...) {
if (!gainFocus) {
// 清除焦点标志位
mPrivateFlags &= ~PFLAG_FOCUSED;
// 触发焦点丢失回调
onFocusChanged(false, 0, null);
// 刷新视图状态
refreshDrawableState();
// 通知监听器
if (mOnFocusChangeListener != null) {
mOnFocusChangeListener.onFocusChange(this, false);
}
}
}
3.5 系统级焦点管理
ViewRootImpl 焦点跟踪
scss
// ViewRootImpl.java
void setView(View view) {
// 初始化焦点状态
mFocusedView = null;
}
void requestChildFocus(View child, View focused) {
// 更新全局焦点视图
if (mFocusedView != focused) {
mFocusedView = focused;
// 通知输入系统
InputManager.getInstance().setFocusedWindow(
mWindowAttributes.token, focused != null);
// 更新无障碍焦点
if (mAccessibilityManager.isEnabled()) {
mAccessibilityManager.notifyAccessibilityStateChanged();
}
}
}
窗口焦点变更
typescript
// WindowManagerService.java
public void setFocusedApp(IBinder token) {
mFocusedApp = token;
InputManagerService.setFocusedApplication(token);
// 通知所有视图焦点状态变更
mRoot.forAllWindows(window -> {
window.setFocused(token != null && token.equals(window.mToken));
});
}
3.6 无障碍焦点同步
焦点与无障碍服务
scss
// View.java
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
// 设置焦点状态
info.setFocused(isFocused());
// 设置焦点区域
Rect bounds = new Rect();
getDrawingRect(bounds);
info.setBoundsInScreen(bounds);
}
// AccessibilityNodeInfo.java
public void setAccessibilityFocused(boolean focused) {
// 通知View系统更新焦点状态
mSource.setAccessibilityFocused(focused);
}
焦点状态同步流程

3.7 焦点调试与日志
焦点状态追踪
java
// ViewRootImpl.java
private void traceFocusChange(View newFocus) {
if (DEBUG_FOCUS) {
Log.v(TAG, "Focus moving from " + mFocusedView + " to " + newFocus);
// 打印焦点堆栈
if (newFocus != null) {
Log.v(TAG, "Focus trace:");
ViewParent parent = newFocus.getParent();
while (parent != null) {
Log.v(TAG, " " + parent);
parent = parent.getParent();
}
}
}
}
ADB调试命令
perl
# 查看当前焦点窗口
adb shell dumpsys window | grep mCurrentFocus
# 查看焦点视图树
adb shell dumpsys activity top | grep -A 50 "Focus"
# 启用焦点变更日志
adb shell setprop log.tag.ViewRootImpl DEBUG
本章小结
-
按键事件流程:
- InputManagerService → ViewRootImpl → 焦点View
- 方向键触发焦点查找和变更
-
焦点变更核心流程:
focusSearch()
查找下一个焦点requestFocus()
请求焦点handleFocusGainInternal()
处理焦点获取requestChildFocus()
通知父容器
-
系统级管理:
- ViewRootImpl 跟踪窗口焦点
- WindowManagerService 管理应用焦点
- AccessibilityManager 同步无障碍焦点
-
调试技术:
- 内置日志标签:
ViewRootImpl
、FocusFinder
- ADB命令查看焦点状态
- 焦点变更堆栈追踪
- 内置日志标签:
关键源码路径:
frameworks/base/core/java/android/view/ViewRootImpl.java
frameworks/base/core/java/android/view/View.java
frameworks/base/core/java/android/view/ViewGroup.java
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
在下一章中,我们将深入分析触摸模式(Touch Mode)对焦点系统的影响及其底层实现机制。