Меню

Меню это распространенный компонент пользовательского интерфейса для множества типов приложений. Чтобы предоставить знакомый и последовательный интерфейс, вы должны использовать класс Menu для организации действий пользователя и прочих опцией приложения.

Начиная с Android 3.0 (API 11), устройства под Android больше не требуют поддержки устаревшей кнопки Меню. В связи с этим изменением, приложения должны уйти от использования традиционной 6-кнопочной панели меню и использовать вместо нее панель инструментов.

Хотя дизайн для некоторых пунктов меню изменился, семантика для определения набора действий и опций по прежнему основана на классе Menu. В данном руководстве мы покажем как создавать три фундаментальных вида меню на всех версиях Android:

Меню опций и панель инструментов

Меню опций это основная коллекция пунктов меню для явления. Это то место, где вы должны размещать глобальные действия для приложения, вроде поиска или настроек.

Если вы разрабатываете приложения для Android 2.3 и ниже, пользователи могут открывать меню опций с помощью кнопки Меню.

На Android 3.0 и выше, пункты меню опций расположены на панели инструментов. Начиная с Android 3.0, кнопка Меню считается устаревшей (и некоторые устройства ее не имеют), поэтому вы должны использовать для этих целей панель инструментов.

Контекстное меню и контекстный режим

Контекстное меню это плавающее меню, которое возникает при долгом нажатии на элемент. Меню содержит действия, которые относятся к изменению выделенного контента или фрейма.

При разработке для Android 3.0 и выше, вместо контекстного меню вы должны использовать контекстный режим для выполнения действий над выделенным элементом. Этот режим отображает пункты меню на панели в верхней части экрана и позволяет пользователям выбрать несколько пунктов.

Всплывающее меню

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

Описание меню в XML

Для всех типов меню Android предоставляет стандартный формат XML для описания пунктов. Вместо создания меню в коде вы должны описывать все пункты меню в файле ресурса меню. Затем вы можете загрузить меню из ресурса в ваше явление или фрагмент.

Использование ресурсов для создания пунктов меню является хорошим подходм по нескольким причинам:

  • Гораздо проще визуализировать структуру меню в XML.
  • Это отделяет содержимое меню от кода, задающего его поведение.
  • Это позволяет создавать альтернативные конфигурации меню для различных версий платформ, размера экранов и других различных конфигураций устройств.

Для описания меню, создайте XML файл в директории res/menu/ и используйте следующие элементы:

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

Пример меню game_menu.xml:

Элемент <item> поддерживает несколько атрибутов, которые вы можете использовать, чтобы определить его внешний вид и поведение:

android:id
Уникальный идентификатор пункта меню, который позволяет узнать какой пункт меню был выбран пользователем.
android:icon
Ссылка на ресурс типа drawable, содержащий иконку пункта меню.
android:title
Ссылка на строковый ресурс, содержащий название пункта меню.
android:showAsAction
Указывает когда и как данный пункт меню должен появляться на панели инструментов.

Это наиболее важные атрибуты, которые вы должны использовать, но существуют и другие, описание которых вы можете найти в разделе Ресурс меню.

Вы можете добавить подменю в любое меню, добавив дочерний элемент <menu> в <item>. Подменю удобный когда приложение содержит много функций, подобно приложениям для PC (файл, редактировать, настройки, и.т.п). Например:

Для использования меню в явлении, необходимо загрузить файл ресурса с помощью метода MenuInflater.inflate(). Далее мы рассмотрим как это сделать для каждого из типов меню.

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

Рисунок 1. Меню опций в браузере Android 2.3

Меню опций это то место, где вы должны размещать глобальные действия для приложения, вроде поиска или настроек.

Положение меню опций на экране зависит от версии Android, для которой вы разрабатываете приложение:

  • При разработке под Android 2.3.x (API 10) и ниже, содержимое меню появляется в нижней части экрана при нажатии аппаратной клавиши меню, как показано на рисунке 1. Сначала показаны первые 6 пунктов меню. Если их более шести, шестой пункт заменяется кнопкой “больше”.
  • При разработке под Android 3.0 (API 11) и выше, пункты меню доступны на панели инструментов. По умолчанию все пункты доступны при нажатии на кнопку меню в правой части панели инструментов или аппаратной клавиши меню (если она есть). Чтобы поместить пункт непосредственно на панель инструментов, вы можете указать для него свойство android:showAsAction="ifRoom", смотрите рисунок 2.

