Android studio bottom navigation bar

Реализация BottomAppBar. Часть 2: Меню и элемент управления Navigation Drawer

В предыдущей статье мы обсудили основы BottomAppBar, который не так давно представили на Google I/O 2018 как часть Material компонентов для Android. Мы рассмотрели способ реализации BottomAppBar и изучили его атрибуты. Также BottomAppBar может отображать элементы меню и элемент управления Navigation Drawer, которые раньше мы использовали в тулбаре.

Теперь элементы меню и элемент управления Navigation Drawer должны быть частью BottomAppBar. А сейчас давайте посмотрим, как использовать меню и Navigation Drawer при помощи BottomAppBar.

Меню BottomAppBar

Сначала необходимо создать .xml файл в каталоге res/menu для элементов меню, которые должны быть показаны в BottomAppBar. Вот мой файл bottomappbar_menu.xml:

В MainActivity, в которой вы вызываете setSupportActionBar(bottom_app_bar), добавьте следующий код в метод:

Теперь элементы меню должны отображаться в BottomAppBar.

Обработка кликов по элементам меню

Для обработки кликов по элементам меню необходимо добавить следующий код в MainActivity:

Теперь меню в BottomAppBar настроено и должно функционировать правильно:

Элемент управления Navigation Drawer в BottomAppBar

Обычно Navigation Drawer реализуется при помощи NavigationView, расположенного в левой части приложения. С BottomAppBar поведение Navigation Drawer изменилось. Теперь Navigation Drawer представляет собой модальное окно в нижней части приложения.

Сперва необходимо создать само модальное окно:

Файл меню для Navigation Drawer также должен быть расположен в res/menu.

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

Следующие строки кода нужно добавить в метод onOptionsItemSelected, который используется для обработки кликов по элементам меню. При клике по значку навигации будет создаваться экземпляр объекта BottomNavigationDrawerFragment и отображаться Navigation View.

Вот код макета MainActivity:

А для кликов по элементам в окне Navigation Drawer вы можете использовать следующий код внутри класса BottomNavigationDrawerFragment:

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

Источник

Реализация BottomAppBar. Часть 1: Material компоненты для Android

BottomAppBar — это один из новых Android Material компонентов, которые были представлены на Google I/O 2018. Это по сути расширение компонента Toolbar. Новый BottomAppBar располагается в нижней части окна приложения в отличие от тулбара, который находится в его верхней части. С помощью этой парадигмы команда Material Design ожидает нового пользовательского опыта (UX). BottomAppBar намного более доступен для пользователя в сравнении с обычным тулбаром. Переместив панель управления и меню в нижнюю часть приложения, BottomAppBar предлагает кардинально новый дизайн для Android приложений.

Вместе с BottomAppBar также изменилось расположение Floating Action Button (FAB) (рус. плавающая кнопка действия). Теперь FAB могут быть размещены либо «врезаясь» в BottomAppBar, либо перекрывая его.

В этой статье будет продемонстрирована реализация основ BottomAppBar вместе с новыми вариантами размещения FAB.

Настройка

Для начала требуются небольшие первоначальные настройки.

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

Ниже приведены необходимые шаги настройки.

1.Добавьте репозиторий Google Maven в файле build.gradle .

2.Добавьте зависимость для material компонентов в файле build.gradle . Имейте в виду, что версия регулярно обновляется.

Читайте также:  Как включить процесс ком андроид

3.Установите в качестве compileSdkVersion и targetSdkVersion версию API минимум для Android P (т.е. 28 и выше).

4.Убедитесь, что ваше приложение наследует тему Theme.MaterialComponents, чтобы BottomAppBar использовал самый последний стиль. В качестве альтернативы вы можете задавать стиль для BottomAppBar при объявлении виджета в XML-файле макета следующим образом:

Реализация

Вы можете добавить BottomAppBar в свой макет следующим образом. Также BottomAppBar должен быть дочерним элементом CoordinatorLayout.

Вы можете привязать FAB к BottomAppBar, указав id элемента BottomAppBar в атрибуте app:layout_anchor элемента FAB. BottomAppBar может обволакивать FAB или FAB может перекрывать BottomAppBar.

Атрибуты BottomAppBar

В таблице ниже показаны атрибуты BottomAppBar.

backgroundTint

Это атрибут установки цвета фона BottomAppBar.

fabAlignmentMode

Атрибут определяет положение FAB (либо в центре, либо в конце BottomAppBar). Ниже показано выравнивание FAB в конце BottomAppBar.

fabAttached

Атрибут предназначен для привязки FAB к BottomAppBar и может быть true или false. Хотя по руководству по материальному дизайну не рекомендуется размещать FAB за пределами BottomAppBar, возможность такой настройки имеется. Ниже показана ситуация, когда для атрибута fabAttached установлено значение false.

fabCradleDiameter

Определяет диаметр «колыбели», содержащей FAB.

