禁用片段的焦点

10

我正在为电视平台开发应用程序,并使用RCU进行导航。

我有一个使用案例,其中有两个片段上下堆叠并同时显示在屏幕上。

有没有办法禁用位于下方的片段的焦点? 对片段视图执行setFocusable(false)无效,并且我能够将焦点设置到下面的片段元素上。

提前感谢。


你可以在onCreate方法中以编程方式添加setOnClickListner。 - Samir Bhatt
类似于此 https://dev59.com/_ILba4cB1Zd3GeqPh64W#25841415 - Samir Bhatt
1
为什么要使用onClickListener?我需要类似于onFocusChanged的东西吗? 我不使用触摸事件,这是带有遥控器的Android电视。 - Veljko
@Veljko,你能找到解决方案吗?我也有同样的问题。 - ysfcyln
1
@ysfcyln,我已经发布了我最终使用的解决方案。 - Veljko
4个回答

5
我最终想出的解决方案是:
为片段添加自定义生命周期监听器,即onFragmentResumeonFragmentPause事件,需要在显示/隐藏或在片段之间切换时手动调用。
@Override
public void onFragmentResume() {

    //Enable focus
    if (getView() != null) {

        //Enable focus
        setEnableView((ViewGroup) view, true);

        //Clear focusable elements
        focusableViews.clear();
    }

    //Restore previous focus
    if (previousFocus != null) {
        previousFocus.requestFocus();
    }
}

@Override
public void onFragmentPause() {

    //Disable focus and store previously focused
    if (getView() != null) {

        //Store last focused element
        previousFocus = getView().findFocus();

        //Clear current focus
        getView().clearFocus();

        //Disable focus
        setEnableView((ViewGroup) view, false);
    }
}

/**
 * Find focusable elements in view hierarchy
 *
 * @param viewGroup view
 */
private void findFocusableViews(ViewGroup viewGroup) {

    int childCount = viewGroup.getChildCount();
    for (int i = 0; i < childCount; i++) {
        View view = viewGroup.getChildAt(i);
        if (view.isFocusable()) {
            if (!focusableViews.contains(view)) {
                focusableViews.add(view);
            }
        }
        if (view instanceof ViewGroup) {
            findFocusableViews((ViewGroup) view);
        }
    }
}

/**
 * Enable view
 *
 * @param viewGroup
 * @param isEnabled
 */
private void setEnableView(ViewGroup viewGroup, boolean isEnabled) {

    //Find focusable elements
    findFocusableViews(viewGroup);

    for (View view : focusableViews) {
        view.setEnabled(isEnabled);
        view.setFocusable(isEnabled);
    }
}

2
我遇到了同样的问题,被接受的答案对我有用。
以下是迄今为止的实现版本(可以改进):
abstract class BaseFragment<....> : Fragment() {

    private val screenFocusHelper = ScreenFocusHelper()

    fun enableFocus() {
        if (view != null) {
            // Enable focus
            screenFocusHelper.setEnableView(view as ViewGroup, true)

            // Clear focusable elements
            screenFocusHelper.focusableViews.clear()
        }

        childFragmentManager.fragments.forEach {
            if (it is BaseFragment<*, *>) {
                it.enableFocus()
            }
        }
    }

    fun disableFocus() {
        if (view != null) {
            // Store last focused element
            screenFocusHelper.previousFocus = view?.findFocus()

            // Clear current focus
            view!!.clearFocus()

            // Disable focus
            screenFocusHelper.setEnableView(view as ViewGroup, false)
        }

        childFragmentManager.fragments.forEach {
            if (it is BaseFragment<*, *>) {
                it.disableFocus()
            }
        }
    }

}

class ScreenFocusHelper {

    var previousFocus: View? = null

    val focusableViews: MutableList<View> = mutableListOf()

    fun setEnableView(viewGroup: ViewGroup, isEnabled: Boolean) {
        findFocusableViews(viewGroup)

        for (view in focusableViews) {
            view.isEnabled = isEnabled
            view.isFocusable = isEnabled
        }
    }

    private fun findFocusableViews(viewGroup: ViewGroup) {
        val childCount = viewGroup.childCount
        for (i in 0 until childCount) {
            val view = viewGroup.getChildAt(i)
            if (view.isFocusable) {
                if (!focusableViews.contains(view)) {
                    focusableViews += view
                }
            }
            if (view is ViewGroup) {
                findFocusableViews(view)
            }
        }
    }

}

1

这是基于 leanback 的 GuidedStepFragment 的解决方案,它将阻止对不属于您布局子项的视图进行任何聚焦。

创建一个自定义视图,并将其设置为位于顶部的第二个 Fragment 的根布局:

import android.R.attr
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout

class GuidedConstraintLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

private var mFocusOutStart = false
private var mFocusOutEnd = false

fun setFocusOutStart(focusOutStart: Boolean) {
    mFocusOutStart = focusOutStart
}

fun setFocusOutEnd(focusOutEnd: Boolean) {
    mFocusOutEnd = focusOutEnd
}

override fun focusSearch(focused: View?, direction: Int): View? {
    val newFocus = super.focusSearch(focused, direction)
    if (direction == FOCUS_LEFT || direction == FOCUS_RIGHT || direction == FOCUS_UP || direction == FOCUS_DOWN) {
        if (isDescendant(this, newFocus)) {
            return newFocus
        }
        if (if (layoutDirection == LAYOUT_DIRECTION_LTR) attr.direction == FOCUS_LEFT else attr.direction == FOCUS_RIGHT) {
            if (!mFocusOutStart) {
                return focused
            }
        } else {
            if (!mFocusOutEnd) {
                return focused
            }
        }
    }
    return newFocus
}

private fun isDescendant(parent: ViewGroup, child: View?): Boolean {
    var localChild = child
    while (localChild != null) {
        if (localChild === parent) {
            return true
        }
        val p = localChild.parent
        if (p !is View) {
            return false
        }
        localChild = p
    }
    return false
}
}

0
我解决了这个问题,创建了一个自定义类来作为片段布局的父级ViewGroup。在您的自定义ViewGroup中,您可以重写方法

focusSearch(focused: View?, direction: Int)

添加以下逻辑:

override fun focusSearch(focused: View?, direction: Int): View? {

        val focusSearch = super.focusSearch(focused, direction)

        if (direction == View.FOCUS_DOWN) when (focused?.id) {
            R.id.some_view_that_has_focus -> return new_view_focus
        }

        if (findViewById<View>(focusSearch.id) == null) {//the view found is not part of this parent return null
            return null
        }

        return focusSearch
}

当新的焦点视图不是这个父级的一部分时,返回null。当其他视图的焦点无法到达时,我会在when语句中手动处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接