Меню

Программное создание меню
Создание ресурса для меню

Android поддерживает три типа меню. Первый - на телефоне есть отдельная кнопка Menu, нажатие которой вызывает меню. Второй тип - контекстное меню, которое появляется при нажатии и удерживания пальца на экране (также можно нажать и удерживать центральную кнопку на телефоне). Контекстное меню в свою очередь может иметь подменю. Сегодня мы познакомимся с первым типом меню.

Меню выбора опций (Options Menu)

При нажатии клавиши Menu появляется набор пунктов меню, прикрепляемый к Активности. Меню может содержать значки. Такое меню может содержать максимум шесть пунктов. При наличии более шести пунктов используется расширенное меню - в этом случае вместо шестого пункта появляется пункт Опции (More). При нажатии данного пункта показывается расширенное меню со списком пунктов, которые не поместились в основной части меню выбора опций.

Когда меню открывается впервые, Android вызывает метод onCreateOptionsMenu(), передавая в качестве параметра объект Menu. Меню можно создавать в виде ресурсов в XML-файле или использовать метод add().

Программное создание меню

Расссмотрим программное создание меню для полноты картины, так как рекомендуется использовать второй способ через ресурсы. Нам понадобится определить несколько констант для пунктов меню:


// идентификаторы для пунктов меню
private static final int IDM_OPEN = 101;
private static final int IDM_SAVE = 102;

public boolean onCreateOptionsMenu(Menu menu)
{
    // добавляем пункты меню
	menu.add(Menu.NONE, IDM_OPEN, Menu.NONE, "Открыть");
	menu.add(Menu.NONE, IDM_SAVE, Menu.NONE, "Сохранить");
}

У метода add() есть четыре параметра:

  • идентификатор группы - позволяет связывать пункт меню с группой других пунктов этого меню
  • идентификатор пункта для обработчика события выбора пункта меню
  • порядок расположения пункта в меню - позволяет определять позицию в меню. По умолчанию (Menu.NONE или 0) пункты идут в том порядке, как задано в коде
  • заголовок - текст, который выводится в пункте меню. Можно использовать строковый ресурс

Метод возвращает объект MenuItem, который можно использовать для установки дополнительных свойств, например, для установить значок, горячую клавишу и т.д.

Если вы хотите создать меню со значками, то воспользуйтесь методом setIcon() при создании меню.


menu.add(Menu.NONE), IDM_OPEN, Menu.NONE, "Открыть")
    .setIcon(R.drawable.icon_menu_open);

Напомним еще раз, что значки можно добавить только к шести пунктам меню (или к пяти, если пунктов больше шести).

Метод onCreateOptionsMenu вызывается системой только один раз при создании меню. Если вам требуется обновить меню во время работы программы, то используйте метод обратного вызова onPrepareOptionsMenu().

При выборе пункта меню вызывается метод onOptionsItemSelected, который передает объект MenuItem - пункт меню, выбранный пользователем. При помощи метода getItemId можно получить идентификатор выбранного пункта меню. После идентфикации пункта меню можно написать код для обработки события выбора меню:


public boolean onOptionsItemSelected(MenuItem item)
{
    switсh (item.getItemId())
	case IDM_OPEN:
	    return true;
	case IDM_SAVE:
	    return true;
		
	return false;	
}

Горячие клавиши

Также можно задавать горячие клавиши для быстрого доступа, используя символы клавиатуры, при помощи нескольких методов:

  • setAlphabeticShortcut(char) - добавляет символ
  • setNumericShortcut(int) - добавляет число
  • setShortcut(char, int) - добавляет комбинацию символа и числа

Например, если задать горячую клавишу setAlphabeticShortcut('q');, то при открытии меню (или при удерживании клавиши MENU) нажатие клавиши Q выберет данный пункт меню. Эта горячая клавиша (или сочетание клавиш) будет показана как подсказка, отображающая ниже имени пункта меню.

Создание подменю

Подменю можно добавить в любое меню, кроме другого подменю. Подменю создается в методе обратного вызова onCreateOptionsMenu() с помощью метода addSubMenu(), который возвращает объект SubMenu. В объект SubMenu можно добавить дополнительные пункты к этому меню, используя метод add(). Например:


public static final int IDM_HELP = 101; 
public static final int IDM_NEW = 201; 
public static final int IDM_OPEN = 202; 
public static final int IDM_SAVE = 203; 
public static final int IDM_CUT = 301; 
public static final int IDM_COPY = 302; 
public static final int IDM_PASTE = 303; 