Примечание: даже если вы не пишете приложение для Android 3.0 и выше, вы можете создать приложение с панелью инструментов. Подробнее о поддержке старых версий читайте в разделе Совместимая панель инструментов.

Рисунок 2.Меню на панели инструментов

Вы можете объявить пункты меню опций либо из вашего подкласса Activity, либо из вашего подкласса Fragment. Если и в явлении и во фрагментах существует меню, они отображаются вместе. Пункты меню явления появляются первыми, за ними следуют пункты меню каждого из фрагментов в том же порядке, в каком фрагменты были добавлены в явление. При необходимости вы можете изменить сортировку пунктов меню с помощью атрибута android:orderInCategory.

Для создания меню опций явления переопределите метод onCreateOptionsMenu(). В этом методе вы можете загрузить меню из ресурса:

Вы также можете добавить пункты меню с помощью метода add() и получить, используя метод findItem().

В Android 2.3.x и ниже, система вызывает метод onCreateOptionMenu() при первом открытии меню. В Android 3.0 и выше, метод вызывается при запуске явления.

Обработка событий нажатия

При выборе пункта меню, система вызывает метод onOptionsItemSelected() вашего явления и передает в него выбранный пункт MenuItem. Вы можете определить какой пункт был выбран с помощью метода getItemId(), который возвращает уникальный идентификатор пункта меню (указанный в атрибуте android:id). Вы можете сравнить этот идентификатор с известными пунктами и выполнить нужное действие:

При успешной обработке нажатия, верните true. Если вы не обработали нажатие на пункт меню, вызовите метод супер-класса onOptionsItemSelected() (стандартная реализация возвращает false).

Если ваше явление включает в себя фрагменты, система сначала вызовет метод onOptionsItemSelected() явления, а затем метод каждого из фрагментов, пока один из них не вернет true или пока не будут вызваны все методы.

Совет: Android 3.0 позволяет объявить метод обработки нажатия на пункт меню с помощью XML атрибута android:onClick. В качестве значения должно быть указано название метода, существующего в явлении, использующем ресурс меню. Метод должен быть публичным и принимать единственный параметр типа MenuItem.

Совет:Если приложение содержит много явлений, которым требуются одинаковые пункты меню, подумайте над созданием явления, которое не содержит ничего, кроме методов onCreateOptionsMenu() и onOptionsItemSelected(). После этого создайте классы-наследники для каждого явления, которому требуются данные пункты меню. Затем, чтобы добавить пункты меню в явления-наследники, переопределите метод onCreateOptionsMenu() и вызовите в нем метод супер-класса super.onCreateOptionsMenu(menu), после чего с помощью метода menu.add() добавьте недостающие пункты. Вы можете также переопределить поведение супер-класса для отдельных пунктов меню.

Изменение пунктов меню во время выполнения программы

После вызова метода onCreateOptionsMenu() создается объект типа Menu и данный метод больше не вызывается в будущем, если только меню не становится недействительным по некоторым причинам. Однако вы должны использовать метод onCreateOptionsMenu() только для первоначального создания меню, а не для совершения изменений в течение жизненного цикла явления.

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

В Android 2.3.x и ниже система вызывает метод onPrepareOptionsMenu() каждый раз при открытии меню опций (при нажатии аппаратной кнопки меню).

В Android 3.0 и выше, меню считается постоянно открытым, если его пункты представлены на панели инструментов. Вы должны вызывать метод invalidateOptionsMenu(), когда происходит нужное событие, чтобы попросить систему вызвать метод onPrepareOptionsMenu().

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

Создание контекстных меню

Рисунок 3.Плавающее контекстное меню слева и контекстная панель инструментов справа.

Контекстные меню служат для выбора действий, которые относятся к конкретному элементу или фрейму пользовательского интерфейса. Вы можете создать контекстное меню для любого визуального элемента, но наиболее часто оно используется для ListView, GridView и прочих элементах, в которых пользователь может выполнить собственное действие над каждым из пунктов.

