Реалізація 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>

 

Читайте також  18 правил ідеального psd-макету — корисний чек-лист для дизайнерів

Коли відбувається перехід між екранами, наприклад, по натисненню кнопки 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 icon FAB
fab?.setImageDrawable(baseline_reply_white_24)

 

Якщо ви хочете зробити анімовані переходи, потрібен додатковий код. Ви можете вивчити вихідний код, прикріплений в кінці цієї статті, у якому знайдете анімацію.

 

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

 

Скроллирование — важливий тригер поведінки для таких компонентів, як BottomAppBar. На сторінці гайдлайнов Material Design рекомендується наступне поведінка для цього випадку:

При скроллінгом BottomAppBar може з’явитися або зникнути:
— Скроллирование вниз приховує BottomAppBar. Якщо на ньому була FAB, вона від’єднується від панелі і залишається на екрані.
— Скроллирование вгору показує BottomAppBar і знову приєднує його до FAB, якщо вона там була.

Нижче наведена демонстрація поведінки 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 слід показувати над нижніми компонентами. Ось демонстрація і відповідний код для реалізації:

Читайте також  Михайло Бессмельцев з колегою розробив нові алгоритми векторизації графіки

 

 

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 є модальним вікном і тому слід наведеним вище правилом реалізації.

 

Деталі реалізації цієї поведінки виглядають наступним чином. У папці 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
}
}
}

 

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

При кліку по значку 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. Коментуйте і задавайте питання.

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *