焦点请求流程
当我们希望某个视图获取焦点时,会调用View.requestFocus()方法,那么requestFocus()干了什么,我们可以以它作为切入点来观察焦点的请求流程。
public final boolean requestFocus() {
return requestFocus(View.FOCUS_DOWN);
}
按照遥控器上下左右键,无参方法默认以下作为焦点的请求方向
最终会调用到requestFocusNoSearch方法,传递方向和之前获得焦点的视图的区域
private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
// 判断当前视图能否获得焦点
if (!canTakeFocus()) {
return false;
}
// 判断在触摸模式下是否可聚焦
if (isInTouchMode() &&
(FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
return false;
}
// 判断父视图是否阻止了当前视图获得焦点
if (hasAncestorThatBlocksDescendantFocus()) {
return false;
}
// 判断当前视图是否可以进行layout,不可layout时,设置标记期望获得焦点
if (!isLayoutValid()) {
mPrivateFlags |= PFLAG_WANTS_FOCUS;
} else {
// 此视图可以进行layout,清除父视图的期望获得焦点标记
clearParentsWantFocus();
}
//
handleFocusGainInternal(direction, previouslyFocusedRect);
return true;
}
非搜索的情况下请求焦点会检查当前视图是否可以获取焦点,最后赋予当前视图焦点
下面看handleFocusGainInternal方法
void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {
if (DBG) {
System.out.println(this + " requestFocus()");
}
//检查当前视图是否已经获得焦点
if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
//设置已获得焦点的标记
mPrivateFlags |= PFLAG_FOCUSED;
//获取之前的焦点
View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
if (mParent != null) {
//通知父视图当前视图请求焦点了
mParent.requestChildFocus(this, this);
//更新此ViewGroup集群中获得焦点的视图
updateFocusedInCluster(oldFocus, direction);
}
if (mAttachInfo != null) {
//通知全局焦点监听器焦点视图更新
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
}
//调用onFocusChanged方法
onFocusChanged(true, direction, previouslyFocusedRect);
//刷新Drawable状态
refreshDrawableState();
}
}
handleFocusGainInternal方法会在UI表现上更新视图的焦点状态,并且调用onFocusChanged方法,我们可以在onFocusChanged方法的监听器上放大缩小获得焦点的视图
结论:requestFocus()方法会判断当前视图能否获得焦点,然后在视图层次结构之间保持视图的稳定更新获得焦点的视图并且调用监听器