@Override 
public boolean onCreateOptionsMenu(Menu menu) 
{
	SubMenu subMenuFile = menu.addSubMenu("Файл");
	subMenuFile.add(Menu.NONE, IDM_NEW, Menu.NONE, "Новый");
	subMenuFile.add(Menu.NONE, IDM_OPEN, Menu.NONE, "Открыть");
	subMenuFile.add(Menu.NONE, IDM_SAVE, Menu.NONE, "Сохранить");
	SubMenu subMenuEdit = menu.addSubMenu("Правка");
	subMenuEdit.add(Menu.NONE, IDM_CUT, Menu.NONE, "Вырезать");
	subMenuEdit.add(Menu.NONE, IDM_COPY, Menu.NONE, "Копировать");
	subMenuEdit.add(Menu.NONE, IDM_PASTE, Menu.NONE, "Вставить");
	menu.add(Menu.NONE, IDM_HELP, Menu.NONE, "Справка");
	return super.onCreateOptionsMenu(menu); 
}

@Override 
public boolean onOptionsItemSelected(MenuItem item) 
{
	CharSequence message;

	switch (item.getItemId()) {
	case IDM_NEW:
		message = "Выбран пункт Новый";
		break;
	case IDM_OPEN:
		message = "Выбран пункт Открыть";
		break;
	case IDM_SAVE:
		message = "Выбран пункт Сохранить";
		break;
	case IDM_CUT:
		message = "Выбран пункт Вырезать";
		break;
	case IDM_COPY:
		message = "Выбран пункт Копировать";
		break;
	case IDM_PASTE:
		message = "Выбран пункт Вставить";
		break;
	case IDM_HELP:
		message = "Выбран пункт Справка";
		break;
	default:
		return false;
	}
	// выводим уведомление о выбранном пункте меню
	Toast toast = Toast.makeText(this, message, Toast.LENGTH_LONG);

	toast.setGravity(Gravity.CENTER, 0, 0);
	toast.show();
	return true;
}

Теперь при выборе пункта меню появится еще одно окно с подменю. Попробуйте сами.

Добавление флажков и переключателей

В пункты меню возможно добавление флажков или переключателей. Чтобы добавить флажок или переключатель для отдельного элемента меню, необходимо использовать метод setCheckable():


MenuItem item = menu.add(0, IDM_FORMAT_BOLD, 0, "Bold");
item.setCheckable(true); 

Если есть необходимость добавить несколько пунктов меню с флажками или переключателями, то можно объединить их в группы меню, создав отдельный идентификатор. Пункт меню добавляется в группу через метод add(), передав ему в качестве первого параметра идентификатор группы меню. Допустим, мы объявили идентификаторы для группы меню Цвет и элементов меню для установки цвета:


public static final int IDM_COLOR_GROUP = 400; 
public static final int IDM_COLOR_RED = 401; 
public static final int IDM_COLOR_GREEN = 402; 
public static final int IDM_COLOR_BLUE = 403; 

Теперь для создания группы меню с флажками нужно назначить идентификатор группы на каждый пункт меню и вызвать метод setGroupCheckable() для всей группы (этом случае нет необходимости вызывать метод setCheckable() для каждого пункта меню):


SubMenu subMenuColor = menu.addSubMenu("Цвет"); 
subMenuColor.add(IDM_COLOR_GROUP, IDM_COLOR_RED, Menu.NONE, "Красный"); 
subMenuColor.add(IDM_COLOR_GROUP, IDM_COLOR_GREEN, Menu.NONE,"Зеленый"); 
subMenuColor.add(IDM_COLOR_GROUP, IDM_COLOR_BLUE, Menu.NONE, "Синий"); 
subMenuColor.setGroupCheckable(IDM_COLOR_GROUP, true, false); 