Есть два способа использования контекстных действий:

  • Плавающее меню. Меню, которое появляется в виде плавающего списка когда пользователь удерживает элемент. В текущий момент времени пользователь может выполнить действие с одним элементом.
  • Контекстный режим. Это режим, в котором система отображает контекстую панель инструментов в верхней части экрана, элементы которой позволяют воздействовать на выбранный элементы (или элементы). В этом режиме пользователь может выполнить действие над несколькими элементами (если ваше приложение позволяет это сделать).

Примечание:контекстный режим доступен начиная с Android 3.0 (API 11) и является предпочтительной техникой для отображения контекстных действий. Если ваше приложение поддерживает Android ниже 3.0, вы должны использовать плавающее меню на таких устройствах.

Создание плавающего контекстного меню

Чтобы создать плавающее контекстное меню:

  1. Зарегистрируйте компонент View, для которого создается меню с помощью метода registerForContextMenu(), передав в него этот компонент.

    Если ваше явление использует ListView или GridView, и вы хотите создать контекстное меню для каждого пункта, зарегистрируйте все пункты, передав ListView или GridView в метод registerForContextMenu()

  2. Реализуйте метод onCreateContextMenu() в вашем явлении или фрагменте.

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

    MenuInflater позволяет создать контекстное меню из файла ресурса меню. Параметры метода включают в себя View, который выбрал пользователь и объект ContextMenu.ContextMenuInfo, который содержит дополнительную информацию о выбранном элементе. Если ваше явление имеет несколько визуальных компонентов, каждый из которых имеет свое контекстное меню, вы можете использовать этот параметр, чтобы определить какое контекстное меню создавать в текущий момент.

  3. Реализуйте метод onContextItemSelected().

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

    Метод getItemId() запрашивает идентификатор выбранного пункта меню, который вы должны задать для каждого пункта в XML файле, используя атрибут android:id.

    Верните true после окончания обработки события. Если вы не обработали выбор пункта меню, вызовите метод супер-класса. Если ваше явление включает фрагменты, сначала вызывается метод явления. Если супер-класс не обработал событие, система передает его в методы каждого фрагмента до тех пора, пока один из них не вернет true или false. Стандартная реализация метода класса Activity возвращает false.

Использование контекстного режима

Контекстуный режим это системная реализация ActionMode, которая фокусирует взаимодействие пользователя а выполнении контекстных действий. Когда пользователь включает этот режим, выбрав элемент, контекстная панель инструментов появляется в верхней части экрана и отображает действия, которые пользователь может совершить с выбранным элементом (или элементами). Пока режим включен, пользователь может выбрать несколько элементов (если вы позволяете ему это делать), отменить выбор элемента и продолжить навигацию в явлении. Когда пользователь отменяет выбор последнего элемента, нажимает кнопку назад или нажимает кнопку Отменить в левой части панели, режим выключается и контекстная панель инструментов исчезает.

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

При разработке под Android 3.0 (API 11) и выше, вам лучше использовать контекстный режим для контекстных действий вместо плавающего контекстного меню.

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

  • Длительное нажатие на элемент
  • Установка флажков или похожих элементов

Как ваше приложение вызывает контекстный режим и что происходит при выборе действия, зависит от его дизайна. Существует две основные формы дизайна:

  • Для контекстных действий произвольных одиночных элементов.
  • Для контекстных действий над группой элементов ListView или GridView.

Включение контекстного режима для одного элемента

Чтобы включить контекстный режим только для одного выбранного элемента, необходимо:

  1. Реализовать интерфейс ActionMode.Callback. В методах данного интерфейса вы можете обработать события нажатия на кнопки панели инструментов и прочие события жизненного цикла для контекстного режима.

  2. Вызвать метод startActionMode() в тот момент, когда вам нужно отобразить контекстную панель.

Например:

  1. Реализуем интерфейс ActionMode.Callback.

    Помните, что методы обратного вызова почти такие же, как и для меню опций, за исключением того, что каждый из них принимает в качестве параметра объект ActionMode, ассоциированный с событием. Вы можете использовать ActionMode чтобы вносить изменения, вроде заголовка, или подзаголовка, используя методы setTitle() и setSubTitle(). Это удобно, чтобы показать сколько пунктов выделено.

    Обратите внимание также на то, что в примере выше mActionMode устанавливается в null при выходе из режима. В следующем шаге вы увидите чем может быть удобна инициализация и сохранение переменной в явлении или фрагменте.

  2. Вызовите метод startActionMode(), чтобы включить контекстный режим:

    Когда вы вызываете startActionMode(), система возвращает объект типа ActionMode. При сохранении объекта в переменную вы можете менять контекстную панель в ответ на события. В примере выше объект ActionMode используется, чтобы гарантировать, что экземпляр ActionMode не пересоздается, если он уже активен.

