Реализация BottomAppBar. Часть 3: Поведения для Android

от автора

Реализация BottomAppBar. Часть 3: Поведения для Android

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

В первой и второй частях нашей серии статей про BottomAppBar мы познакомились с BottomAppBar и обсудили его атрибуты. Также мы объяснили, как реализовать Navigation Drawer и меню приложения в рамках BottomAppBar.

Поведение

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

Макет

Первый гайдлайн описывает макет BottomAppBar. Вот что предлагается:

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

Гайдлайн макета

Основываясь на этом гайдлайне, на главных экранах рекомендуется использовать макет BottomAppBar, показывающий несколько пунктов в меню и центрированную FAB (Floating Action Button). На второстепенных экранах, переход на которые осуществляется с главных, макет BottomAppBar должен состоять из выровненной по правому краю FAB и нескольких дополнительных пунктов в меню. Переходы между этими двумя экранами должны выполняться надлежащим образом. Gif сверху демонстрирует этот гайдлайн.

Теперь давайте посмотрим, как можно реализовать это поведение. У нас есть два xml-файла в папке res/menu для меню каждого экрана:

<?xml version="1.0" encoding="utf-8"?> <menu xmlns:tools="http://schemas.android.com/tools"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:android="http://schemas.android.com/apk/res/android">      <item         android:id="@+id/app_bar_search"         android:icon="@drawable/baseline_search_white_24"         android:title="@string/action_search"         app:showAsAction="ifRoom"/>  </menu>

<?xml version="1.0" encoding="utf-8"?> <menu xmlns:tools="http://schemas.android.com/tools"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:android="http://schemas.android.com/apk/res/android">      <item         android:id="@+id/app_bar_mail"         android:icon="@drawable/baseline_mail_white_24"         android:title="@string/action_mail"         app:showAsAction="ifRoom"/>      <item         android:id="@+id/app_bar_delete"         android:icon="@drawable/baseline_delete_white_24"         android:title="@string/action_delete"         app:showAsAction="ifRoom"/>      <item         android:id="@+id/app_bar_archieve"         android:icon="@drawable/baseline_archive_white_24"         android:title="@string/action_archieve"         app:showAsAction="ifRoom"/>  </menu>

Когда происходит переход между экранами, например, по нажатию кнопки TOGGLE SCREEN в нашем случае, макет BottomAppBar, включая меню и FAB, должен измениться. Вот базовый код для такого поведения макета BottomAppBar:

// Hide navigation drawer icon bottom_app_bar.navigationIcon = null  // Move FAB from the center of BottomAppBar to the end of it bottom_app_bar.fabAlignmentMode = BottomAppBar.FAB_ALIGNMENT_MODE_END  // Replace the action menu bottom_app_bar.replaceMenu(bottomappbar_menu_secondary)  // Change FAB icon fab?.setImageDrawable(baseline_reply_white_24)

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

Скроллирование

Скроллирование — важный триггер поведения для таких компонентов, как BottomAppBar. На странице гайдлайнов Material Design рекомендуется следующее поведение для этого случая:

При скроллировании BottomAppBar может появиться или исчезнуть:
— Скроллирование вниз скрывает BottomAppBar. Если на нём была FAB, она отсоединяется от панели и остается на экране.
— Скроллирование вверх показывает BottomAppBar и снова присоединяет его к FAB, если она там была.

Ниже приведена демонстрация поведения BottomAppBar при скроллировании.

Скроллирование и BottomAppBar

Чтобы использовать это поведение, BottomAppBar и FAB должны быть прямыми дочерними элементами CoordinatorLayout. Затем мы включаем hideOnScroll и устанавливаем флаги скроллирования для BottomAppBar:

<com.google.android.material.bottomappbar.BottomAppBar     android:id="@+id/bottom_app_bar"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:layout_gravity="bottom"     app:fabAlignmentMode="center"     app:hideOnScroll="true"     app:layout_scrollFlags="scroll|enterAlways"/>

Этого вполне достаточно для реализации такого поведения BottomAppBar.

Возвышение

