在tv端开发中,焦点处理是一个很是重要的技术。该篇主要是想整理相关知识。(本文档依据sdk26进行分析)java
在android的绘制流程中**ViewRootImpl#performTraversals()**起着关键的做用,而焦点状态也会经过影响视图的绘制。
下面来看看android事如何进行第一次寻焦的android
private void performTraversals() {
......
if (mFirst && sAlwaysAssignFocus) {
// handle first focus request
if (mView != null) {
if (!mView.hasFocus()) {
mView.restoreDefaultFocus();
}
}
}
......
}
复制代码
mView.restoreDefaultFocus()将会去查找当前试图第一个可聚焦的View。将会执行requestFocus(int direction, Rect previouslyFocusedRect)。由于ViewGroup重写了该方法,增长了是否拦截焦点处理的逻辑,下面咱们先来看看ViewGroup#requestFocus(int direction, Rect previouslyFocusedRect)ide
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
if (DBG) {
System.out.println(this + " ViewGroup.requestFocus direction="
+ direction);
}
int descendantFocusability = getDescendantFocusability();
switch (descendantFocusability) {
//拦截焦点,无论当前View是否被聚焦,子View必定获取不到焦点。
case FOCUS_BLOCK_DESCENDANTS:
return super.requestFocus(direction, previouslyFocusedRect);
//在子View以前判断是否应被聚焦,若是为false则会去判断其子View
case FOCUS_BEFORE_DESCENDANTS: {
final boolean took = super.requestFocus(direction, previouslyFocusedRect);
return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
}
// 在子View判断焦点以后判断
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);
}
}
复制代码
接着咱们来看看View#requestFocus(int direction, Rect previouslyFocusedRect)this
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
return requestFocusNoSearch(direction, previouslyFocusedRect);
}
private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
// 判断是否可被聚焦
if ((mViewFlags & FOCUSABLE) != FOCUSABLE
|| (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
// 判断触摸状态下是否可被聚焦
if (isInTouchMode() &&
(FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
return false;
}
// 判断父View是否须要拦截
if (hasAncestorThatBlocksDescendantFocus()) {
return false;
}
//执行聚焦操做
handleFocusGainInternal(direction, previouslyFocusedRect);
return true;
}
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) {
//通知父容器改变焦点View
mParent.requestChildFocus(this, this);
updateFocusedInCluster(oldFocus, direction);
}
if (mAttachInfo != null) {
//全局监听回调
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
}
//执行焦点变化与强制绘制
onFocusChanged(true, direction, previouslyFocusedRect);
refreshDrawableState();
}
}
复制代码
首先会根据按键生成一个寻焦方向(能够查看ViewRootImpl#ViewPostImeInputStage#processKeyEvent)spa
private int processKeyEvent(QueuedInputEvent q) {
......
//上面是判断是否处理当前按键,如dispatchKeyEvent返回true则不会执行下面的焦点逻辑
// 根据当前事件生成一个寻焦方向。
if (event.getAction() == KeyEvent.ACTION_DOWN) {
int direction = 0;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (event.hasNoModifiers()) {
direction = View.FOCUS_LEFT;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (event.hasNoModifiers()) {
direction = View.FOCUS_RIGHT;
}
break;
case KeyEvent.KEYCODE_DPAD_UP:
if (event.hasNoModifiers()) {
direction = View.FOCUS_UP;
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (event.hasNoModifiers()) {
direction = View.FOCUS_DOWN;
}
break;
case KeyEvent.KEYCODE_TAB:
if (event.hasNoModifiers()) {
direction = View.FOCUS_FORWARD;
} else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
direction = View.FOCUS_BACKWARD;
}
break;
}
if (direction != 0) {
//查询当前汇集view,根据当前view查询下一个方向的聚焦view
View focused = mView.findFocus();
if (focused != null) {
//因而可知,focusSearch 为寻焦的主要方法,可从新该方法来修改焦点逻辑
View v = focused.focusSearch(direction);
if (v != null && v != focused) {
// do the math the get the interesting rect
// of previous focused into the coord system of
// newly focused view
focused.getFocusedRect(mTempRect);
if (mView instanceof ViewGroup) {
((ViewGroup) mView).offsetDescendantRectToMyCoords(
focused, mTempRect);
((ViewGroup) mView).offsetRectIntoDescendantCoords(
v, mTempRect);
}
if (v.requestFocus(direction, mTempRect)) {
playSoundEffect(SoundEffectConstants
.getContantForFocusDirection(direction));
return FINISH_HANDLED;
}
}
// Give the focused view a last chance to handle the dpad key.
if (mView.dispatchUnhandledMove(focused, direction)) {
return FINISH_HANDLED;
}
} else {
// find the best view to give focus to in this non-touch-mode with no-focus
View v = focusSearch(null, direction);
if (v != null && v.requestFocus(direction)) {
return FINISH_HANDLED;
}
}
}
}
return FORWARD;
}
复制代码
下面可查看View#focusSearch与ViewGroup#focusSearch 的相关处理rest
View.java public View focusSearch(@FocusRealDirection int direction) {
if (mParent != null) {
return mParent.focusSearch(this, direction);
} else {
return null;
}
}
ViewGroup.java public View focusSearch(View focused, int direction) {
if (isRootNamespace()) {
// 经过FocusFinder来查找下一个聚焦view
return FocusFinder.getInstance().findNextFocus(this, focused, direction);
} else if (mParent != null) {
return mParent.focusSearch(focused, direction);
}
return null;
}
复制代码
FocusFinder 是一个焦点处理的类,主要用于在一个方向上,经过当前view与聚焦方案来合理判断下一个被聚焦viewcode