Пакетное включение контекстного режима в ListView и GridView

Если у вас есть коллекция элементов в ListView или GridView (или любая другая, которая расширяет AbsListView) и вы хотите позволить пользователям выполнять действия над группой элементов, необходимо:

  • Реализовать интерфейс AbsListView.MultiChoiceModeListener и установить его для группы элементов с помощью метода setMultiChoiceModeListener(). В методах данного слушателя вы можете создать действия для контекстной панели инструментов, обработать событие нажатия и прочие события, которые наследуются из интерфейса ActionMode.Callback.
  • Вызвать метод setChoiceMode() с аргументом CHOICE_MODE_MULTIPLE_MODAL.

Например:

Вот и все. Теперь когда пользователь длительно удерживает элемент, система вызывает метод onCreateActionMode() и отображает контекстную панель инструментов с соответствующими действиями. Пока контекстная панель отображается, пользователь может выбрать дополнительные элементы.

В некоторых случаях, когда контекстная панель предлагает общие действия, вы можете использовать флажки или похожие элементы, чтобы позволить пользователю выбирать элементы, поскольку они могут не обнаружить реакцию на длительное нажатие. Когда пользователь устанавливает флажки, вы можете включать контекстный режим.

Создание всплывающего меню

Рисунок 4. Всплывающее меню в приложении Gmail.

Всплывающее меню (PopupMenu) это модальное окно, привязанное к визуальному элементу (View). Оно появляется внизу элемента, в которому оно привязано, если есть свободное место и сверху в противном случае. Такое меню удобно в следующих случаях:

  • Создание скрытого меню для действий, относящихся к конкретному содержанию (например заголовок письма, см. рисунок 4.)

    Примечание:это не то же самое, что контекстное меню, которое служит для действий, изменяющих содержимое выбранного элемента.

  • Отображения второй части действия (например при нажатии кнопки “Добавить” пользователь может указать, что конкретно требуется добавить).
  • Создание выпадающего списка, подобно элементу Spinner, для которого не требуется сохранять выбор.

Примечание: класс PopupMenu доступен начиная с API 11 и выше.

После создания XML файла меню нужно проделать следующее:

  1. Создать экземпляр класса PopupMenu с помощью конструктор и передать в него текущий Context приложения и компонент View, к которому данное меню будет привязано.
  2. Воспользоваться объектом MenuInflater для загрузки меню из ресурса в объект Menu, возвращаемый методом PopupMenu.getMenu(). В API 14 и выше вы можете просто использовать метод PopupMenu.inflate().
  3. Вызвать метод PopupMenu.show().

Например, создадим кнопку с атрибутом android:onClick, которая открывает всплывающее меню:

Для отображения меню в явлении можно написать что-то подобное:

Меню удаляется когда пользователь выбирает пункт меню или нажимает за его границами. Вы можете отслеживать событие удаления меню, используя PopupMenu.OnDismissListener.

Обработка событий нажатия

Для выполнения действий при выборе пункта меню вы должны реализовать интерфейс PopupMenu.OnMenuItemClickListener и зарегистрировать его в объекте PopupMenu с помощью метода setOnMenuItemclickListener(). Когда пользователь выбирает пункт, система вызывает метод обратного вызова onMenuItemClick() данного интерфейса.

Например:

Создание группы меню

Группа меню это коллекция пунктов меню, имеющих схожие черты. С помощью групп вы можете:

  • Показать или спрятать все пункты с помощью метода setGroupVisible().
  • Разрешить или запретить все пункты с помощью метода setGroupEnabled().
  • Указать должны ли пункты быть помечаемым с помощью метода setGroupCheckable().

Вы можете создать группу, вложив элементы <item> внутрь элемента <group> в файле ресурса или передав ID группы в метод add().

Пример создания группы с помощью ресурсов:

Элементы группы отображаются на одном уровне с первым пункт – все три пункта соседи. Однако, вы можете изменить черты двух элементов в группе, ссылаясь на идентификатор группы и используя методы, перечисленные выше. Система никогда не разделяет элементы группы. Например, если вы указали атрибут android:showAsAction="ifRoom" для каждого элемента группы, они или будут оба отображены на панели, или оба спрятаны в расширенном меню.

