안드로이드

[Kotlin/Android] NavigationUI, Bottom Navigation Hide NavigationUI

란서 2022. 2. 7. 12:50

계속 업데이트 중.

 

참고

https://velog.io/@eoqkrskfk94/Jetpack-Navigation-Graph%EB%A1%9C-bottom-navigation-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0

 

Jetpack Navigation Graph로 bottom navigation 설정하기

Jetpack Navigation Graph를 이용해 bottom navigation 만드는 방법

velog.io

https://developer.android.com/guide/navigation/navigation-ui

 

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

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

developer.android.com

 

 

Update UI Component With NavigationUI

우리는 UI Component(프래그먼트 및 액티비티)가 다른 UI Component로 이동할 때 유저들에게 더 친숙하고 유용한 탐색 방법을 제공할 필요가 있다. 이를 위해 NavigationUI를 사용할 수 있다.

Top App Bar

 

Navigation Drawer
Bottom Navigation

 

네비게이션 Component는 NavigationUI Class를 포함한다. 해당 클래스는 위 이미지에서 보이는

 

"Top App Bar",

"Navigation Drawer",

"Bottom Navigation"

 

과 같은 네비게이션을, 관리할 수 있는 static method를 사용할 수 있다.

 


Tie Destination To Menu Item

 

NavigationUI class는  Navigation Drawer 또는 Bottom NavigationView 와 같이 Menu 와 Menu item을 필요로 하는 UI 와 목적지 UI를 연관시키는 helper method를 제공한다.

 

  • onNavDestinationSelected()

    public static final boolean onNavDestinationSelected(MenuItem item, NavController navController)​
    MenuItem과 menu item과 연관된 목적지를 host하는 NavController를 매개변수로 필요로 하는 메서드.

    MenuItem.onNavDestinationSelected(navController)​
     
    로 사용할 수도 있다.

    해당 메서드가 호출되면, Menu Item의 id와 같은 id를 가진 목적지 UI로 아이템 클릭을 통해 이동할 수 있다.
    (따라서 반드시 NavgiationUI가 포함하는 destination의 id와 Menu item의 id가 같아야지만 가능.)


    //In Navigation.xml
    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        ... >
    
        ...
    
        <fragment android:id="@+id/details_page_fragment"
             android:label="@string/details"
             android:name="com.example.android.myapp.DetailsFragment" />
    </navigation>


    //In Menu.xml
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    
        ...
    
        <item
            android:id="@+id/details_page_fragment"
            android:icon="@drawable/ic_details"
            android:title="@string/details" />
    </menu>

Bottom Naviagtion

유저가 화면 하단에 위치한 메뉴 아이템을 선택할 때 NavController가 자동적으로 onNavDestinationSelected()를 호출하여 선택된 Item에 따라 UI를 자동적으로 업데이트 한다.

 

  1. main_activity.xml 파일에 bottom Navigation View 만들기.

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <androidx.fragment.app.FragmentContainerView
                .../>
    
            <com.google.android.material.bottomnavigation.BottomNavigationView
                android:id="@+id/bot_naiv_menu"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:labelVisibilityMode="labeled"
                app:menu="@menu/activity_main_bot_navi"
                />
    
        </androidx.constraintlayout.widget.ConstraintLayout>​


  2. Bottom Navigation View의 menu와 연결해줄 activity_main_bot_navi.xml 만들기.

    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:id="@+id/fragment_solaroid_gallery"
            android:icon="@drawable/gallery"
            android:enabled="true"
            android:title="갤러리"/>
    
        <item
            android:id="@+id/fragment_solaroid_frame"
            android:icon="@drawable/frame"
            android:enabled="true"
            android:title="액자식"/>
    
    </menu>​
    ※ Menu item의 id는 navgiation.xml 파일 내 destination들의 id와 일치해야 한다.



  3. MainActivity class, onCreate() 메서드에서 navigationUI와 NavController를 연동해주기.

    setupWithNavController(NavigationView, NavController) 메서드를 이용한다.
    (NavigationView.setupWithNavController(NavController) 도 가능.)

    val navHostFragment = binding.navHostFragment.getFragment<NavHostFragment>()
    //val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    
    val navController = navHostFragment.findNavController()
    binding.botNaivMenu.setupWithNavController(navController)​


    NavHostFragment 타입의 값 가져오는 방법.

    ① supprotFragmentManager.findFragmentById(R.id.~~) as NavHostFragment 사용.

    //데이터 바인딩 시
    ② binding.~~~.getFragment<NavHostFragment>() 사용.



    1~3과정을 거치면 menu item이 선택됐을 때 (Selected) onNavDestinationSelected()가 자동으로 호출되고 menu item의 id와 같은 destination을 찾아 이동한다.


  4. BottomNavigation UI를 설정한 app의 모습




