В этом уроке:
- Как создать макет для Navigation Drawer
- Как инициализировать Navigation Drawer
- Как обработать событие выбора пункта в списке меню Navigation Drawer
- Как установить слушатель (listener) на открытие и закрытие Navigation Drawer
- Открытие и закрытие Navigation Drawer по нажатию значка приложения в тулбаре
- Скачать образец приложения с Navigation Drawer
- Скачать Android Design Icons
- Как разместить Navigation Drawer под Toolbar’ом
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 для боковой панели навигации.
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- The main content view --> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- The navigation drawer --> <ListView android:id="@+id/left_drawer" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" android:choiceMode="singleChoice" android:divider="@android:color/transparent" android:dividerHeight="0dp" android:background="#111"/> </android.support.v4.widget.DrawerLayout>
Этот макет имеет некоторые важные особенности:
Основной 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<String>(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) { mTitle = title; getActionBar().setTitle(mTitle); }
Слушатель для открытия и закрытия 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); } ... }
Перевод источника. Скачать исходный код.
Я не правильно понял проблему, на самом деле StatusBar на своем месте, это под ToolBar-ом какая-то полоса размером со StatusBar, поэтому я эту полосу принял за него принял, подскажите как от неё избавится?
в разных версиях по-разному отображается. Простого решения нет – легче применить сторонние библиотеки.
т.е. используя шаблон Navigation Drawer в Android Studio нет возможности в моём случае поднять StatusBar наверх над Тoolbar-ом?
Уважаемый admin, сделал как вы посоветовали,
перенес toolbar из app_bar_main.xml в activity_main.xml,
navigation drawer стал выезжал под StatusBar (почему-то StatusBar оказался под Тoolbar-ом)
как теперь поменять местами StatusBar и Тoolbar ?
Используйте библиотеку https://github.com/mikepenz/MaterialDrawer – она имеет много гибких настроек. Мы ее подробно разбираем в курсе по приложению для Youtube http://www.fandroid.info/prodvinutyj-kurs-po-sozdaniyu-android-prilozheniya-dlya-youtube/
Спасибо за ресурс!
Как увеличить размер текста в activity_menu_drawer.xml ?
Используйте атрибут android:textSize=”18sp”
за ответ спасибо, но это не совсем мой случай, когда создаю проект на шаблоне «Navigation Drawer Activity», activity_main.xml выглядет следующим образом:
пытался сделать по вашей рекомендации, ничего не получается…
Чтобы navigation drawer выезжал под toolbar’ом перенесите toolbar из app_bar_main.xml в activity_main.xml, измененный таким образом:
Использую Android Studio и шаблон “Navigation Drawer Activity”
Запускаю пустой проект на основе этого шаблона, вижу что выдвигающаяся панель закрывает заголовок активности.
как сделать, чтобы панель выдвигалась под заголовком?
Вы должны переместить DrawerLayout в качестве верхнего родителя и переместить Toolbar из DrawerLayout контейнера контента. Пример ниже:
RelativeLayout
----Toolbar
----DrawerLayout
---ContentView
---DrawerList
Отличный пример,
Как сделать, чтобы выпадающее меню было и на других активити без дублирования обработки нажатий на пункты меню?
Использовать фрагменты в приложении
спасибо огромное за ваши труды !!