自定义ViewGroup焦点处理

时间:2015-04-09 12:29:31

标签: android focus android-view android-custom-view viewgroup

假设我有一个可自定义的ViewGroup,它具有一些可聚焦的子视图(Android机顶盒的自定义垂直菜单应该在远程控制器上作出反应)。

每当自定义ViewGroup获得焦点时,我都需要将焦点传递给某些子视图。

我将descendantFocusability设置为beforeDescendants并将OnFocusChangeListener设置为自定义ViewGroup,但永远不会调用OnFocusChangeListener.onFocusChanged()。看起来beforeDescendants不能像我预期的那样工作。实际设置beforeDescendants的工作方式与设置afterDescendants的工作方式相同 - 最近的子视图会关注焦点,自定义ViewGroup没有机会决定应该关注哪个子视图。

如何实现理想的行为?在ViewGroup s处理焦点的最佳做法是什么?

2 个答案:

答案 0 :(得分:3)

在深入研究Android资源后,我了解如何将焦点转移到ViewGroup的孩子身上。例如,我有自定义ViewGroup,其中包含很少EditText个字段。当用户点击ViewGroup(每个EditText之外)时,我想将焦点调度到其中一个字段。

为此,我们需要将descendantFocusability设置为FOCUS_AFTER_DESCENDANTS。这意味着我们可以处理焦点事件之前我们的自定义视图将尝试集中注意力:

setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);


下一步是覆盖onRequestFocusInDescendants方法,如果设置了标记requestFocus,则会在自定义视图的FOCUS_AFTER_DESCENDANTS之前调用。在这种方法中,我们可以请求儿童焦点:

@Override
protected boolean onRequestFocusInDescendants(final int dir, final Rect rect) {
    return getChildAt(this.target).requestFocus();
}

此方法的一个重要事项是:如果我们在此处返回true,则不会要求我们的自定义视图进行关注(仅限FOCUS_AFTER_DESCENDANTS)。

通过这种方式,我们可以更改target字段,以便在点击ViewGroup时更改要关注的孩子。

<小时/> 为了更好地理解,您可以在Android资源中的requestFocus中找到ViewGroup方法。

答案 1 :(得分:2)

我有类似的问题。在我们的Android TV应用程序中,我们想要选择自定义菜单的最后一个焦点位置(这是自定义垂直LinearLayout)。

您应该覆盖方法addFocusables(views: ArrayList<View>, direction: Int, focusableMode: Int)。当ViewRootImpl搜索所有可用的可聚焦视图时,将调用此方法。标志FOCUS_AFTER_DESCENDANTS和FOCUS_BEFORE_DESCENDANTS仅确定将父视图插入到可聚焦视图中的位置。如果是FOCUS_BEFORE_DESCENDANTS,则您的自定义视图将添加到您的孩子之前。如果是FOCUS_AFTER_DESCENDANTS,则您的自定义视图将添加到您的孩子之后

覆盖的逻辑是,如果您之前没有关注过的孩子,则仅在列表中添加自定义视图。

override fun addFocusables(views: ArrayList<View>, direction: Int, focusableMode: Int) {
    // if we did have focused child before, we must add only parent view
    // otherwise our child already has focus, and we don't wont to change focus behavior
    if (focusedChild == null) {
        views.add(this)
    } else {
        super.addFocusables(views, direction, focusableMode)
    }
}

您还必须覆盖requestFocus(direction: Int, previouslyFocusedRect: Rect),因为您将焦点传递给了孩子或决定自己无法做到。

override fun requestFocus(direction: Int, previouslyFocusedRect: Rect): Boolean = 
        getViewToFocus()?.requestFocus() ?: false

在此示例中,getViewToFocus()返回我要关注的子对象;如果没有任何子对象,则返回null

private fun getViewToFocus() =
        when {
            lastSelectedPos in 0..childCount -> getChildAt(lastSelectedPos)
            isNotEmpty() -> getChildAt(0)
            else -> null
        }

我在示例中使用Kotlin,但是您也可以使用Java。享受吧!