Меню
Меню в приложениях представляет класс android.view.Menu , и каждая activity ассоциируется с объектом этого типа. Объект android.view.Menu может включать различное количество элементов, а те в свою очередь могут хранить подэлементы.
Определение меню в xml
Меню, как и файлы интерфейса или изображений, также представляет собой ресурс. Однако при создании нового проекта с Empty Activity по умолчанию нет никаких ресурсов меню, поэтому при необходимости их нужно добавлять вручную. Так, для определения ресурсов меню в проекте нажмем правой кнопкой мыши в проекте на каталог res и далее в открывшемся списоке выберем пункт New -> Android Resource File :

Далее в появившемся окне укажем для имени файла название main_menu , а для поля Resource Type (тип ресурса) выберем Menu :

После этого в каталоге res будет создан подкаталог menu, в котором будет находиться файл main_menu.xml .

По умолчанию этот файл определяет один пустой элемент menu:
Изменим содержимое файла, определив несколько пунктов:
Тег является корневым узлом файла и определяет меню, состоящее из одного или нескольких элементов и .
Элемент представляет объект MenuItem, которой является одним из элементов меню. Этот элемент может содержать внутренний подэлемент , с помощью которого создается подменю.
Элемент включает следующие атрибуты, которые определяют его внешний вид и поведение:
- android:id : уникальный id элемента меню, который позволяет его опознать при выборе пользователем и найти через поиск ресурса по id
- android:icon : ссылка на ресурс drawable, который задает изображение для элемента ( android:icon=»@drawable/ic_help» )
- android:title : ссылка на ресурс строки, содержащий заголовок элемента. По умолчанию имеет значение «Settings»
- android:orderInCategory : порядок следования элемента в меню
Наполнение меню элементами
Мы определили меню с тремя элементами, но само определение элементов в файле еще не создает меню. Это всего лишь декларативное описание. Чтобы вывести его на экран, нам надо использовать его в классе Activity. Для этого надо переопределить метод onCreateOptionsMenu . Итак, перейдем к классу MainActivity и изменим его следующим образом:
package com.example.menuapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.Menu; public class MainActivity extends AppCompatActivity < @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); >@Override public boolean onCreateOptionsMenu(Menu menu) < getMenuInflater().inflate(R.menu.main_menu, menu); return true; >>
Метод getMenuInflater возвращает объект MenuInflater , у которого вызывается метод inflate() . Этот метод в качестве первого параметра принимает ресурс, представляющий наше декларативное описание меню в xml, и наполняет им объект menu, переданный в качестве второго параметра.
Запустим приложение по умолчанию и нажмем на кнопку меню в правом верхнем углу:

Обработка нажатий в меню
Если мы нажмем на любой из пунктов меню, то ничего не произойдет. Чтобы привязать к меню действия, нам надо переопределить в классе activity onOptionsItemSelected .
Для вывода выбранного элемена меню в файле activity_main.xml определим текстовое поле с >
И изменим класс MainActivity:
package com.example.menuapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class MainActivity extends AppCompatActivity < @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); >@Override public boolean onCreateOptionsMenu(Menu menu) < getMenuInflater().inflate(R.menu.main_menu, menu); return true; >@Override public boolean onOptionsItemSelected(MenuItem item) < int TextView headerView = findViewById(R.id.selectedMenuItem); switch(id)< case R.id.action_settings : headerView.setText("Настройки"); return true; case R.id.open_settings: headerView.setText("Открыть"); return true; case R.id.save_settings: headerView.setText("Сохранить"); return true; >//headerView.setText(item.getTitle()); return super.onOptionsItemSelected(item); > >
Чтобы понять, какой пункт меню выбран, вначале получаем его идентификатор int >. Затем пробегаемся в конструкции switch..case и выбираем нужный вариант и в зависимости от выбора производим определенные действия — в данном случае устанавливаем текст TextView.