Использование помечаемых пунктов меню

Рисунок 5.Подменю с помечаемыми пунктами.

Меню можно удобно использовать для включаемых опций (включено-выключено), используя флажки или радио-кнопки. На рисунке 5 показано подменю с радио-кнопками, в котором можно пометить один и з пунктов.

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

Вы можете использовать возможность разрешить пометить отдельные пункты меню, используя атрибут android:checkable для элемента <item> или android:checkableBehavior для элемента <group>. Например, все пункты следующего меню являются помечаемыми с помощью радио-кнопок:

Атрибут android:checkableBehavior может принимать следующие значения:

single
Только один пункт группы может быть помечен (радио-кнопки)
all
Все пункты могут быть помечены (флажки)
none
Пункты не могут быть помечены

Вы можете установить состояние пометки по умолчанию для пункта, используя атрибут android:checked и изменять это состояние в коде с помощью метода setChecked().

Когда помечаемый пункт меню выбирается, система вызывает соответствующий метод (вроде onOptionsItemSelected()). В этом методе вы можете установить состояние флажка, поскольку флажок или радио-кнопка не меняют свое состояние автоматически. Вы можете узнать текущее состояние с помощью метода isChecked() и устанавливать новое с помощью метода setChecked(). Например:

Если вы не устанавливаете состояние этим способом при выборе пункта, визуально для пользователя пункт меню не изменится. Когда вы устанавливаете состояние, явление сохраняет состояние пункта и при повторном открытии меню пользователь это состояние увидит.

Примечание: помечаемые пункты меню предназначены для использования только для работы в пределах сеанса и не сохраняют свое состояние при завершении приложения. Если ваше приложение имеет настройки, которые вы хотите сохранять для пользователя, вы должны использовать для этого SharedPreferenced.

Добавление пунктов меню на основе намерения

Иногда требуется создать пункт меню для запуска явления с помощью намерений (Intent) (неважно явление вашего или стороннего приложения). Когда вы знаете какое намерение использовать и у вас есть для этого пункт меню, вы должны просто создать намерение и выполнить его с помощью метода startActivity() в каком-либо подходящем методе обратного вызова при выборе пункта меню (например в методе onOptionsItemSelected()).

Однако, если вы не уверены, что устройство пользователя содержит нужное приложение, которое обработает намерение, вы должны добавлять пункт в меню только убедившись, что приложение существует. Чтобы решить эту проблему, Android позволяет динамически добавлять пункты меню при обнаружении явлений, которые могут обработать ваше намерение.

Итак, чтобы добавить пункт меню, основываясь на доступности явлений, необходимо сделать следующее:

  1. Объявить намерение с категорией CATEGORY_ALTERNATIVE и/или CATEGORY_SELECTED_ALTERNATIVE, а так же другими требованиями.
  2. Вызвать метод Menu.addIntentOptions(). После этого Android произведет поиск приложений, которые могут обработать намерение и добавит их в ваше меню.

Если приложения, удовлетворяющие намерению не установлены, в меню не будет добавлено ничего.

Примечание: CATEGORY_SELECTED_ALTERNATIVE используется для обработки текущего выделенного элемента на экране и должно использовать только при создании меню в методе onCreateContextMenu().

Например:

Для каждого найденного явления, у которого соответствуют фильтры намерений, будет добавлен свой пункт меню, название которого будет взято из значения атрибута android:label фильтра намерений, а в качестве иконки будет использовать иконка соответствующего приложения. Метод addIntentOptions() возвращает количество добавленных пунктов меню.

Примечание: когда вы вызываете метод addIntentOptions(), это переопределяет все пункты меню группы, указанной первым аргументом.

Разрешаем вашему явлению быть добавленным в меню другого приложения

Вы также можете позволить добавлять ваше явление в меню сторонних приложений. Для этого необходимо добавить фильтр намерений с категорией CATEGORY_ALTERNATIVE и/или CATEGORY_SELECTED_ALTERNATIVE. Например:

Подобности о фильтрах намерений читайте в разделе Намерения и фильтры намерений.

В качестве примера использования меню смотрите код демонстрационного приложения Note Pad.

Добавить комментарий