一,焦点相关Api
说明
View
中定义了一个方法,用来判断View
自身是否获取到焦点或者其内部的子View
是否获取到焦点。
/**
* Returns true if this view has focus itself, or is the ancestor of the
* view that has focus.
*
* @return True if this view has or contains focus, false otherwise.
*/
@ViewDebug.ExportedProperty(category = "focus")
public boolean hasFocus() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0;
}
View
中定义了一个isFocused
方法,该方法用于判断该View
自身是否已经获取到焦点。
/**
* Returns true if this view has focus
*
* @return True if this view has focus, false otherwise.
*/
@ViewDebug.ExportedProperty(category = "focus")
public boolean isFocused() {
return (mPrivateFlags & PFLAG_FOCUSED) != 0;
}
ViewGroup
中定义了一个名为setDescendantFocusability
的方法,该方法需要指定一个focusability
,该值可以为三种类型:
FOCUS_BEFORE_DESCENDANTS:viewgroup会优先其子类控件而获取到焦点
FOCUS_AFTER_DESCENDANTS:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
FOCUS_BLOCK_DESCENDANTS:viewgroup会覆盖子类控件而直接获得焦点
二,界面初始化时系统如何标记初始焦点
ViewRootImpl
类中有一个比较重要的方法performTraversals
,该方法中有如下一段代码:
if (mFirst) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: mView.hasFocus()="
+ mView.hasFocus());
if (mView != null) {
if (!mView.hasFocus()) {
mView.requestFocus(View.FOCUS_FORWARD);
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: requested focused view="
+ mView.findFocus());
} else {
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: existing focused view="
+ mView.findFocus());
}
}
}
handle first focus request
意味着这里处理第一次焦点的请求,注意这里的mView
其实就是DecorView
,也就是位于view tree
的最顶层的View
。其本质就是一个FrameLayout
,当自身或者其内部子View
不包含焦点的时候,就会调用requestFocus
尝试标记焦点View
。我们再来看下ViewGroup
中重写的requestFocus
:
/**
* {@inheritDoc}
*
* Looks for a view to give focus to respecting the setting specified by
* {@link #getDescendantFocusability()}.
*
* Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
* find focus within the children of this group when appropriate.
*
* @see #FOCUS_BEFORE_DESCENDANTS
* @see #FOCUS_AFTER_DESCENDANTS
* @see #FOCUS_BLOCK_DESCENDANTS
* @see #onRequestFocusInDescendants(int, android.graphics.Rect)
*/
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
if (DBG) {
System.out.println(this + " ViewGroup.requestFocus direction="
+ direction);
}
int descendantFocusability = getDescendantFocusability();
switch (descendantFocusability) {
case FOCUS_BLOCK_DESCENDANTS:
return super.requestFocus(direction, previouslyFocusedRect);
case FOCUS_BEFORE_DESCENDANTS: {
final boolean took = super.requestFocus(direction, previouslyFocusedRect);
return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
}
case FOCUS_AFTER_DESCENDANTS: {
final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
return took ? took : super.requestFocus(direction, previouslyFocusedRect);
}
default:
throw new IllegalStateException("descendant focusability must be "
+ "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
+ "but is " + descendantFocusability);
}
}
- 我们注意到,这里会根据
getDescendantFocusability
返回的结果,也就是会先判断我们有没有设置focusability
,然后会根据类型首先判断onRequestFocusInDescendants
的返回值,如果为真就直接返回了。onRequestFocusInDescendants
是个什么鬼呢,接着看:
@SuppressWarnings({"ConstantConditions"})
protected boolean onRequestFocusInDescendants(int direction,
Rect previouslyFocusedRect) {
int index;
int increment;
int end;
int count = mChildrenCount;
if ((direction & FOCUS_FORWARD) != 0) {
index = 0;
increment = 1;
end = count;
} else {
index = count - 1;
increment = -1;
end = -1;
}
final View[] children = mChildren;
for (int i = index;
i != end;
i += increment) {
View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
if (child.requestFocus(direction, previouslyFocusedRect)) {
return true;
}
}
}
return false;
}
- 我们注意到,其内部遍历所有子
View
,当子View
为可见时,就尝试去标记焦点。因为onRequestFocusInDescendants
方法为protected
类型,所以我们通常可以重写该方法做一些操作。