Стоит отметить, что в данном случае, если наша задача заключалась, чтобы просто в выводе текста выбранного пункта меню, то мы вместо конструкции switch просто могли написать так:
headerView.setText(item.getTitle());
Программное создание меню
Кроме определения элементов меню в xml, можно также создать меню программным способом. Для добавления новых пунктов меню используется метод add() класса Menu .
Например, изменим код MainActivity :
package com.example.menuapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class MainActivity extends AppCompatActivity < @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); >@Override public boolean onCreateOptionsMenu(Menu menu) < super.onCreateOptionsMenu(menu); menu.add("Настройки"); menu.add("Открыть"); menu.add("Сохранить"); return true; >@Override public boolean onOptionsItemSelected(MenuItem item) < String title = item.getTitle().toString(); TextView headerView = findViewById(R.id.selectedMenuItem); headerView.setText(title); return super.onOptionsItemSelected(item); >>
Использованная версия метода add() принимает заголовок для пункта меню.
Как сделать Navigation Drawer в Android
Navigation Drawer (навигационная секция) – боковая панель, которая выводит на экран основные навигационные опции приложения в левой части экрана. Она скрыта большую часть времени, и отображается по свайпу от левого края экрана или нажатию значка приложения в ActionBar.
Этот урок описывает, как реализовать Navigation Drawer, используя API-интерфейсы, доступные в библиотеке поддержки android.support.v4.
Полный код приложения с Navigation Drawer можно скачать по ссылке внизу страницы.
Navigation Drawer Design
Прежде чем вы решите использовать Navigation Drawer в вашем приложении, вы должны ознакомиться с примерами использования и принципами проектирования, определенными в Navigation Drawer: руководство по проектированию.
Смотрите видео, как использовать готовый шаблон Navigation Drawer Activity в Android Studio – структура шаблона, пример работы в приложении.
Создать макет Navigation Drawer
Чтобы добавить Navigation Drawer, объявите пользовательский интерфейс с DrawerLayout объектом в качестве корневого View вашего макета. Внутри , добавьте View, который содержит основной контент для экрана (основной макет, когда панель навигации скрыта) и еще один View, который содержит содержимое Navigation Drawer.
Ниже приведен код макета, который использует DrawerLayout с двумя дочерними View: FrameLayout для основного содержания ( Фрагмент , создаваемый динамически во время работы программы), и ListView для боковой панели навигации.
Этот макет имеет некоторые важные особенности:
Основной View с контентом (в FrameLayout выше) должен быть первым дочерним элементом в DrawerLayout, для того, чтобы панель навигации была над контентом.
Основному View устанавливается значение match_parent для ширины и высоты, поскольку он представляет весь интерфейс, когда скрыт NavigationDrawer.
Для ListView необходимо указывать параметр горизонтального выравнивания с помощью атрибута android:layout_gravity. Поддержка RTL-языков (справа налево) решается указанием значения «start» вместо «left» (в таком случае Navigation Drawer будет появляться справа на RTL макете).
Ширину ListView указываем в dp, а высоту ставим match_parent. Ширина боковой панели навигации должна быть не более 320dp, чтобы пользователь мог всегда видеть часть основного контента.
Инициализируем Drawer List
В классе activity в первую очередь необходимо инициализировать список элементов для Navigation Drawer. Как вы это сделаете зависит от контента вашего приложения. Панель навигации включает в себя listview, поэтому список должен быть заполнен с помощью адаптера (например ArrayAdapter или SimpleCursorAdapter).
Например, вот как можно инициализировать Navigation Drawer со строкой массива:
public class MainActivity extends Activity < private String[] mPlanetTitles; private DrawerLayout mDrawerLayout; private ListView mDrawerList; . @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPlanetTitles = getResources().getStringArray(R.array.planets_array); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerList = (ListView) findViewById(R.id.left_drawer); // Set the adapter for the list view mDrawerList.setAdapter(new ArrayAdapter(this, R.layout.drawer_list_item, mPlanetTitles)); // Set the list's click listener mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); . > >
Этот код также вызывает метод setOnItemClickListener (), чтобы обрабатывать нажатия пунктов в списке. В следующем разделе показано, как реализовать этот интерфейс и изменить вид экрана, когда пользователь выбирает элемент.
Обработка события нажатия пункта в Navigation Drawer
Когда пользователь выбирает элемент в списке, система вызывает метод onItemClick() объекта DrawerItemClickListener, реализующего интерфейс OnItemClickListener и присвоенного списку через setOnItemClickListener().
Действия в методе onItemClick() зависят от задач приложения. В следующем примере, при выборе каждого элемента списка в основное View (в FrameLayout, определенный по ID R.id.content_frame ) вставляется новый создаваемый фрагмент:
private class DrawerItemClickListener implements ListView.OnItemClickListener < @Override public void onItemClick(AdapterView parent, View view, int position, long id) < selectItem(position); >> /** Смена фрагментов в основном окне программы */ private void selectItem(int position) < // Создание нового фрагмента и вставка изображения для показа, в зависимости от выбранной позиции Fragment fragment = new PlanetFragment(); Bundle args = new Bundle(); args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); fragment.setArguments(args); // Вставка нового фрагмента взамен существующего FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.content_frame, fragment) .commit(); // Выделение выбранного элемента списка, обновление заголовка окна и закрытие бокового меню mDrawerList.setItemChecked(position, true); setTitle(mPlanetTitles[position]); mDrawerLayout.closeDrawer(mDrawerList); >@Override public void setTitle(CharSequence title)
Слушатель для открытия и закрытия Navigation Drawer
Для прослушивания событий открытия и закрытия бокового меню , вызываем setDrawerListener() в вашем DrawerLayout и передаем его интерфейсу DrawerLayout.DrawerListener . Этот интерфейс предоставляет обратные вызовы для таких событий, как onDrawerOpened() и onDrawerClosed() .
Однако, вместо реализации DrawerLayout.DrawerListener , если ваше activity включает action bar, вы можете расширить класс ActionBarDrawerToggle . Реализуя в нем интерфейс DrawerLayout.DrawerListener, можно переопределить обратные вызовы, что упрощает взаимодействие между значком строки меню и Navigation Drawer (подробнее об этом в следующем разделе).
Как обсуждалось в гайдлайне по Navigation Drawer, когда боковое меню выезжает, нужно менять содержимое панели действий action bar, например, изменить заголовок и удалить элементы действий, которые являются контекстно-зависимыми к основному содержимому. Следующий код показывает, как вы можете сделать это путем переопределения метода обратного вызова DrawerLayout.DrawerListener класса ActionBarDrawerToggle :
public class MainActivity extends Activity < private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; private CharSequence mDrawerTitle; private CharSequence mTitle; . @Override public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); . mTitle = mDrawerTitle = getTitle(); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) < /** Этот код вызывается, когда боковое меню переходит в полностью закрытое состояние. */ public void onDrawerClosed(View view) < super.onDrawerClosed(view); getActionBar().setTitle(mTitle); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() >/** Этот код вызывается, когда боковое меню полностью открывается. */ public void onDrawerOpened(View drawerView) < super.onDrawerOpened(drawerView); getActionBar().setTitle(mDrawerTitle); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() >>; // Set the drawer toggle as the DrawerListener mDrawerLayout.setDrawerListener(mDrawerToggle); > /* Этот код вызывается, когда мы вызываем invalidateOptionsMenu() */ @Override public boolean onPrepareOptionsMenu(Menu menu) < // If the nav drawer is open, hide action items related to the content view boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList); menu.findItem(R.id.action_websearch).setVisible(!drawerOpen); return super.onPrepareOptionsMenu(menu); >>
В следующей секции описываются аргументы конструктора ActionBarDrawerToggle и другие шаги, необходимые для установки и настройки взаимодействия со значком action bar icon.
Открыть и закрыть панель навигации по значку приложения
Пользователи могут открывать и закрывать navigation drawer жестом от левого края экрана. Если вы используете action bar, или пришедший ему на смену в Android 5.0 API новый виджет Toolbar, вы также должны позволять пользователям открывать и закрывать navigation drawer, нажав на значок приложения. Кроме того, рядом со значком приложения должен быть специальный значок, обозначающий наличие navigation drawer. Вы можете реализовать все это поведение при помощи ActionBarDrawerToggle , как показано в предыдущем разделе.
Чтобы использовать ActionBarDrawerToggle , создайте экземпляр при помощи конструктора с такими аргументами:
- Activity , в котором размещается боковая панель навигации.
- DrawerLayout .
- drawable ресурс, используемый в качестве индикатора панели.Стандартный навигационный значок панели доступен в Download the Action Bar Icon Pack.
- Строковый ресурс для обозначения открытой панели (для специальных возможностей).
- Строковый ресурс для обозначения закрытой панели (для специальных возможностей).
Теперь, в зависимости от использования класса ActionBarDrawerToggle в вашем списке панели навигации, вы должны вызвать ActionBarDrawerToggle в нескольких местах жизненного цикла activity:
public class MainActivity extends Activity < private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; . public void onCreate(Bundle savedInstanceState) < . mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle( this, /* host Activity */ mDrawerLayout, /* DrawerLayout object */ R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */ R.string.drawer_open, /* "open drawer" description */ R.string.drawer_close /* "close drawer" description */ ) < /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) < super.onDrawerClosed(view); getActionBar().setTitle(mTitle); >/** Called when a drawer has settled in a completely open state. */ public void onDrawerOpened(View drawerView) < super.onDrawerOpened(drawerView); getActionBar().setTitle(mDrawerTitle); >>; // Set the drawer toggle as the DrawerListener mDrawerLayout.setDrawerListener(mDrawerToggle); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); > @Override protected void onPostCreate(Bundle savedInstanceState) < super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); >@Override public void onConfigurationChanged(Configuration newConfig) < super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); >@Override public boolean onOptionsItemSelected(MenuItem item) < // Pass the event to ActionBarDrawerToggle, if it returns // true, then it has handled the app icon touch event if (mDrawerToggle.onOptionsItemSelected(item)) < return true; >// Handle your other action bar items. return super.onOptionsItemSelected(item); > . >
5.1. Использование нескольких Activity и NavigationDrawer.
NavigationDrawer – это боковое окно, которое используется в основном в качестве меню, стандартное поведение которого это появление при свайпе слева или при клике на кнопку меню в левом верхнем углу экрана.
В NavigationDrawer можно указать верхнюю часть и список с пунктами меню с иконками. Можно указать как в виде независимых пунктов так и сгруппировать по логике выполняемых действий как показано в нижней части списка пункты Share и Send сгруппированы в группу Communicate.
Создайте проект с макетом Empty Activity, затем код файла activity_main.xml измените на нижеследующий:
LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> Button android:text="Open Second Activity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnOpenSecond"/> LinearLayout>
Код класса MainActivity.java напишите так:
public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Получение ссылки на кнопку в макете по id кнопки Button btn = findViewById(R.id.btnOpenSecond); // Установка обработчика события клик кнопки btn.setOnClickListener(new View.OnClickListener() @Override public void onClick(View v) // Каждое Activity указывается через объект класса Intent. Для запуск Activity находящегося в этом же проекте необходимо создать объект Intent с передачей контекста и названия класса второго Activity. Контекст - это активный экземпляр Activity. Для указания контекста внтури Activity можно использовать this, а в случае использования контекста внутри подклассов или как в этом случае внутри кода интерфейсов необходимо указать "название класса".this, например: MainActivity.this Intent second = new Intent(MainActivity.this, NavigationActivity.class); // Функция для запуска второго Activity (окна) startActivity(second); > >); > >
Теперь необходимо добавить новое Activity на основе шаблона NavigationDrawerActivity. Нажмите правой кнопкой мыши на папку java затем выберите New -> Activity -> Navigation Drawer Activity.
Затем во всплывающем окне в поле ActivityName напишите NavigationActivity. Вы можете в это поле написать какое угодно название для Activity, но выполнение предлагаемых шагов необходимо для того, чтобы код примера работал корректно. В следующих двух изображениях показаны необходимые для выполнения шаги.