fabCradleRoundedCornerRadius

Задаёт радиус угла в точке встречи «колыбели» и горизонтальной части BottomAppBar.

fabCradleVerticalOffset

Указывает смещение «колыбели» снизу.

Вот весь XML-файл макета, который использовался для приведённых выше примеров.

Мы разобрались с основами нового компонента Android Material — BottomAppBar, а также новыми функциями FAB. Виджет BottomAppBar сам по себе не является сложным в использовании, поскольку он расширяет обычный Toolbar, но он кардинально меняет подход к проектированию интерфейса приложения.

Вторая и третья части этой серии про BottomAppBar будут посвящены работе с меню и навигацией и реализацией различных поведений BottomAppBar в соответствии с принципами Material Design.

Источник

Два года назад на Google I/O Android-разработчикам представили новое решение для навигации в приложениях — библиотеку Jetpack Navigation Component . Про маленькие приложения уже было сказано достаточно, а вот о том, с какими проблемами можно столкнуться при переводе большого приложения на Navigation Component , информации практически нет.

В этой и следующих двух статьях я расскажу о кейсах, с которыми может встретиться разработчик, желающий опробовать Navigation Component в большом Android-приложении.

UPDATE 30.12.2020:

Выводы, сделанные в этой статье, некорректны, так как при настройке BottomNavigationView была допущена серьёзная ошибка, которая повлекла за собой все описанные в статье проблемы. Подробности — в последней статье цикла.

Это текстовая версия моего выступления в рамках серии митапов по Android 11 в Android Academy. Само выступление было на английском, статью пишу на русском. Кому удобнее смотреть – велкам.

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

Disclaimer

Я сделал пример, по структуре навигации повторяющий основные моменты навигации соискательского приложения hh.ru, и выхватил ряд проблем, о которых и собираюсь рассказать. Я основательно поресёрчил практическую сторону вопроса, но, разумеется, рассмотрел далеко не все возможные кейсы.

Схема моего тестового приложения выглядит так:

В цикле статей мы разберём каждый переход, который описан на этой схеме, а также несколько кейсов, которые не поместились на картинку.

Кейсы с BottomNavigationView

Когда я только-только услышал про Navigation Component , мне стало интересно: как будет работать BottomNavigationView и как Google подружит несколько отдельных back stack-ов в разных вкладках. Два года назад с этим кейсом были некоторые проблемы, и я решил проверить, как там обстоят дела сегодня.

Читайте также:  Samsung google android mobile

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

Первый опыт

Я установил Android Studio 4.1 Beta (последнюю более-менее стабильную версию на тот момент) и попробовал шаблон приложения с нижней навигацией. Начало было многообещающим.

  • Мне сгенерировали Activity в качестве контейнера для хоста навигации и нижней навигации

Я убрал «шумовые» атрибуты, чтобы было проще читать.

Стандартный ConstraintLayout , в который добавили BottomNavigationView и тэг для инициализации NavHostFragment -а ( Android Studio , кстати, подсвечивает, что вместо фрагмента лучше использовать FragmentContainerView ).

  • Для каждой вкладки BottomNavigationView был создан отдельный фрагмент

Все фрагменты были добавлены в качестве отдельных destination-ов в общий граф навигации.

  • А ещё в проект был добавлен файл-ресурс для описания меню BottomNavigationView

При этом я заметил, что идентификаторы элементов меню должны совпадать с идентификаторами destination-ов в графе навигации. Не самая очевидная связь между табами BottomNavigationView и фрагментами, но работаем с тем, что есть.

Пора запускать приложение

После создания приложения из шаблона я запустил его и сразу столкнулся с двумя проблемами.

Первая проблема: при переключении между вкладками их состояние не сохранялось.

Для проверки я добавил во вкладку Dashboard простенькую ViewModel со счётчиком. На гифке видно, как я переключаюсь со вкладки Home на вкладку Dashboard , увеличиваю счётчик до четырёх. После этого я переключился обратно на вкладку Home и вновь вернулся на Dashboard . Счётчик сбросился.

Баг с описанием этой проблемы уже два года висит в Issue Tracker-е. Чтобы решить её, Google-у потребовалось серьёзно переработать внутренности фреймворка Fragment -ов, чтобы поддержать возможность работать с несколькими back stack-ами одному FragmentManager -у. Недавно на Medium вышла статья Ian Lake, в которой он рассказывает, что Google серьёзно продвинулись в этом вопросе, так что, возможно, фикс проблемы с BottomNavigationView не за горами.

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

Для демонстрации этой проблемы я добавил на вкладку Dashboard кнопку, которая ведёт на следующий экран. На гифке видно, как я переключаюсь на вкладку Dashboard , увеличиваю счётчик до трёх, а затем перехожу на экран Graphic . Если я нажимаю на кнопку Back – то всё работает как надо, состояние вкладки не сбрасывается. Но если, находясь на экране Graphic , я ещё раз нажму на вкладку Dashboard , то после возврата на первый экран в стеке увижу, что его состояние сброшено.

