Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable focus on fragment

I'm working on application for TV platform and use RCU for navigation.

I have use case where I have two fragments one above each other and visible on the screen at the same time.

Is there a way to disable focusing fragment that is below? setFocusable(false) on fragments view doesnt work, and I'm able to focus elements in fragment below.

Thanks in advance.

like image 537
Veljko Avatar asked Sep 04 '17 09:09

Veljko


2 Answers

The solution that I've come up with in the end is:

Added custom Lifecycle listeners for the fragment namely: onFragmentResume and onFragmentPause events which I call manually when I need to show/hide or switch between fragments.

@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);
    }
}
like image 144
Veljko Avatar answered Oct 29 '22 01:10

Veljko


I had the same problem and the accepted answer worked for me.

Here is my version of the implementation so far (it can be improved):

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)
            }
        }
    }

}
like image 44
Eduard Avatar answered Oct 29 '22 02:10

Eduard