본문 바로가기

Android/AAC

[Android] AAC Navigation + BottomNavigationView 사용 방법.

728x90

필자가 저번에 AAC Navigation을 사용하여 Fragment 간의 이동을 구현한 예제를 만들어 봤었다.

해당 예제에서는 간단한 버튼을 만들고, 버튼을 통해서 Fragment를 변경하도록 구현을 했었는데,

BottomNavigationView를 사용하여 Fragment 를 변경하고자 하니 정상적으로 동작이 되지 않았다.

 

따라서, AAC Navigation과 BottomNavigationView를 같이 사용하는 방법에 대해서 작성해볼까 한다.

글을 작서하기 앞서, AAC Navigtation에 대한 기본적인 사용 방법은 해당 게시글에서 확인하길 바란다.

https://heegs.tistory.com/63

 

[AAC] AAC Navigation 사용 실전 압축 정리

클린 아키텍처 예제를 작업 한 이후, 다양한 기술들을 적용시켜보고 있던 와중, Fragment 간의 이동을 아주 편리하게 도와주는 AAC Navigation 이 있길래 사용해보있다. AAC Navigation 에 대한 개념 정리글

heegs.tistory.com


우선,

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 

 

NavigationUI로 UI 구성요소 업데이트  |  Android 개발자  |  Android Developers

NavigationUI로 UI 구성요소 업데이트 탐색 구성요소에는 NavigationUI 클래스가 포함되어 있습니다. 이 클래스에는 상단 앱 바, 탐색 창 및 하단 탐색으로 탐색을 관리하는 정적 메서드가 포함되어 있

developer.android.com

 

728x90