Введение

«Как вы сделаете нижнюю навигацию?» — вопрос, который проверяет не умение поставить библиотеку, а понимание паттерна: сколько пунктов допустимо, что говорит Material и как пункты связаны с экранами.

Нижняя навигация — это панель, по которой пользователь ходит по главным разделам приложения. На собеседовании за ней стоит проверка трёх вещей: знаете ли вы BottomNavigationView, понимаете ли правила Material про количество пунктов и умеете ли связать навигацию с экранами без сломанного бэкстека. Разберём всё по порядку.

Что такое BottomNavigationView

BottomNavigationView — стандартная нижняя панель навигации Android, реализация компонента Material Design.

Документация определяет его как панель, которая позволяет переключаться между top-level экранами в один тап, и прямо говорит: использовать её стоит, когда в приложении от трёх до пяти главных разделов. Класс наследуется от NavigationBarView — абстрактной базы нижней навигации, от которой также наследуется NavigationRailView (боковая панель).

Наполнение панели задаётся не кодом, а menu-ресурсом: у каждого пункта — title, icon и состояние enabled. Вот классический набор из трёх разделов:

menu_bottom_navigation.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/nav_home"
        android:icon="@drawable/ic_home"
        android:title="@string/nav_home" />
    <item
        android:id="@+id/nav_feed"
        android:icon="@drawable/ic_feed"
        android:title="@string/nav_feed" />
    <item
        android:id="@+id/nav_profile"
        android:icon="@drawable/ic_profile"
        android:title="@string/nav_profile" />
</menu>
activity_main.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">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/menu_bottom_navigation" />

</LinearLayout>

Обратите внимание: высоту панели и её вид задаёт тема Material — в коде она не нужна. wrap_content по высоте достаточно: размер контейнера определяет компонент.

Типичная ошибка на собеседовании: «Пункты добавляются через код — newTab, addItem». Это путаница с TabLayout. В BottomNavigationView пункты живут в menu-ресурсе, а код либо подписывается на выбор, либо связывает панель с навигацией. Умение объяснить роль menu — маркер, что кандидат действительно работал с компонентом.

Настройка: слушатели и связь с навигацией

Панель сделана, теперь её нужно связать с экранами — и вот здесь кандидаты начинают импровизировать.

Минимальный способ — слушатель выбора пункта. Современный вариант — setOnItemSelectedListener (в новых версиях библиотеки старый setOnNavigationItemSelectedListener помечен как deprecated):

MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val bottomNavigation = findViewById<BottomNavigationView>(R.id.bottom_navigation)
        bottomNavigation.setOnItemSelectedListener { item ->
            when (item.itemId) {
                R.id.nav_home -> showScreen(HomeFragment())
                R.id.nav_feed -> showScreen(FeedFragment())
                R.id.nav_profile -> showScreen(ProfileFragment())
            }
            true
        }
    }
}

Но на собеседовании и в реальном проекте ответ уровня middle звучит иначе: через Navigation Component. Панель связывается с NavController одной строкой — setupWithNavController — и тогда пункты меню соответствуют destinations графа навигации, а бэкстек и состояния экранов настраиваются декларативно.

MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val bottomNavigation = findViewById<BottomNavigationView>(R.id.bottom_navigation)
        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.fragment_container) as NavHostFragment
        val navController = navHostFragment.navController
        bottomNavigation.setupWithNavController(navController)
    }
}

Почему это спрашивают

setupWithNavController — короткий вопрос, который сразу делит кандидатов. Один отвечает «навешиваю listener и вручную переключаю фрагменты». Другой объясняет, что пункты меню мапятся на destinations, а бэкстеком управляет NavController, поэтому при повороте экрана и системном back навигация не ломается. Второе — это уровень middle.

Программный выбор пункта тоже задаётся через меню: menuItem.isChecked = true. И не забывайте про поведение скрытия при скролле: в CoordinatorLayout панель может исчезать при прокрутке контента через HideBottomViewOnScrollBehavior — эта фича часто всплывает в вопросах «как сделать, чтобы панель пряталась при скролле».

Правила Material и подводные камни

Material задаёт жёсткие правила для нижней навигации — и интервьюеры их спрашивают.

Спецификация прямо говорит: нижняя навигация — это от трёх до пяти пунктов, каждый пункт — top-level раздел, доступный из любого места приложения. Меньше трёх — используйте вкладки, больше пяти — вкладки или drawer. Нижнюю навигацию не смешивают с вкладками: у вкладок общая тема, у разделов нижней навигации — независимые.

