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.
 

Related Whitepaper:

Rapid Android Development: Build Rich, Sensor-Based Applications with Processing

Create mobile apps for Android phones and tablets faster and more easily than you ever imagined

Use 'Processing', the free, award-winning, graphics-savvy language and development environment, to work with the touchscreens, hardware sensors, cameras, network transceivers, and other devices and software in the latest Android phones and tablets.

Get it Now!  

Leave a Reply


× six = 54



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
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.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books