У метода setGroupCheckable() три параметра:

  • первый параметр — идентификатор группы меню;
  • второй параметр — true, если в группе разрешены переключатели или флажки;
  • третий параметр — устанавливает единственный (true) или множественный (false) выбор пунктов меню. Этот параметр фактически определяет внешний вид меню — это будет меню с переключателями или флажками.

    Для управления состоянием флажков и переключателей в обработчике события выбора пункта меню нужно написать следующее:

    
    @Override 
    public boolean onOptionsItemSelected(MenuItem item) 
    {
    	CharSequence message;
    
    	switch (item.getItemId()) {
        ...
    	case IDM_COLOR_RED: 
    		// инвертируем состояние флажка 
    		item.setChecked(!item.isChecked()); 
    		message = "Красный цвет";
    		break;
    
    	default:
    		return false;
    }
    

    Запустите проект, вызовите меню и выберите пункт меню Цвет. У вас появится подменю с тремя пунктами (Красный, Зеленый, Синий) в виде флажков. Состояние флажков и переключателей обрабатывается в коде программы и сохраняется при повторных вызовах меню.

    Создание ресурса для меню

    Рассмотрим работу с меню рекомендованным способом через ресурсы. Для создания меню используются ресурсы, которые должны храниться в XML-файле. Сам файл должен находиться в папке res/menu/ вашего проекта. Меню состоит из следующих элементов:

    <menu>
    Определяет меню, который будет содержать пункты меню. Элемент <menu> должен быть корневым элементом в XML-структуре файла и может содержать один или несколько элементов <item> и <group>
    <item>
    Создает непосредственно пункты меню. Данный элемент может иметь вложенный элемент <menu> для создания подменю
    <group>
    При желании можете также использовать невидимый контейнер для элементов <item>. Это позволяет достичь некоторых эффектов

    Предположим, мы решили использовать меню для какой-нибудь игры. Создадим новый файл game_menu.xml:

    
    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+id/new_game"
              android:title="@string/new_game" />
        <item android:id="@+id/help"
              android:title="@string/help" />
    </menu>
    

    Мы создали меню с двумя пунктами. Каждый пункт включает в себя следующие атрибуты:

    android:id
    Идентификатор пункта меню, по которому приложение может распознать при выделении пункта меню пользователем
    android:title
    Текст, который будет выводиться в меню

    Существуют и другие атрибуты для элемента item, например android:icon="@drawable/home" позволит также вывести значок для пункта меню. Дополнительную информацию можно узнать из документации Menu Resource.

    Кстати, вы можете использовать встроенные системныее значки Android. Например, android:icon="@android:drawable/ic_menu_help" позволит вам вывести значок помощи, который зашит в систему. Подробнее о системных значках почитайте в статье Системные графические ресурсы

    При создании меню мы указали на строковые ресурсы @string/new_game и @string/help. Необходимо добавить новые строки в файле strings.xml:

    
    <string name="new_game">Новая игра</string>
    <string name="help">Справка</string>
    

    Теперь нужно внести изменения в основном классе. Программа должна сконвертировать созданный нами ресурс меню в программный объект. Для этой цели существует специальный метод MenuInflater.inflate(), который вызывается в специальном методе обратного вызова onCreateOptionsMenu(). Данный метод и предназначен для вывода меню при нажатии кнопки MENU на устройстве:

    Выберите в меню Source | Override/Implement method | поставьте флажок у onCreateOptionsMenu и вставьте в заготовку код:

    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
    {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.game_menu, menu);
        return true;
    }
    

    После вставки кода среда разработки попросит импортировать недостающие пространства имен.

    
    import android.view.Menu;
    import android.view.MenuInflater;
    

    Запустив программу, нажмите кнопку MENU на эмуляторе, чтобы увидеть созданное меню.

    Создание меню для Android

    Метод getMenuInflater() возвращает экземпляр класса MenuInflater, который мы используем для чтения данных меню из XML.

    Как видите, меню появляется в нижней части экрана. Всего можно одновременно вывести на экран шесть пунктов меню. Если пунктов больше, то будет выведено пять пунктов плюс шестой пункт More, который позволит увидеть остальные пункты. Давайте проверим и добавим новые пункты меню.

    Сначала добавим шесть пунктов.

    Добавим еще один пункт к меню, чтобы их стало семь.

    На всякий случай скажу, что в Android 3.0, предназначенной для планшетов, немного изменились правила вывода меню. Данная информация выходит за рамки наших уроков. Читайте документацию.

    Выбор пунктов меню

    Мы научились создавать меню. Но пока оно бесполезно, так как пункты меню никак не реагируют на наши нажатия. Для обработки нажатий пунктов меню служит метод onOptionsItemSelected(). Метод распознает пункт, выбранный пользователем, через MenuItem. Мы можем теперь определить выбранный пункт через вызов getItemId(), который возвращает идентификатор пункта меню. Далее через оператор switch нам остается определить нужные команды:

    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
    {
        // Операции для выбранного пункта меню
        switch (item.getItemId()) 
    	{
        case R.id.new_game:
            newGame();
            return true;
        case R.id.help:
            showHelp();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }
    
    public void newGame()
    {
    
    	edtext.setText("Выбран пункт Новая игра");
    }
    
    public void showHelp()
    {
    	edtext.setText("Выбран пункт Справка");
    }
    

    Запустите приложение, вызовите меню и выберите первый или второй пункт меню. В текстовом поле должно появиться сообщение.

    В приведенном примере getItemId() запрашивает ID для выбранного пункта меню и начинает сравнивать через оператор выбора switch с идентификаторами, которые мы задали в XML-ресурсах. При обнаружении нужного идентификатора выполняется обработчик для заданного пункта меню. Если программа ничего не обнаружит, то выполняется оператор default, который возвращает super class.

    В Android 3.0 можно добавить атрибут android:onClick в ресурсах меню, и вам уже не нужно использовать onOptionsItemSelected(). При помощи android:onClick вы можете указать нужный метод при выборе пункта меню.