Код класса NaivagationAcitivity здесь не приводится, так как он генерируется автоматически со стороны Android Studio и ничего дополнительного писать на данный момент не подразумевается. Единственно, если хотите чтобы стандартные пункты меню выполняли какие-либо действия надо в соответствующем блоке if else if функции onNavigationItemSelected написать необходимое действие. Например можете изменить код функции onNavigationItemSelected своего проекта на код приведенный ниже:
@Override public boolean onNavigationItemSelected(MenuItem item) // Получаем id выбранного пункта меню. int id = item.getItemId(); // Сравнение полученного пункта меню с предполагаемыми id пунктов меню if (id == R.id.nav_camera) // Handle the camera action > else if (id == R.id.nav_gallery) > else if (id == R.id.nav_slideshow) > else if (id == R.id.nav_manage) > else if (id == R.id.nav_share) // Код выполняется если id выбранного пункта меню указан как share // Открытие окна для выбора приложения через которое можно поделиться какой-либо текстовой информацией Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, "Текст которым хотите поделиться"); sendIntent.setType("text/plain"); startActivity(Intent.createChooser(sendIntent, "Поделиться")); > else if (id == R.id.nav_send) > DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; >
С добавлением нового Activity на основе шаблона Navigation Drawer добавляются ещё 4 файла макета в папку res/layout и два файла в папку res/menu.
Файлы в папке res/layout:
- activity_navigation.xml – файл с виджетами DrawerLayout и NavigationView является корневым в котором выводятся другие виджеты.
- app_bar_navigation.xml – в этом файле в качестве корневого контейнера используется ConstraintLayout внутри него размещен AppBarLayout и Toolbar. Два последних служат для замены системного Toolbar-а на свой для вывода кнопки открытия меню NavigationDrawer-а.
- content_navigation.xml – контейнер для разещения основных виджетов Activity. Выполняет ту же функцию как и файл activity_main.xml для MainActivity.
- nav_header_navigation.xml – файл заголовка меню NavigationDrawer-а. Можно изменить всё содержимое этого файла изменения оформления заголовка NavigationDrawer при необходимости.
В целях ознакомления с использованием вложенных макетов код файла content_navigation.xml замените на код ниже:
LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior"> LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="1" /> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="2" /> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="3" /> LinearLayout> LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="4" /> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="5" /> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="6" /> LinearLayout> LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="7" /> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="8" /> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="9" /> LinearLayout> LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="+" /> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="0" /> Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="=" /> LinearLayout> LinearLayout> LinearLayout>
Шаблон Navigation Drawer Activity
Статья устарела! Она редактировалась последний раз в 2015 году. С тех пор появился Koltin, а сам код модифицировался под использование Jetpack Navigation. Оставлена на память, потом будет удалена.
Рассмотрим шаблон Navigation Drawer Activity. Создадим новый проект и выберем нужный шаблон.