하단에 위치한 menu item을 클릭할 때 이와 연동된 fragment로 이동하는 모습.

 

 


Listener for Navigation Events

 

우리는 NavigationView가 특정 Fragment로 이동했을 때만 보여지거나 안보여지길 원할 수 있다. 이러한 로직을 구현하기 위해서는 Navigation events listener를 이용해야 한다.

앞에서 보았듯이 NavController는 NavHost(Fragment)가 가진 내용물(current Destination : Fragments)을 새로운 내용물(other destination : Fragments) 로 교체하는 역할을 한다.

대부분의 경우, NavigationUI는 NavHost의 내용물에 포함되지 않는다. 따라서 NavController에 의해 UI가 다른 목적지로 이동할 때 NavigationUI는 별도로 로직을 구성해야만 한다.

NavController는 onDestinationChangedListener()를 제공하는데 이러한 리스너는 NavigationUI의 View상태를 바꾸거나 이벤트 설정을 해주고 싶을 때 유용하게 사용될 수 있다. 

  • onDestinationChangedListener(NavController, NavDestination, Bundle) 

    해당 리스너는 navController의 현재 도착지(current destination) 또는 navHost가 가진 argument가 바뀌면 호출된다. 

    새로운 메소드를 해당 리스너에 추가하고 싶다면, addOnDestinationChangedListener() 메서드를 사용한다.


  • addOnDestinationChagnedListener(onDestinationChangedListener)

    해당 리스너와 람다식을 이용하여 다음과 같은 로직을 구현할 수 있다. 

    - 특정 fragment에서 NavigationUI 숨기기.

    ① destination id 사용.
    //In MainActivity, onCreate()
    navController.addOnDestinationChangedListener{_,destination,_ ->
        if(destination.id == R.id.fragment_solaroid_create || destination.id == R.id.fragment_solaroid_detail) {
            binding.botNaivMenu.visibility = View.GONE
        } else {
            binding.botNaivMenu.visibility = View.VISIBLE
        }
    }​
     
    해당 코드는 navController가 이동하는 도착지의 id에 따라 NavigationUI의 visibility를 설정하는 방식이다.

    ② argument사용.

    argument를 사용하는 방식은 navHost과 연결된 xml파일에 argument와 default값을 정의한 뒤 이를 이용하는 방식이다. 
    <navigation 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"
        app:startDestination="@id/fragment_solaroid_gallery">
    
    	<fragment
            android:id="@+id/fragment_solaroid_gallery"
            android:name="com.example.solaroid.solaroidgallery.SolaroidGalleryFragment"
            android:label="SolaroidGalleryFragment"
            tools:layout="@layout/fragment_solaroid_gallery">
    
            <argument
                android:name="ShowAppBar"
                android:defaultValue="true"/>
    
        </fragment>
    
        <fragment
            android:id="@+id/fragment_solaroid_frame"
            android:name="com.example.solaroid.solaroidframe.SolaroidFrameFragment"
            android:label="SolaroidFrameFragment"
            tools:layout="@layout/fragment_solaroid_frame">
    
    
            <argument
                android:name="ShowAppBar"
                android:defaultValue="true"/>
    
        </fragment>
        
    </navigation>​
    따라서 다음과 같이 <fragment> 내에 <argument>를 설정하는데, 이와 같은 argument는 네비게이션끼리 이동할 때 주고받을 수는 없다. (type이 명시되어 있지 않기 때문에?)

    //In MainActivity class, onCreate()
    navController.addOnDestinationChangedListener{_, _, argument ->
    	binding.botNaivMenu.isVisible = argument?.getBoolean("ShowAppBar",false) == true
    }

    -getBoolean(String, defalutValue) : 매개변수 String이 argument의 name이 맞는지 확인 후, 맞다면 true를 , 그렇지 않다면 defaultValue로 설정한 값 (해당 코드에서는 Boolean type의 false)을 반환. 


  • 결과

 

설정해준 fragment에서는 NavigatioUI가 나타나지만, 그렇지 않은 fragment에서는 사라지는 모습.