«Не самое лучшее первое впечатление», – подумал я. И начал искать фикс.

У нас есть workaround

Решение этих проблем живёт в специальном репозитории Google-а с примерами работы с Architecture Components , в проекте NavigationAdvancedSample.

Большая часть фикса расположена в файле NavigationExtensions.kt. В самом проекте довольно много кода, поэтому я не буду его разбирать подробно, а вместо этого подсвечу основные моменты, которые относятся к решению проблем.

  • Во-первых, для каждой вкладки вводится отдельный, независимый граф навигации

Соответственно, для примера BottomNavigationView с тремя вкладками у нас получится три отдельных файла навигации XML, в которых в качестве startDestination будут указаны первые фрагменты вкладок.

  • Во-вторых, для каждой вкладки под капотом создаётся отдельный NavHostFragment , который будет связан с графом навигации этой вкладки

FragmentManager пока что не поддерживает работу с множеством back stack-ов одновременно, поэтому пришлось придумать альтернативное решение, которое позволило ассоциировать с каждым графом свой back stack. Им стало создание отдельного NavHostFragment -а для каждого графа. Из этого следует, что с каждой вкладкой BottomNavigationView у нас будет связан отдельный NavController .

  • В-третьих, мы устанавливаем в BottomNavigationView специальный listener , который будет заниматься переключением между back stack-ами фрагментов
Читайте также:  Usb модем под андроид

В прикреплённом кусочке кода мы видим, как при переключении между вкладками BottomNavigationView выполняется специальная транзакция в FragmentManager -е, которая прикрепляет фрагмент выбранной вкладки и отцепляет все остальные фрагменты. По сути, так мы и переключаемся между различными back stack-ами.

  • В итоге метод настройки BottomNavigationView возвращает разработчику специальную LiveData , которая содержит в себе NavController выбранной вкладки. Этот NavController можно использовать, например, для обновления надписи на ActionBar -е

Метод для настройки BottomNavigationView вызывают в onCreate -е, когда Activity создаётся в первый раз, затем в методе onRestoreInstanceState , когда Activity пересоздаётся с помощью сохранённого состояния.

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

Опять же, не самая очевидная связь между этими элементами, зато работает.

После применения этого workaround-а первые две проблемы исчезли – теперь состояние вкладки сохраняется между переключениями вкладок.

Первая проблема решилась:

Адаптация workaround-а для фрагментов

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

Посмотрите внимательно на эту схему:

На ней можно увидеть, что пользователь начинает свой путь в приложении со Splash-экрана:

Google говорит, что Splash-экраны – зло, ухудшающее UX приложения. Тем не менее, Splash-экраны – суровая реальность большинства крупных Android-приложений. И если мы хотим использовать в нашем приложении Single Activity -архитектуру, то в качестве контейнера нижней навигации придётся использовать Fragment , а не Activity :

Я добавил вёрстку для фрагмента с нижней навигацией и перенёс настройку BottomNavigationView во фрагмент:

Я добавил в свой пример Splash-экран и дополнительную вкладку для BottomNavigationView . А чтобы пример стал ещё более походить на приложение для соискателей hh.ru, я также убрал из него ActionBar .

Для этого я поменял тему приложения с Theme.MaterialComponents.DayNight.DarkActionBar на Theme.MaterialComponents.DayNight.NoActionBar и убрал код для связки NavController -а с ActionBar -ом:

После всех манипуляций я включил режим Don’t keep activities, запустил свой пример и… получил краш при сворачивании приложения.

На гифке видно, как я запустил приложение, и после Splash-экрана показывается экран с нижней навигацией. После этого мы сворачиваем приложение и получаем краш.

В чём была причина? При вызове onDestroyView активный NavHostFragment пытается отвязаться от NavController -а. Так как мой фрагмент-контейнер с нижней навигацией никак не привязывал к себе NavController , который он получил из LiveData , метод Navigation.findNavController из onDestroyView крашил приложение.

Добавляем привязку NavController -а к фрагменту с нижней навигацией (для этого в Navigation Component-е есть утилитный метод Navigation.setViewNavController ), и проблема исчезает.

Но это ещё не всё. Не выключая режим Don’t keep activities, я попробовал свернуть, а затем развернуть приложение. Оно снова упало, но с другим неприятным исключением – IllegalStateException в FragmentManager – FragmentManager already executing transactions .

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

Краш происходит в методах, которые прикрепляют NavHostFragment к FragmentManager -у после их создания. Это исключение можно исправить при помощи костыля: обернуть методы attach-detach в Handler.post <> .

После добавления Handler.post приложение заработало, как надо.

Выводы по работе с BottomNavigationView

На этом с BottomNavigationView всё, на следующей неделе расскажу про кейсы с вложенными графами навигации.

Источник

Оцените статью