androidfocusleanback

BrowseFragment next focus issues


I am working with the Android Leanback API, and I am having some headaches with the BrowseFragment. For whatever reason, I can't seem to move focus from the BrowseFragment to an ImageButton that is directly above it. Since this is supposed to be on a TV, users will only be able to navigate by shifting focus with a D-pad, there is no option for just tapping on the button.

I have the following layout in one of my fragments that basically creates a top row containing a title, the button that I want to focus on, and a logo image followed by the BrowseFragment beneath it (I swap it in at runtime where the FrameLayout is).

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="@android:color/transparent"
          android:orientation="vertical"
          android:paddingLeft="3dp"
          android:paddingRight="3dp">

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_marginLeft="@dimen/lb_browse_padding_start"
    android:layout_marginTop="@dimen/lb_browse_padding_top"
    android:layout_marginRight="@dimen/lb_browse_padding_end"
    >

    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="@dimen/lb_browse_title_text_size"
    android:layout_gravity="left"
    android:layout_centerVertical="true"
    android:id="@+id/category_title"/>

    <ImageView
        android:id="@+id/imageLogo"
        android:layout_width="108dp"
        android:layout_height="44dp"
        android:layout_gravity="right"
        android:layout_alignParentRight="true"
        android:src="@drawable/cc_logo_focused"
        android:layout_centerVertical="true"/>

    <ImageButton
        android:layout_width="@dimen/cc_genre_theme_search_image_width"
        android:layout_height="@dimen/cc_genre_theme_search_image_height"
        android:src="@drawable/search_icon_focus"
        android:layout_gravity="right"
        android:background="@drawable/button_color"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/category_title"
        android:contentDescription="Search button"
        android:scaleType="fitCenter"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:adjustViewBounds="true"
        android:padding="5dp"
        android:clickable="true"
        android:layout_centerVertical="true"
        android:id="@+id/gt_search_button"
        android:backgroundTintMode="add"
        android:backgroundTint="@color/colorTranslucentBackground"/>


</RelativeLayout>

<FrameLayout
    android:id="@+id/CategoryFragment"
    android:name="android.support.v17.leanback.BrowseFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</LinearLayout>

Any ideas for how to get the app to focus properly? Thanks!


Solution

  • I ended up using the following two functions in conjunction to fix this issue. Posting here in case anyone else has a similar issue in the future.

    browseFragment is an instance variable in the Fragment class that these methods are a part of.

    private final int maxHookIntoFocusTries = 5;
    private int hookIntoFocusTries = 0;
    
    private void initFocusManagement() {
        View view = browseFragment.getView();
        Handler handler = new Handler();
        if(view == null){
            //Wait for the view to be added
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    initFocusManagement();
                }
            };
            handler.postDelayed(runnable, 250);
        }
        if ( view instanceof ViewGroup) {
            boolean found = hookIntoFocusSearch((ViewGroup) view);
            if ( found ){
                Timber.d("Successfully fixed focus");   //This is just a log
            }else if(hookIntoFocusTries < maxHookIntoFocusTries){
                //Allow multiple attempts to hook into the focus system
                //I want to say this was needed for when the browse fragment was
                //created but the child content hadn't been populated yet.
                //Been a while since I've messed with this code though
                hookIntoFocusTries++;
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        initFocusManagement();
                    }
                }, 250);
            }
        }
    
    }
    
    private boolean hookIntoFocusSearch(ViewGroup vg) {
        boolean found = false;
        for ( int i=0; i<vg.getChildCount(); i++ ) {
            View view = vg.getChildAt(i);
            if ( view instanceof BrowseFrameLayout) {
                BrowseFrameLayout bfl = (BrowseFrameLayout)view;
                bfl.setOnFocusSearchListener(new BrowseFrameLayout.OnFocusSearchListener() {
                    @Override
                    public View onFocusSearch(View focused, int direction) {
                        if ( direction == View.FOCUS_UP ) {
                            return searchButton;
                        } else {
                            return null;
                        }
                    }
                });
                found = true;
                break;
            } else if ( view instanceof ViewGroup ) {
                boolean foundInRecurse = hookIntoFocusSearch((ViewGroup)view);
                if ( foundInRecurse ) {
                    found = true;
                    break;
                }
            }
        }
        return found;
    }
    

    EDIT: Updated code to include a retry handler