第三章:焦点分发全链路源码解析

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

本章小结

  1. 按键事件流程

    • InputManagerService → ViewRootImpl → 焦点View
    • 方向键触发焦点查找和变更
  2. 焦点变更核心流程

    • focusSearch() 查找下一个焦点
    • requestFocus() 请求焦点
    • handleFocusGainInternal() 处理焦点获取
    • requestChildFocus() 通知父容器
  3. 系统级管理

    • ViewRootImpl 跟踪窗口焦点
    • WindowManagerService 管理应用焦点
    • AccessibilityManager 同步无障碍焦点
  4. 调试技术

    • 内置日志标签:ViewRootImplFocusFinder
    • 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)对焦点系统的影响及其底层实现机制。

相关推荐
雨白8 小时前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk8 小时前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING9 小时前
RN容器启动优化实践
android·react native
恋猫de小郭11 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker16 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴16 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭1 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农2 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos