1.1 焦点基础模型与状态管理
焦点状态存储机制
在Android系统中,每个View都通过位标记(bit flags)来管理其焦点状态:
// View.java 焦点状态存储
private int mPrivateFlags; // 使用位标记存储焦点状态
// 焦点相关标志位定义
static final int PFLAG_FOCUSED = 0x00000001;
static final int PFLAG_FOCUSABLE = 0x00000010;
static final int PFLAG_FOCUSABLE_IN_TOUCH_MODE = 0x00000020;
// 判断焦点状态的方法
public boolean isFocused() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0;
}
public boolean isFocusable() {
return (mPrivateFlags & PFLAG_FOCUSABLE) != 0;
}
焦点状态变更流程
当焦点状态改变时,系统会触发一系列回调:
1.2 焦点与输入事件关系
事件分发链
Android输入事件的分发遵循特定路径:
关键源码解析
// ViewRootImpl.java
public void processKeyEvent(KeyEvent event) {
// 获取当前焦点视图
View focused = mView.findFocus();
if (focused != null) {
// 将事件分发给焦点视图
if (focused.dispatchKeyEvent(event)) {
return; // 事件已被处理
}
}
// 系统级按键处理(如返回键)
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
// 处理返回键逻辑
break;
}
}
}
1.3 焦点请求流程
焦点请求调用链
// 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. 检查View是否可获取焦点
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);
}
}
}
父容器处理流程
// ViewGroup.java
public void requestChildFocus(View child, View focused) {
// 清除之前的焦点
if (mFocused != child) {
if (mFocused != null) {
mFocused.unFocus();
}
mFocused = child; // 设置新的焦点子视图
}
// 向上传递到根视图
if (mParent != null) {
mParent.requestChildFocus(this, focused);
}
}
1.4 焦点变更事件流
完整焦点变更流程
1.5 触摸模式对焦点的影响
触摸模式状态管理
// ViewRootImpl.java
void setTouchMode(boolean inTouchMode) {
// 更新全局触摸模式状态
mAttachInfo.mInTouchMode = inTouchMode;
// 广播到所有视图
mView.dispatchTouchModeChange(inTouchMode);
}
// View.java
public void dispatchTouchModeChange(boolean inTouchMode) {
onTouchModeChanged(inTouchMode);
if (mChildren != null) {
for (View child : mChildren) {
child.dispatchTouchModeChange(inTouchMode);
}
}
}
触摸事件中的焦点处理
// View.java
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 在触摸模式下自动请求焦点
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
requestFocus();
}
break;
}
// ... 其他处理逻辑
}
1.6 焦点相关XML属性详解
| 属性 | 说明 | 默认值 | 示例 |
|---|---|---|---|
android:focusable | 视图是否可获得焦点 | false | android:focusable="true" |
android:focusableInTouchMode | 触摸模式下是否可获得焦点 | false | android:focusableInTouchMode="true" |
android:nextFocusDown | 向下导航时的下一个焦点视图ID | 无 | android:nextFocusDown="@+id/next_view" |
android:nextFocusUp | 向上导航时的下一个焦点视图ID | 无 | android:nextFocusUp="@id/prev_view" |
android:nextFocusLeft | 向左导航时的下一个焦点视图ID | 无 | android:nextFocusLeft="@+id/left_view" |
android:nextFocusRight | 向右导航时的下一个焦点视图ID | 无 | android:nextFocusRight="@+id/right_view" |
1.7 焦点调试技巧
ADB调试命令
# 查看当前窗口焦点信息
adb shell dumpsys window windows | grep -E 'Focus|Current'
# 启用焦点调试日志
adb shell setprop log.tag.FocusFinder DEBUG
adb logcat -s FocusFinder
# 检查视图焦点状态
adb shell dumpsys activity top | grep -A 35 "Focus"
代码调试方法
// 在Activity中打印当前焦点视图
View focusedView = getCurrentFocus();
if (focusedView != null) {
Log.d("FocusDebug", "Focused view: " + focusedView.getClass().getSimpleName());
}
// 检查视图焦点状态
boolean isFocused = myView.isFocused();
boolean isFocusable = myView.isFocusable();
boolean inTouchMode = myView.isInTouchMode();
本章小结
- 焦点状态存储:通过
mPrivateFlags位标记管理焦点状态 - 事件分发机制:按键事件优先分发给当前焦点视图
- 焦点请求流程:
requestFocus()触发焦点变更的完整链式调用 - 触摸模式影响:触摸事件自动触发焦点请求的特殊逻辑
- 属性配置:XML属性控制焦点行为和导航顺序
- 调试技巧:ADB命令和代码方法检查焦点状态
在下一章中,我们将深入探讨Android的焦点查找算法(FocusFinder),解析系统如何确定下一个焦点视图的实现原理。