У каждого компонента в мире Material Design есть возвышение, аналогичное нашему физическому миру. У BottomAppBar возвышение — 8dp, а само содержимое экрана возвышается на 0dp. FAB в статичном состоянии возвышается на 12dp. Два компонента, которые мы ещё вспомним в этой статье, Navigation Drawer и Snackbar, возвышаются на 16dp и 6dp соответственно.

Как правило, Snackbar — это компонент для уведомления пользователя, выскакивающий из нижней части экрана. Но если на экране есть BottomAppBar или Navigation Drawer, поведение Snackbar должно измениться. В этих случаях Snackbar следует показывать над нижними компонентами. Вот демонстрация и соответствующий код для реализации:

Snackbar и BottomAppBar

private fun displayMaterialSnackBar() {         val marginSide = 0         val marginBottom = 550         val snackbar = Snackbar.make(                 coordinatorLayout2,                 "FAB Clicked",                 Snackbar.LENGTH_LONG         ).setAction("UNDO") {  }         // Changing message text color         snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.colorSnackbarButton))          val snackbarView = snackbar.view         val params = snackbarView.layoutParams as CoordinatorLayout.LayoutParams          params.setMargins(                 params.leftMargin + marginSide,                 params.topMargin,                 params.rightMargin + marginSide,                 params.bottomMargin + marginBottom         )          snackbarView.layoutParams = params         snackbar.show() }

Как мы уже упоминали, Navigation Drawer возвышается на 16dp, что означает — согласно гайдлайну —

Меню, выпадающие из BottomAppBar (например, Navigation Drawer), открываются как модальные окна на уровень выше, чем сам BottomAppBar.

Ниже приведена реализация нашего Navigation Drawer:

Поведение Navigation Drawer

Navigation Drawer является модальным окном и поэтому следует приведённому выше правилу реализации.

Детали реализации этого поведения выглядят следующим образом. В папке res/menu должен быть создан xml-файл меню для Navigation View, который будет использован в Navigation Drawer:

<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto">     <group android:checkableBehavior="none">         <item             android:id="@+id/nav1"             android:icon="@drawable/baseline_mail_white_24"             android:title="@string/nav_item1" />         <item             android:id="@+id/nav2"             android:icon="@drawable/baseline_bookmark_white_24"             android:title="@string/nav_item2" />         <item             android:id="@+id/nav3"             android:icon="@drawable/baseline_message_white_24"             android:title="@string/nav_item3" />         <item             android:id="@+id/nav4"             android:icon="@drawable/baseline_note_white_24"             android:title="@string/nav_item4" />         <item             android:id="@+id/nav5"             android:icon="@drawable/baseline_location_on_white_24"             android:title="@string/nav_item5" />         <item             android:id="@+id/nav6"             android:icon="@drawable/baseline_sync_white_24"             android:title="@string/nav_item6" />         <item             android:id="@+id/nav7"             android:icon="@drawable/baseline_cloud_upload_white_24"             android:title="@string/nav_item7" />         <item             android:id="@+id/nav8"             android:icon="@drawable/baseline_favorite_white_24"             android:title="@string/nav_item8" />         <item             android:id="@+id/nav9"             android:icon="@drawable/baseline_chrome_reader_mode_white_24"             android:title="@string/nav_item9" />         <item             android:id="@+id/nav10"             android:icon="@drawable/baseline_select_all_white_24"             android:title="@string/nav_item10" />         <item             android:id="@+id/nav11"             android:icon="@drawable/baseline_sort_white_24"             android:title="@string/nav_item11" />         <item             android:id="@+id/nav12"             android:icon="@drawable/baseline_access_time_white_24"             android:title="@string/nav_item12" />         <item             android:id="@+id/nav13"             android:icon="@drawable/baseline_data_usage_white_24"             android:title="@string/nav_item13" />      </group> </menu>

Затем должен быть создан файл макета для фрагмента, использующего Navigation Drawer:

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/navigation_view_layout"     android:layout_width="match_parent"     android:layout_height="wrap_content"     app:behavior_hideable="true"     app:layout_behavior="@string/bottom_sheet_behavior">      <com.google.android.material.navigation.NavigationView         android:id="@+id/navigation_view"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_gravity="bottom"         android:layout_marginTop="4dp"         android:paddingBottom="40dp"         app:layout_constraintEnd_toEndOf="parent"         app:layout_constraintHorizontal_bias="0.0"         app:layout_constraintStart_toStartOf="parent"         app:layout_constraintTop_toBottomOf="@+id/view2"         app:menu="@menu/bottom_nav_drawer_menu"         app:theme="@style/NavigationDrawerStyle" />      <TextView         android:id="@+id/textView"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_marginStart="12dp"         android:layout_marginTop="16dp"         android:fontFamily="@font/rubik_medium"         android:text="@string/bottom_sheet_name"         android:textColor="@color/colorAccent"         android:textSize="18sp"         android:textStyle="bold"         app:layout_constraintStart_toEndOf="@+id/imageView"         app:layout_constraintTop_toTopOf="parent" />      <TextView         android:id="@+id/textView2"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_marginTop="2dp"         android:text="@string/bottom_sheet_email"         android:textColor="@color/colorAccent"         app:layout_constraintStart_toStartOf="@+id/textView"         app:layout_constraintTop_toBottomOf="@+id/textView" />      <ImageView         android:id="@+id/imageView"         android:layout_width="48dp"         android:layout_height="48dp"         android:layout_marginStart="24dp"         android:background="@drawable/baseline_account_circle_black_48"         app:layout_constraintStart_toStartOf="parent"         app:layout_constraintTop_toTopOf="@+id/textView" />      <View         android:id="@+id/view2"         android:layout_width="match_parent"         android:layout_height="2dip"         android:layout_marginTop="15dp"         android:background="#447e7e7e"         app:layout_constraintEnd_toEndOf="parent"         app:layout_constraintHorizontal_bias="0.0"         app:layout_constraintStart_toStartOf="parent"         app:layout_constraintTop_toBottomOf="@+id/textView2" />      <ImageView         android:id="@+id/close_imageview"         android:layout_width="24dp"         android:layout_height="24dp"         android:layout_marginTop="8dp"         android:layout_marginEnd="16dp"         android:layout_marginBottom="8dp"         android:background="@drawable/baseline_close_black_24"         android:visibility="gone"         app:layout_constraintBottom_toBottomOf="@+id/textView2"         app:layout_constraintEnd_toEndOf="parent"         app:layout_constraintTop_toTopOf="@+id/textView" />  </androidx.constraintlayout.widget.ConstraintLayout>

Этот файл макета содержит Navigation View и другие компоненты, формирующие макет для Navigation Drawer. Чтобы создать этот макет, нам нужен класс фрагмента, расширяющий BottomSheetDialogFragment:

class BottomNavigationDrawerFragment: BottomSheetDialogFragment() {      override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {         return inflater.inflate(R.layout.fragment_bottom_navigation_drawer, container, false)     }      override fun onActivityCreated(savedInstanceState: Bundle?) {         super.onActivityCreated(savedInstanceState)          navigation_view.setNavigationItemSelectedListener { menuItem ->             // Bottom Navigation Drawer menu item clicks             when (menuItem.itemId) {                 // R.id.nav1 -> context!!.toast(getString(R.string.nav1_clicked))             }             // Add code here to update the UI based on the item selected             // For example, swap UI fragments here             true         }     } }

При клике по значку Navigation Drawer создаётся экземпляр этого фрагмента, который показывается в виде модального окна:

override fun onOptionsItemSelected(item: MenuItem?): Boolean {         when (item!!.itemId) {             android.R.id.home -> {                 val bottomNavDrawerFragment = BottomNavigationDrawerFragment()                 bottomNavDrawerFragment.show(supportFragmentManager, bottomNavDrawerFragment.tag)             }         }         return true }

Это статья завершает нашу серию статей про BottomAppBar. Найти исходный код этой статьи вы можете на Github. Комментируйте и задавайте вопросы.

← Реализация BottomAppBar. Часть 1: Material компоненты для Android
← Реализация BottomAppBar. Часть 2: Меню и элемент управления Navigation Drawer


ссылка на оригинал статьи https://habr.com/post/422445/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *