安卓机顶盒开发中的焦点之二

一,焦点相关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类型,所以我们通常可以重写该方法做一些操作。

    推荐阅读