Статьи

Создание скользящего навигационного меню Google TV

Стандартным шаблоном дизайна для приложений Google TV обычно является использование библиотеки LeftNavBar. К сожалению, нет apklib, и он не включен в качестве библиотеки надстроек Google TV, поэтому нужно скомпилировать и работать с самой библиотекой. Библиотека LeftNavBar — это в основном панель действий, которая переворачивается вертикально.

Проблема, с которой я столкнулся, заключается в том, что панель всегда видна на экране, и она не реализует шаблон «Расположение ящика», который позволяет меню полностью скользить. Он позволяет отображать значки, которые доступны, и это всегда виден на экране. Итак, я создал альтернативу, которая будет использоваться с Serenity, начиная с 1.4.0. При этом используется библиотека MenuDrawer и следующий пользовательский макет.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<?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>

Это заимствует несколько элементов дизайна из Google TV LeftNavBar (главным образом, заголовок приложения, элементы прогресса и параметр «Настройки» в меню). Остальная часть содержит ListView с именем menu_list_options, который можно использовать для заполнения списка опций меню. В случае необходимости я использую TextView CompoundDrawable для создания параметров с их значками. Вы можете заполнить список любым способом , и если вы объедините MenuDrawer с скажем SlidingMenu, вы можете реализовать многоуровневый выдвижной ящик, который вы видите в приложениях, таких как YouTube.

Результаты похожи на следующее:

Скриншот-от-2013-08-25-183736

Привязав все дальше к клавише «Меню» пульта дистанционного управления Google TV и к экранному значку «Ящик», который можно нажимать, вы можете выдвинуть ящик при необходимости и скрыть его, когда это не так. Важно убедиться, что у пользователей есть какой-то визуальный индикатор, например стандартные 3 точки или тире, которые вы видите на панели действий в большинстве приложений.

Чтобы связать это в деятельности, похоже на следующее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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));
}

Если вы хотите связать это с ключом меню Google TV, вот пример:

1
2
3
4
5
6
7
8
9
if (keyCode == KeyEvent.KEYCODE_MENU && !menuDrawer.isMenuVisible()) {
   menuDrawer.toggleMenu();
   return true;
}
 
if (keyCode == KeyEvent.KEYCODE_BACK && menuDrawer.isMenuVisible()) {
   menuDrawer.toggleMenu();
   return true;
}

В целом, используя MenuDrawer и SlidingMenu, я обнаружил, что первое проще в использовании и хорошо работает с пультом дистанционного управления. У меня возникли проблемы с фокусировкой при попытке использовать SlidingMenu с пультом, а с MenuDrawer — нет Следует отметить, что при использовании макетов MenuDrawer элементы в ящике, даже если они не видны, могут получить фокус с помощью навигации D-Pad. Поэтому убедитесь, что вы используете опции nextFocus * в своих макетах, чтобы помочь контролировать вещи. Также вы можете скрыть такие параметры, как представление menu_list_options, когда оно фактически не отображается.

Итак, у вас есть базовая панель навигации, которая скрывается, когда она не нужна, и может быть сделана, чтобы выдвигаться, когда она есть. При просмотре телевизора пространство на экране оказывается на первом месте, поэтому максимально возможное освобождение иногда может быть необходимым.