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

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)对焦点系统的影响及其底层实现机制。

相关推荐
fundroid15 分钟前
Swift 进军 Android,Kotlin 该如何应对?
android·ios
前端世界18 分钟前
鸿蒙系统安全机制全解:安全启动 + 沙箱 + 动态权限实战落地指南
android·安全·harmonyos
_一条咸鱼_3 小时前
Vulkan入门教程:源码级解析
android·面试·android jetpack
嘉小华3 小时前
ThreadLocal 详解
android
wkj0013 小时前
php 如何通过mysqli操作数据库?
android·数据库·php
kymjs张涛5 小时前
零一开源|前沿技术周报 #7
android·前端·ios
wuwu_q7 小时前
RK3566/RK3568 Android11 修改selinux模式
android·rk3568
_一条咸鱼_7 小时前
Android Runtime内存共享与访问控制原理剖析(71)
android·面试·android jetpack
嘉小华8 小时前
Android 协程全景式深度解析:第六章 高阶并发模式
android