Отдельный блок правил — как показывать иконки и подписи в зависимости от количества пунктов:

  • Три пункта — иконки и подписи у всех.
  • Четыре пункта — активный: иконка + подпись; неактивные: иконки, подписи желательны.
  • Пять пунктов — активный: иконка + подпись; неактивные: иконки, подписи если помещаются.
  • Иконка обязательна всегда, подпись — по правилам выше.

Частая ошибка на собеседовании: кандидат говорит «я сделаю шесть пунктов, это же просто». По спецификации Material — нет: больше пяти разделов — это уже не нижняя навигация, а повод пересмотреть структуру приложения (tabs или drawer). Интервьюер проверяет, знаете ли вы границы компонента, а не только его API.

Ещё один нюанс — Material 3. В новых проектах навигацию делают на компоненте Navigation Bar: он построен на той же базе (NavigationBarView), но выглядит иначе — выше контейнер, есть индикатор активного состояния, поддержка dynamic color и нет тени. Уметь объяснить разницу M2/M3 и то, что в Material 3 это тот же класс-основа, — приятный бонус к ответу.

Типичные ошибки на собеседовании

Вопрос про нижнюю навигацию — короткий, а ошибок на нём делают больше, чем кажется.

Первая ошибка — незнание ограничения «3–5 пунктов» и его обоснования. Вторая — путаница с TabLayout («пункты добавляются через код»). Третья — навигация вручную через supportFragmentManager без NavController, когда на вопрос «а что будет с бэкстеком при повороте» кандидат не может ответить.

Главный промах — панель без состояния. Кандидат рассказывает, как переключает фрагменты, но молчит про сохранение выбранного пункта при повороте экрана и про то, что пункты — это destinations навигационного графа. Состояние выбранного пункта хранит сам компонент, а вот согласованность с экранами — задача навигации.

Ещё одна классика: кандидат не знает, что у пункта может быть enabled=false (пункт виден, но некликабелен) и что выбор программно делается через menu.findItem(id).isChecked = true. Мелкие детали, но именно они отличают кандидата, который «читал», от того, кто «делал».

  • Определение: BottomNavigationView — панель для 3–5 top-level разделов; наследуется от NavigationBarView.
  • Наполнение: menu-ресурс с id, icon, title; выбор программно — isChecked = true.
  • Связка: слушатель setOnItemSelectedListener или setupWithNavController для корректного бэкстека.
  • Правила Material: 3–5 пунктов, иконка обязательна, подписи по количеству; в Material 3 — компонент Navigation Bar на той же базе.

Кто я и как помогу подготовиться

Меня зовут Рустем Бикбулатов, я senior Android-разработчик и провожу технические собеседования. Помогаю Android-разработчикам с опытом 1–2 года перейти на уровень middle за 4–8 недель: закрываю пробелы в Kotlin, Coroutines, архитектуре и тренирую ответы на собеседованиях в Яндекс, Авито и других сильных компаниях.

Вопросы про BottomNavigationView — это проверка не API, а того, как вы принимаете решения: когда панель уместна, как связать её с навигацией и какие границы задаёт Material. Более 100 учеников и разборов в моей практике показывают: именно на «простых» компонентах кандидаты с опытом 1–2 года застревают, когда дело доходит до объяснений.

Хотите, чтобы код говорил за вас на собеседовании?

Бесплатный разбор: пришлите код или резюме — посмотрю и скажу, что мешает перейти на следующий грейд.

Итоги

Итог по BottomNavigationView:

  • Компонент — панель для 3–5 top-level разделов, наследуется от NavigationBarView; пункты задаются menu-ресурсом с иконками и подписями.
  • Связка с экранами — слушатель setOnItemSelectedListener для простых случаев или setupWithNavController для корректного бэкстека и состояний.
  • Правила Material — 3–5 пунктов, иконка обязательна, отображение подписей зависит от количества; в Material 3 — компонент Navigation Bar на той же базе.

Главное, что нужно запомнить

Сильный ответ звучит так: «BottomNavigationView — панель нижней навигации для 3–5 главных разделов, пункты живут в menu-ресурсе, а связывается она с экранами через setOnItemSelectedListener или setupWithNavController; правила Material запрещают больше пяти пунктов и смешивание с вкладками». Этого достаточно, чтобы закрыть вопрос.

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