[Android / Kotlin] Jetpack Navigation

Subeen·2024년 2월 7일
0

Android

목록 보기
57/73

Jetpack Navigation

기존에는 Fragment를 전환하려면 supportFragmentManager로 FragmentManager 인스턴스를 반환 받고 add나 replace 를 통해 Fragment를 교체하고 commit으로 직접 transaction을 실행했어야 했다.
그런데, Jetpack이 도입 되면서 화면 전환에 FragmentManager 직접 사용하는 것보다 Navigation Library를 사용하는 것을 권장하고 있다.

Navigation의 3대 컴포넌트

Navigation graph는 네비게이션 에디터를 사용하거나 xml을 직접 수정함으로써 앱에서 사용 할 모든 화면과 연관 관계, 이동 방법을 정의하게 된다.

Navigation host는 Navigation graph에서 정의한 Fragment를 화면에 표시하기 위한 컨테이너이다.
컨테이너를 배치할 레이아웃 파일에 FragmentContainerView를 추가하면 된다. (android:name="androidx.navigation.fragment.NavHostFragment")
이 때, 안드로이드 네임 속성에 의해서 View가 navhost Fragment에 연결되게 된다.
app:defaultNavHost 속성에 의해 네비게이션 호스트가 시스템의 백버튼 입력을 가로채서 이전 화면으로 전환할 수 있게 한다.
app:navGraph="@navigation/nav_graph"로 네비게이션 그래프를 지정해 주게 된다.

Navigation controller는 화면 전환을 수행하는 컨트롤러이다. 모든 네비게이션 호스트는 하나의 네비게이션 컨트롤러를 가지게 된다.
프레그먼트를 전환하기 위해서는 네비게이션 호스트로부터 네비게이션 컨트롤러의 인스턴스를 얻어서 네비게이션 메소드를 실행하게 된다.

// 네비게이션 컨트롤러의 인스턴스를 프레그먼트 안에서 얻기 위함 
navController = findNavController()

// 네비게이션 컨트롤러의 인스턴스를 액티비티 안에서 얻기 위함 
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController

Fragment간 데이터 전달

기존에는 프레그먼트 사이에서 데이터를 전달하는 데에는 key-value 타입의 Bundle을 사용했었다. 하지만, Bundle은 타입 안정성을 보장하지 않는 한계가 있다. 이를 개선하기 위해 NavigationComponent에서는 SafeArgument 개념을 도입해서 데이터를 안전하게 전달 할 수 있도록 한다.

Safe Args를 사용하여 Fragment간 데이터 전달하기

  • Safe Argument가 활성화 되면 세 종류의 클래스가 자동으로 만들어지게 된다.
    • Navigation의 action이 시작되는 대상의 이름에 Directions 라는 접미어가 붙은 클래스
    • argument를 전달하는데 사용한 action의 이름과 동일한 이름의 클래스
    • 데이터를 수신하는 대상의 이름에 Args라는 접미어가 붙은 클래스
// 아래와 같이 인수를 설정하고 navigate 메소드에 전달함으로써 데이터를 전달 할 수 있게 된다. 
val amount = binding.amountTv.text.toString().toInt()
val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
view.findNavController().navigation(action)

Jetpack Navigation 사용하기

dependency

  • navigation dependency를 추가한다.
dependencies {
	...
    
    // Navigation
    implementation("androidx.navigation:navigation-fragment-ktx:2.7.6")
    implementation("androidx.navigation:navigation-ui-ktx:2.7.6")
}
  • Bottom Navigation View를 설정하기 위해 Navigation Graph를 만들어준다.
    • res > New > Android Resource File 을 클릭하여 파일을 만들어주는데 File name을 입력하고 Resource typeNAvigation으로 설정한다.

각 Fragment의 id와 label을 설정한다.

  • id는 menu에서 설정한 Fragment의 id와 동일하게 설정해야 한다.

View

  • Fragment의 Navigation을 수행할 View를 추가한다.
    • FrameLayout 안에 FragmentContainerView를 추가한다.
    • FragmentContainerView가 Navigation host가 되어서 Fragment들을 보여주는 표시를 이 곳에서 관리한다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/bottom_navigation_view"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">


        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />

    </FrameLayout>


    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation_view"
        android:layout_width="0dp"
        android:layout_height="66dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/bottom_navigation_menu" />

</androidx.constraintlayout.widget.ConstraintLayout>
class MainActivity : AppCompatActivity() {
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        setUpJetpackNavigation()
    }

    private fun setUpJetpackNavigation() {
		// Navigation Controller 인스턴스를 취득 
        val host = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = host.navController
       /*
        * setupWithNavController를 사용하여 Bottom Navigation View를 Navigation Controller와 연결
        * Navigation이 Fragment 전환을 수행한다. 
        */

       binding.bottomNavigationView.setupWithNavController(navController)
    }
}

Safe Argument로 데이터 전달하기

dependency

  • Safe Argument 사용을 위해 dependency를 추가한다.
// project build.gradle
plugins {
	...
    id("androidx.navigation.safeargs.kotlin") version "2.5.0" apply false

}
// app build.gradle 
plugins {
	...
    id("androidx.navigation.safeargs.kotlin")
}

데이터를 전달 받을 Fragment 생성

데이터를 전달 받을 DetailFragment를 추가하고, Bottom Navigation의 탭에서 선택시 DetailFragment로 이동 및 데이터 전달을 수행하기 위해 화살표를 만들어 연결한다.

위에서 생성한 DetailFragment에서 Argument를 받을 수 있도록 설정한다.
Arguments 탭에서 플러스 버튼을 클릭하면 다음과 같은팝업이 나타나는데, 전달 받을 데이터의 Name과 Type을 설정한다.

Fragment

// HomeFragment
    private fun initView() {
        binding.btSendData.setOnClickListener {
            // DetailFragment로 데이터 전달 및 이동 
            val action = HomeFragmentDirections.actionFragmentHomeToFragmentDetail("test")
            findNavController().navigate(action)
        }
    }
// DetailFragment
    private fun initView() {
        // HomeFragment 로부터 전달 받은 데이터
        val id = args.id
        Log.d("TAG", "HomeFragment: $id") // test
    }
profile
개발 공부 기록 🌱

0개의 댓글