Для беглого знакомства можете сразу запустить проект и посмотреть его в действии. При запуске приложение выглядит как обычная программа «Hello World». Но есть и отличия. Нажмите на значок в виде трёх горизонтальных полосок в заголовке. Значок в документации называется «гамбургером» (Hamburger menu). Это официальная позиция Гугла. Но в реальности значок символизирует полосатых котов (никому не рассказывайте). При нажатии слева вылезет навигационная шторка. Шторка работает как обычная шторка в ванной. По высоте она занимает весь экран, включая системную область. Можете подвигать шторку вперёд-назад, чтобы увидеть, что верхняя кромка шторки в системной области полупрозрачна и не закрывает системные значки. Подобное поведение доступно на устройствах под Android 5 и выше. На старых устройствах шторка находится под системной панелью. Недавно стал проверять работу под Android 8.0 и увидел, что шторка теперь не закрывает системную панель. Ниже для сравнения я привёл два варианта.

Сама шторка состоит из двух основных частей — в верхней части находится картинка и текст, а в нижней — меню со значками. Меню в свою очередь разделено на две группы. В верхней части значки можно выбрать и выбранный пункт останется выделенным. В нижней части меню пункты не выделяются. Уберите шторку обратно и вызовите теперь её не нажатием на значок гамбургера, а движением пальца от края экран в центр. Получилось? Отлично, а теперь выдвигайте шторку медленно и наблюдайте за значком гамбургера. Вы увидите, что во время движения значок трансформируется. К сожалению, шторка закрывает значок и непонятно, во что превращаются три полоски. А превращаются они в три кота ж! стрелку. Позже я покажу, как увидеть её. А может не покажу, я ещё не решил.
Возвращаемся в студию и начинаем изучать код проекта.
Если открыть файл activity_main.xml в режиме Design, то можно увидеть, как будет выглядеть приложение с открытой шторкой.
Небольшие расхождения имеются, но в целом совпадает с реальным приложением.
Посмотрим на его содержание.
Сейчас важно запомнить, что за выдвигающую шторку отвечает элемент NavigationView, который входит последним в контейнере DrawerLayout и представляет собой навигационное меню. А перед меню находится вставка include, указывающая на разметку app_bar_main.xml.
Атрибут tools:openDrawer позволяет указать студии, что навигационное меню нужно отобразить в раскрытом виде в режиме просмотра разметки.
NavigationView
В 2014 году Google показал новый дизайн и различные новые примеры по навигации. Но вначале они использовали подручные средства, которые были под рукой — фрагменты.
Спустя год компания разработала на основе предка FrameLayout новый компонент NavigationView, который стал частью библиотеки Android Design Support Library.
Новый подход оказался неожиданным, но логичным. Раз выдвижная шторка содержит навигационное меню, то и класс был спроектирован как меню. Вы можете создать элементы меню в ресурсах res/menu стандартным способом и получить готовую навигацию.
Необходимые рекомендации по созданию навигационной выдвижной шторки можно найти на странице Navigation drawer — Patterns.
Перейдём к деталям.
Теперь в рекомендациях не указаны точные размеры шторки, хотя раньше нужно было самостоятельно указать ширину шторки. Видимо, новый класс сам обеспечивает необходимую ширину. Интерес представляют два последних атрибута.
Тег NavigationView содержит ссылку на собственную разметку в атрибуте app:headerLayout, который указывает на файл nav_header_main.xml (верхняя часть шторки), а также на меню в атрибуте app:menu, который ссылается на ресурс меню menu/activity_main_drawer.xml.
Откроем файл nav_header_main.xml и посмотрим на разметку шторки.
Разметка состоит из ImageView и двух TextView, размещённых в контейнере LinearLayout. Фон контейнера определён в ресурсе drawable/side_nav_bar.xml и представляет собой градиент.
Остальные атрибуты понятны и не требуют пояснений.
Можно (но не нужно) настроить верхнюю часть шторки не через XML, а программно.
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); // Наполняем шапку элементами View headerLayout = navigationView.inflateHeaderView(R.layout.nav_header_main); ImageView headerImageView = headerLayout.findViewById(R.id.imageView);
После обновления одной из версий библиотеки Design Support, доступ к шапке осуществляется теперь через другой код.
View headerLayout = navigationView.getHeaderView(0);
Теперь рассмотрим ресурс навигационного меню res/menu/activity_main_drawer.xml.
Принцип создания элементов меню остался стандартным. Каждый пункт меню представляет собой тег item с указанием значка и текста. Для группировки используется элемент group. Поведение элементов меню в группе регулируется атрибутом android:checkableBehavior. В примере используется значение single — при нажатии на пункт меню, он останется выделенным (принцип переключателя RadioButton). Всего доступно три варианта.
- single – можно выбрать один элемент группы (переключатель)
- all — можно выбрать все элементы группы (флажок)
- none – элементы не выбираются
В библиотеке Android Support Design версии 23 вариант all не работает и будет действовать, как со значением single.
Также следует обратить внимание, что теперь проект ссылается на векторные рисунки, которые находятся в папке drawable-21.
Осталось рассмотреть тег include, который ссылается на файл ресурса res/layout/app_bar_main.xml. Он вам будет знаком по шаблону Blank Activity, который мы изучали в статье Библиотека Android Support Design. Только там он находился в файле activity_main.xml, а здесь его перенесли в файл app_bar_main.xml. Всё остальное осталось без изменений. Повторяться не будем.
Теперь изучим код активности для работы со шторкой.
В классе активности реализуется интерфейс OnNavigationItemSelectedListener с его методом onNavigationItemSelected():
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener < @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) < // Handle navigation view item clicks here. int if (id == R.id.nav_camara) < // Handle the camera action >else if (id == R.id.nav_gallery) < >else if (id == R.id.nav_slideshow) < >else if (id == R.id.nav_manage) < >else if (id == R.id.nav_share) < >else if (id == R.id.nav_send) < >DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; > >
Логика кода ничем не отличается о работы с обычным меню. Определяется идентификатор выбранного пункта и далее вам нужно написать свой код. Затем вызывается метод closeDrawer() для закрытия шторки.
Добавим код для первого пункта меню.
if (id == R.id.nav_camera) < // Handle the camera action Toast.makeText(getApplicationContext(), "Вы выбрали камеру", Toast.LENGTH_SHORT).show(); >
При нажатии кнопки «Назад» проверяется состояние шторки. Если шторка открыта (isDrawerOpen()), то её закрываем с помощью метода closeDrawer().
@Override public void onBackPressed() < DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) < drawer.closeDrawer(GravityCompat.START); >else < super.onBackPressed(); >>
В методе onCreate() происходит инициализация шторки.
@Override protected void onCreate(Bundle savedInstanceState)
Теперь поговорим об изменениях, которые можно внести в проект.
Хотите выдвигать шторку справа? Установите значение end у атрибута layout_gravity. Обычно используется для стран с обратным порядком букв.

