Creating a Google TV Sliding Navigation Menu

The standard design pattern for Google TV apps is generally to use the LeftNavBar library. Unfortunately there isn’t an apklib and it isn’t included as a google tv addon library, so one has to compile and work with the library itself. The LeftNavBar library is basically the action bar flipped vertically.

The problem I have with it it is that the bar is always visible on the screen, and it doesn’t implement the Drawer Layout pattern, which lets the menu slide all the way in. It allows shows the icons that are available and it is always visible on the screen. So, I’ve created an alternative that will be used with Serenity starting with 1.4.0. This uses the MenuDrawer library, and the following custom layout.
 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#50000000" >

    <LinearLayout
        android:id="@+id/title_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="59dp"
        android:background="@android:color/background_dark"
        android:gravity="center_vertical"
        android:paddingLeft="8dip"
        android:paddingRight="8dip" >

        <ImageView
            android:id="@+id/left_icon"
            android:layout_width="36dip"
            android:layout_height="36dip"
            android:layout_marginRight="8dip"
            android:scaleType="fitCenter"
            android:src="@drawable/serenity_bonsai_logo_small"
            android:visibility="visible" />

        <LinearLayout
            android:layout_width="0dip"
            android:layout_height="59dp"
            android:layout_marginRight="8dip"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/title"
                android:layout_width="match_parent"
                android:layout_height="0dip"
                android:layout_weight="1"
                android:ellipsize="end"
                android:gravity="center_vertical"
                android:singleLine="true"
                android:text="@string/app_label" />

            <TextView
                android:id="@+id/subtitle"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:singleLine="true"
                android:visibility="gone" />
        </LinearLayout>

        <ProgressBar
            android:id="@+id/progress_circular"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="8dip"
            android:max="10000"
            android:visibility="gone" />

        <ImageView
            android:id="@+id/right_icon"
            android:layout_width="36dip"
            android:layout_height="36dip"
            android:layout_marginRight="8dip"
            android:scaleType="fitCenter"
            android:visibility="gone" />
    </LinearLayout>

    <ListView
        android:id="@+id/menu_list_options"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/title_bar_layout"
        android:descendantFocusability="afterDescendants"
        android:focusable="false"
        android:nextFocusDown="@+id/menu_help_text"
        android:nextFocusUp="@+id/menu_settings"
        android:scrollingCache="true" >
    </ListView>

    <LinearLayout android:id="@+id/menu_help"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:layout_above="@+id/menu_settings"
        android:layout_alignParentBottom="true"
        android:visibility="visible"
        android:addStatesFromChildren="true" >

        <TextView android:id="@+id/menu_help_text"
            android:layout_width="0dp"
            style="@android:style/TextAppearance.Holo.Medium"
            android:text="Help"
            android:textColor="@android:color/white"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:background="@drawable/menu_item_selector"
            android:drawableLeft="@drawable/ic_action_action_help"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:nextFocusUp="@+id/menu_list_options"
            android:clickable="true"
             />
    </LinearLayout>

    <LinearLayout android:id="@+id/menu_settings"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/background_dark"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:layout_alignParentBottom="true"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:visibility="gone" >

        <TextView android:id="@+id/menu_settings_text"
            android:layout_width="0dp"
            style="@android:style/TextAppearance.Holo.Medium"
            android:text="Help"
            android:textColor="@android:color/white"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:background="@drawable/menu_item_selector"
            android:drawableLeft="@drawable/leftnav_bar_option_icon_normal_dark"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:nextFocusUp="@+id/menu_options"
            android:clickable="true"
             />
    </LinearLayout>
</RelativeLayout>

This borrows a few design elements from the Google TV LeftNavBar (mainly the app header, progress items, and the Settings option in the menu). The rest contains a ListView named menu_list_options, which can be used to populate a list of options for the menu. In may case I use a TextView CompoundDrawable to create the options with their icons. You can populate the list however you want, and if you combine MenuDrawer with say SlidingMenu you could implement the multilevel sliding drawer that you see in apps like YouTube.

The results are similar to the following:

screenshot-from-2013-08-25-183736

By tying things further into the Google TV remote’s Menu key, and an on screen Drawer icon that is clickable, you can have the drawer slide out when needed and be hidden when it isn’t. It is important to make sure that users have some sort of visual indicator like standard 3 dots or dashes you see in the ActionBar on most apps.

To tie this into an activity is similar to the following:

protected void createSideMenu() {
   mainContext = this;
   menuDrawer = MenuDrawer.attach(this, MenuDrawer.Type.OVERLAY);
   menuDrawer.setMenuView(R.layout.menu_drawer);
   menuDrawer.setContentView(R.layout.activity_plex_app_main);
   menuDrawer.setDrawerIndicatorEnabled(true);

   View menuButton = findViewById(R.id.menu_button);
   menuButton.setOnClickListener(new MenuDrawerOnClickListener(menuDrawer));
   populateMenuOptions();
}

protected void populateMenuOptions() {
   List<MenuDrawerItem> drawerMenuItem = new ArrayList<MenuDrawerItem>();
   drawerMenuItem.add(new MenuDrawerItemImpl(getResources().getString(R.string.options_main_about), R.drawable.ic_action_action_about));
   drawerMenuItem.add(new MenuDrawerItemImpl(getResources().getString(R.string.options_main_clear_image_cache), R.drawable.ic_action_content_remove));
   drawerMenuItem.add(new MenuDrawerItemImpl(getResources().getString(R.string.tutorial), R.drawable.ic_action_tutorial));
   drawerMenuItem.add(new MenuDrawerItemImpl("Empty Video Queue", R.drawable.ic_action_content_remove));

   ListView listView = (ListView)menuDrawer.getMenuView().findViewById(R.id.menu_list_options);
   hideMenuItems(listView);
   listView.setAdapter(new MenuDrawerAdapter(this, drawerMenuItem));
   listView.setOnItemClickListener(new MainMenuDrawerOnItemClickedListener(menuDrawer, mainGallery));
}

If you want to tie this into the Google TV Menu key, here is a sample:

if (keyCode == KeyEvent.KEYCODE_MENU && !menuDrawer.isMenuVisible()) {
   menuDrawer.toggleMenu();
   return true;
}

if (keyCode == KeyEvent.KEYCODE_BACK && menuDrawer.isMenuVisible()) {
   menuDrawer.toggleMenu();
   return true;
}

In general using MenuDrawer vs SlidingMenu I found the former to be easier to use and worked well with the remote control. I experienced focus issues when trying to use SlidingMenu with a remote control, and not so with MenuDrawer. One thing to note, with MenuDrawer layouts is that items in the drawer even when they are not visible can gain focus with the D-Pad navigation. So make sure you are using the nextFocus* options in your layouts to help control things. Also you may want to hide options like the menu_list_options view when it isn’t actually showing.

So there you have it, a basic navigation drawer which hides when it isn’t needed, and can be made to slide out when it is. With a TV experience, screen space is at a premium, so freeing up much as possible can be a necessary thing at times.
 

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


9 − five =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close