[Из песочницы] Создание Bottom Navigation Bar на Kotlin с использованием Anko

Начиная новый проект я решила попробовать полностью отказаться от XML-файлов, на основе которых формируются layouts, а экраны создавать с помощью библиотеки Anko. Имея небольшой опыт разработки под Android (порядка 2-х лет) и еще меньший опыт написания кода на Kotlin (чуть больше полугода) я сразу столкнулась с проблемой включения в приложение Navigation Architecture Component, а вернее отрисовкой Bottom Navigation Bar, создаваемом BottomNavigationView.

Первым делом я обратилась к просторам Инета, чтобы найти возможное решение. Но все статьи найденные мной с той или иной степенью доходчивости рассказывали о том как работать с компонентами навигации и никто (из тех чьи статьи я проштудировала) не делал это на Anko. Решив поставленную задачу, я предлагаю сообществу свой вариант создания Bottom Navigation Bar.

Полностью весь код можно посмотреть здесь
Я пропускаю этап создания нового проекта в Android Studio, отмечу только что для работы с Anko и Navigation Architecture Component в build.gradle на уровне модуля необходимо добавить следующие зависимости:

implementation "org.jetbrains.anko:anko:$anko_version"
implementation "org.jetbrains.anko:anko-constraint-layout:$anko_version"
implementation "com.android.support.constraint:constraint-layout:2.0.0-alpha3"

implementation 'android.arch.navigation:navigation-fragment:1.0.0-beta02'
implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0-beta02'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-beta02'

implementation 'com.google.android.material:material:1.0.0'

Следующий шаг — создаем структуру будущего приложения. Для отрисовки основной активити вместо xml-файла создаем класс MainActivityUI наследуемый от интерфейса AnkoComponent:

class MainActivityUI: AnkoComponent {
    override fun createView(ui: AnkoContext): View = with(ui) {
        constraintLayout {  }
    }
}

В классе MainActivity setContentView (R.layout.activity_main) заменяем на MainActivityUI ().setContentView (this).

Затем создаем package fragments в котором будут лежать наши фрагменты и package ui для размещения классов, отвечающих за отрисовку экранов соответствующих фрагментов. Так выглядит структура проекта:

fragments
        ui
            HomeUI
            UsersUI
            DetailsUI
            MoreUI
        HomeFragment
        UsersFragment
        DetailsFragment
        MoreFragment

Теперь займемся непосредственно навигацией и созданием Bottom Navigation Bar.
Подробное описание включения новых компонентов навигации и описание работы в Navigation Editor можно найти на странице документации здесь. Для создания файла (graph«а) навигации между экранами приложения, необходимо в папку res добавить еще одну папку, а именно navigation и уже в неё добавить файл navigation_graph.xml. Для этого проекта он будет таким:



    
        
    

    
        
    

    
        
    

    

Для отображения самого Bar«а необходимо создать еще одну ресурсную папку, а именно menu. В ней располагается файл отвечающий за видимую часть bar«а. Вот так он выглядит в этом проекте:


    
    
    
    

Настало время соединить все имеющееся вместе и посмотреть как же это работает.

Добавим в MainActivityUI контейнер для фрагментов, а также определим контейнер для navigation bar.


constraintLayout {
    val fragmentContainer = frameLayout {
        id = R.id.fragment_container
    }.lparams {
        width = matchParent
        height = matchConstraint
    }
    val bottomNavigation = bottomNavigation {
        id = R.id.bottom_nav_view
        inflateMenu(R.menu.bottom_navigation_menu)
    }
    applyConstraintSet {
        fragmentContainer {
            connect(
                START to START of PARENT_ID,
                END to END of PARENT_ID,
                TOP to TOP of PARENT_ID,
                BOTTOM to TOP of R.id.bottom_nav_view
            )
        }
        bottomNavigation {
            connect(
                START to START of PARENT_ID,
                END to END of PARENT_ID,
                TOP to BOTTOM of R.id.fragment_container,
                BOTTOM to BOTTOM of PARENT_ID
            )
        }
    }
}

Особо следует указать на то, что bottomNavigation в данном примере это extantion функция, имеющая следующий вид:

inline fun ViewManager.bottomNavigation(init: BottomNavigationView.() -> Unit = {}) =
        ankoView({ BottomNavigationView(it) }, theme = 0, init = init)

Теперь в MainActivity необходимо определить объект NavHostFragment (см. в доке):

private val host by lazy { NavHostFragment.create(R.navigation.navigation_graph) }

А в методе onCreate () определить:

supportFragmentManager.beginTransaction()
    .replace(R.id.fragment_container, host)
    .setPrimaryNavigationFragment(host)
    .commit()

Завершающий штрих — добавим в onStart () MainActivity объект класса NavController, который позволяет осуществлять переход между фрагментами выбирая тот или иной объект navigation bar«а:

override fun onStart() {
    super.onStart()
    val navController = host.findNavController()
    findViewById(R.id.bottom_nav_view)?.setupWithNavController(navController)
    navController.addOnDestinationChangedListener{_, destination, _ ->
        val dest: String = try {
            resources.getResourceName(destination.id)
        } catch (e: Resources.NotFoundException) {
            Integer.toString(destination.id)
        }
        Log.d("NavigationActivity", "Navigated to $dest")
    }
}

Запускаем приложение и vois là…


Скриншот

vextlctocaf6-i5-_14wxz_kuww.png

© Habrahabr.ru