На самом деле смысла в этом не оказалось. Да, шторка выдвигается вручную. Но если нажать на значок гамбургера, то приложение валится с ошибкой. Любое нажатие в меню шторки также приводит к ошибке. Теоретически можно написать код, который исправит проблему, но он будет сложным. Забудьте об этом совете.
Тем не менее, можно реализовать забавный эффект — добавить вторую шторку на экран. Первая будет работать главной и реагировать на нажатие значка, а вторая будет дополнительной для вывода какой-то информации. Достаточно в разметку добавить второй NavigationView с атрибутом android:layout_gravity=»end»
Кстати, если выдвигать правую шторку, то можно наблюдать трансформацию значка без помех. Скриншот во время частичного выдвижения шторки справа.

Для изменения цвета значков и текста в навигационном меню используйте атрибуты app:itemIconTint и app:itemTextColor.
Данным атрибутам соответствуют методы setItemIconTintList() и setItemTextColor() компонента NavigationView.

Так как наличие шторки не совсем очевидно, можно при первом запуске показать шторку в раскрытом состоянии. Далее можно запомнить состояние шторки при первом запуске в настройках, чтобы во второй раз выводить её уже в закрытом состоянии. Можете самостоятельно реализовать эту возможность.
// показываем в открытом состоянии в onCreate() drawer.openDrawer(GravityCompat.START);
Напоследок покажу превращение значка гамбургера в стрелку в явном виде, как и обещал в начале статьи. Напомню, что по рекомендации Material Design шторка должна закрывать всю область экрана. Но если вы хотите поместить шторку под заголовком, то следует немного поправить разметку. Откроем файл app_bar_main.xml и вырежем из него небольшой кусок. Затем в файле activity_main.xml добавим LinearLayout в качестве корневого контейнера и вставим скопированный ранее кусок кода.
Смотрим на значок.
Сама анимация значка зависит от переменной toggle (объект класса ActionBarDrawerToggle). Если вы её уберёте, то никакого значка в заголовке приложения не будет.
Можно поменять цвет значка гамбургера. Откроем файл стилей res/values/styles.xml и добавим:
Элемент spinBars со значением true позволяет использовать анимацию. В противном случае значок будет статичным.
Навигация
В шаблоне присутствует метод onNavigationItemSelected() с аннотацией @SuppressWarnings(«StatementWithEmptyBody») (Оператор с пустым телом). Нам нужно добавить свой код для навигации, который должен реагировать на нажатия в меню шторки. Нам понадобятся фрагменты. Для примера создадим первый фрагмент.
package ru.alexanderklimov.navigationdrawerdemo; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class FirstFragment extends Fragment < @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) < return inflater.inflate(R.layout.fragment_first, container, false); >>
Разметка для первой активности.
По такому же образу создайте второй фрагмент SecondFragment и т.д.
Определим RelativeLayout в файле content_main.xml в качестве контейнера.
Теперь можем написать недостающий код для навигации по фрагментам в MainActivity.
@SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) < // Создадим новый фрагмент Fragment fragment = null; Class fragmentClass = null; // Handle navigation view item clicks here. int if (id == R.id.nav_camera) < // Handle the camera action fragmentClass = FirstFragment.class; >else if (id == R.id.nav_gallery) < fragmentClass = SecondFragment.class; >else if (id == R.id.nav_slideshow) < >else if (id == R.id.nav_manage) < >else if (id == R.id.nav_share) < >else if (id == R.id.nav_send) < >try < fragment = (Fragment) fragmentClass.newInstance(); >catch (Exception e) < e.printStackTrace(); >// Вставляем фрагмент, заменяя текущий фрагмент FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction().replace(R.id.container, fragment).commit(); // Выделяем выбранный пункт меню в шторке item.setChecked(true); // Выводим выбранный пункт в заголовке setTitle(item.getTitle()); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; >
Добавляем счётчик в меню шторки
Откройте файл res/menu/activity_main_drawer.xml и добавьте атрибут app:actionViewClass=»android.widget.TextView» ко второму и третьему элементу меню из шаблона. Теперь эти элементы будут связаны с текстовыми метками.
Объявим текстовые метки и инициализируем их в методе onCreate(). В отдельном методе будем управлять их свойствами.
private TextView mSlideshowTextView; private TextView mGalleryTextView; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); . mGalleryTextView = (TextView) MenuItemCompat.getActionView(navigationView.getMenu(). findItem(R.id.nav_gallery)); mSlideshowTextView = (TextView) MenuItemCompat.getActionView(navigationView.getMenu(). findItem(R.id.nav_slideshow)); // метод для счетчиков initializeCountDrawer(); >private void initializeCountDrawer()

Вы можете переделать метод под себя, чтобы динамически изменять показания счётчика.
Сдвигаем содержимое экрана
При выдвижении шторки можно сдвинуть основное содержание. Потребуется небольшая модификация кода. Для начала нужно присвоить идентификатор контейнеру ConstraintLayout в content_main.xml.
В MainActivity добавим экземпляру ActionBarDrawerToggle метод onDrawerSlide() и сдвинем содержимое на определённую величину. При желании можно также изменить размер, используя второй параметр метода slideOffset.
final ConstraintLayout content = findViewById(R.id.content); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string .navigation_drawer_close) < @Override public void onDrawerSlide(View drawerView, float slideOffset) < super.onDrawerSlide(drawerView, slideOffset); float slideX = drawerView.getWidth() * slideOffset; content.setTranslationX(slideX); // а также меняем размер content.setScaleX(1 - slideOffset); content.setScaleY(1 - slideOffset); >>; drawer.addDrawerListener(toggle); toggle.syncState();
Дополнительное чтение
Библиотека mxn21/FlowingDrawer с прикольным эффектом.