필자가 저번에 AAC Navigation을 사용하여 Fragment 간의 이동을 구현한 예제를 만들어 봤었다.
해당 예제에서는 간단한 버튼을 만들고, 버튼을 통해서 Fragment를 변경하도록 구현을 했었는데,
BottomNavigationView를 사용하여 Fragment 를 변경하고자 하니 정상적으로 동작이 되지 않았다.
따라서, AAC Navigation과 BottomNavigationView를 같이 사용하는 방법에 대해서 작성해볼까 한다.
글을 작서하기 앞서, AAC Navigtation에 대한 기본적인 사용 방법은 해당 게시글에서 확인하길 바란다.
우선,
Fragment를 생성하고, nav_graph를 생성하는 등
기본적인 AAC Navigtation을 사용하는 것과 마찬가지로 설정을 해둔다.
그 후, Fragment 간의 이동을 구현할 부분에서 BottomNavigationView를 사용한 Layout을 생성해 준다.
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavi"
android:layout_width="match_parent"
android:layout_height="57dp"
... />
그 다음, BottomNavigationView에서 보여줄 아이템들을 res/menu 폴더에 생성해준다.
menu 폴더가 없을 경우, res 폴더 밑에 menu 폴더를 생성 한 후 menu 아이템을 작성할 xml 파일을 만들면 된다.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/bottom_item_first"
android:icon="@drawable/ic_launcher_foreground"
android:title="first" />
<item
android:id="@+id/bottom_item_second"
android:icon="@drawable/ic_launcher_foreground"
android:title="second" />
</menu>
icon은 마땅히 넣어둔 이미지가 없어서 기본적으로 가지고있는 launcher 이미지를 사용하도록 하였다.
Menu Item도 생성해 주었으면, BottomNavigationView에서 해당 아이템들을 클릭했을 때 Fragment를 변경하도록 구현해주면 된다.
우선,
BottomNavigationView에 대한 클릭 이벤트를 추가해 주도록 하자.
bottomNavi.setOnClickListener {
when (it.id) {
R.id.bottom_item_first -> {
it.findNavController().navigate(R.id.move_first_fragment)
}
R.id.bottom_item_second -> {
it.findNavController().navigate(R.id.move_second_fragment)
}
else -> {
Logger.d("onClick else ..")
}
}
}
이처럼 onClickListener를 사용하여 구현을 하게 되면,
정작 해당 아이템들을 클릭했을 때 이벤트를 이곳에서 받지 못한다.
따라서,
bottomNavi.setOnItemSelectedListener {
when (it.itemId) {
R.id.bottom_item_first -> {
true
}
R.id.bottom_item_second -> {
true
}
else -> {
false
}
}
}
이처럼 setOnItemSelectListener를 사용하여 클릭 이벤트를 잡아주어야 한다.
하지만,
setOnItemSelectListener를 사용하게 되면 기존의 onClickListener를 사용했을 때 처럼
it.findNavController().navigate()
를 사용할 수 없다.
it에는 View, Activity, Fragment가 들어갈 수 있는데 setOnItemSelectListener에서의 it은 MenuItem이기 때문이다.
따라서, 다른 방법을 통해 이동할 수 있는지 확인해 보았다.
Navigation.createNavigateOnClickListener(R.id.move_first_fragment)
와 같이 선언하여 이동이 가능하다고 한다.
nav_graph의 Action에 정의된 ID 값으로 리스너를 반환하여 이동시키는 방법이라고 하는데,
해당 라인만 추가해서는 정상적으로 동작하지 않았다.
다른 추가적인 작업이 필요할 수 있고 필자가 잘못 작업하여 동작이 되지 않았을 수 있지만,
여러모로 구글링을 해보고 적용해 보아도 정상적으로 동작하지 않았다.
따라서, 필자는 다시한번 AAC Navigation 관련한 Developers 문서를 확인해 보았는데
그 곳에서 해결 방안을 찾을 수 있었다.
(역시 공식 문서에는 없는게 없는것 같다.)
공식문서를 확인하면 다음과 같이 정의되어 있다.
NavigationUI 에는 연결된 대상을 호스팅하는 NavController와 함께 MenuItem을 받는 도우미 메서드 onNavDestinationSelected()가 포함되어 있습니다.
MenuItem의 id가 대상의 id와 일치하면 NavController가 그 대상으로 이동할 수 있습니다.
즉, MenuItem을 사용하는 setOnItemSelectListener에서는 onNavDestinationSelected()를 사용하면 된다는 뜻이다.
또한, BottomNavigation 관련한 부분에는 다음과 같이 정의되어 있다.
NavigationUI는 하단 탐색도 처리할 수 있습니다.
사용자가 메뉴 항목을 선택하면 NavController는 onNavDestinationSelected()를 호출하고 하단 탐색 메뉴에서 선택된 항목을 자동으로 업데이트합니다.
즉, NavController와 onNavDestinationSelected를 사용하여 자동으로 fragment를 변경할 수 있다는 것이다.
그렇다면, 사용하는 방법에 대해서 확인해보자.
Developers 문서의 스니펫에서는 다음과 같이 구현되어 있다.
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_main)
...
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
findViewById<BottomNavigationView>(R.id.bottom_nav)
.setupWithNavController(navController)
}
onCreate 시 FragmentManager를 사용하여 navHostFragment를 구하고,
navHostFragment를 사용하여 navController를 구하여 해당 BottomNavigationView에 controller를 설정해주면 되는 것 같다.
필자는 해당 부분을 따로 함수로 빼내고, onCreate에서 호출하도록 구현하였다.
private fun initNavigation() {
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
findViewById<BottomNavigationView>(R.id.bottomNavi).setupWithNavController(navController)
bottomNavi.setOnItemSelectedListener {
Logger.d("bottomNavi setOnItemSelectedListener .. $it\n${it.itemId}\n${it.actionView}")
when (it.itemId) {
R.id.first_fragment -> {
}
R.id.second_fragment -> {
}
}
it.onNavDestinationSelected(navController)
}
}
우선, setOnItemSelectListener에서 when절을 통해 분기처리를 하지 않아도 상관 없다.
it.onNavDestinationSelected(navController)
을 통하여 클릭된 메뉴에 따라 자동적으로 Fragment를 변경시켜주기 때문이다.
하지만, Fragment를 변경할 때 별도의 작업이 필요할 경우가 있을 것 같아서 임시로 when 절을 작성해 주었다.
위 처럼 controller를 설정을 해두고,
setOnitemSelectListener 구문에 it.onNavDestinationSelected()만 호출하도록 구현 한 다음 실행을 해보면
정상적으로 원하는대로 Fragment가 변경되는 것을 확인 할 수 있다.
클릭 이벤트를 받을 수 있을 때와 없을 때 Navigation을 사용해 Fragment를 이동하는 동작이 다르게 구현될 것이라고는 생각을 못했었다.
역시 어떠한 것을 적용하기 앞서, 구글링을 통해 사용 방법을 확인하는 것 보다 앞서 Developers 문서를 통해서 찾아보는 것이 좋을 것 같다.
기존에 버튼을 클릭했을 때 Fragment를 이동하는 방식과 BottomNavigationView를 사용하여 Fragment를 이동하는 방식 두 가지를 사용한다면, 실 구축시에 기본적인 UI 구현에는 큰 문제가 없을 것 같다.
Developers 문서를 확인해보니 다양한 사용 방법이 나와있는데, 추후 필요할 때 한번 더 정독을 한 후 작업을 진행해야 할 것 같다.
해당 게시글에 작성한 내용이 작성되어 있는 Developers 문서 확인이 필요하다면, 다음 링크를 확인하길 바란다.
https://developer.android.com/guide/navigation/navigation-ui?hl=ko#bottom_navigation
'Android > AAC' 카테고리의 다른 글
[Android] AAC (Android Architecture Components) 란 ? (Feat. ViewModel) (0) | 2022.05.31 |
---|---|
[Android] Jetpack DataStore 실전 압축 정리 (0) | 2022.02.17 |
[Android] AAC Navigation 사용 실전 압축 정리 (0) | 2022.02.14 |
[Android] 데이터 바인딩 어댑터 (Data Binding Adapter) 사용 방법 (0) | 2022.02.07 |
[Android] Room 라이브러리 (0) | 2020.06.22 |