Android

Jetpack Navigation 총정리 1탄

seokzoo 2022. 9. 19. 04:32
반응형

제목에 일부로 총정리라고는 적었지만, 사실 자주 잊어버리는 것에 대해서 정리하기 위해서 작성한 포스트이니, 없는 것에 대해서는 댓글 남겨주시면 공부해서 올려놓겠습니다!

또한 해당 포스트는 Kotlin 버전에 대해서만 작성하며, Java는 작성하지 않습니다!

현재 포스트 정리 목록 - 2022.09.19

  • Jetpack Navigation은 뭐죠?
  • navController란? navController의 사용법은?
  • safe Args란?
  • 데이터 전달하기
  • 간단한 Global action이란?

Jetpack Navigation은 뭐죠?

이전에 fragment 간의 이동에는 fragmentManager를 이용해서 이동하곤 했습니다. 아래와 같이요!

parentFragmentManager.commit {
          replace<SecondFragment>(R.id.fc) }

parentFragmentManager 또는 supportFragmentManager를 이용해 fragmentManager를 호출한 뒤 commit이라는 확장함수를 통해 transaction 처리를 해 프래그먼트를 변경해주곤 했습니다.

그런데 Jetpack Navigation(이하 Navigation)은 fragmentManager를 호출하는 것이 아닌, navController를 이용해 navigate 함수와 함께 프래그먼트의 이동을 합니다.

물론 NavController뿐 아니라, Navigation graph, NavHost도 필요합니다.


Navigation graph는 프래그먼트 간의 이동 정보, 전달할 수 있는 argument, animation 등 여러 정보를 담고있는 xml 그래프입니다.

NavHost는 navigation graph에서 대상을 표시하는 빈 컨테이너로, 프래그먼트 대상을 표시하는 기본 NavHost의 구현인 NavHostFragment를 포함한다고 합니다. 말이 어려운데... fragmentContainerView에 보통 지정해주며, 프래그먼트가 표시되는 기본 컨테이너라고 생각하면 될 것 같습니다.

NavController는 NavHost 내에서 앱 탐색을 관리하는 객체로 이를 이용해 이동을 시켜주는 역할입니다.

한줄로 요약하자면, 프래그먼트의 이동 경로, 정보들이 Navigation graph에 표시되어 있고, 이 이동 경로 대로 이동할지, 또는 그냥 다른 프래그먼트로 이동할지에 대해서 NavController에 전달해주면, NavController가 NavHost에 적절한 프래그먼트를 보여주는 방식으로 작동합니다!

그런데, 이렇게 복잡하고 많은걸 왜써?

라고 생각하실 수 있습니다!
구글에서 말하는 Navigation의 장점을 나열해보자면,

  • fragment transaction(전환, 이동) 처리
  • 기본적으로 뒤로가기, 위로 가기(앱 상단 메뉴에서의 뒤로가기) 작업을 올바르게 처리합니다.
  • 프래그먼트 애니메이션, 전환에 대한 표준화된 리소스 제공
  • 딥링크 처리
  • 바텀 네비게이션, 드로어블 메뉴 등 최소한의 작업으로 네비게이션 작업(전환) 수행 가능
  • Safe Args를 통해 기존 Bundle을 통한 nullable한 데이터 전달이 아닌, non nullable할수도, 아닐수도 있는(선택이 가능한) type safety한 데이터 전달이 가능합니다.
  • ViewModel 지원(navGraphViewModels()라는 ViewModel이 존재하며, navGraph간의 데이터 공유가 가능합니다.)

이렇게나 다양한 장점이 존재합니다.

구글에서 나열해준 장점 외에도 제가 느낀 장점을 말해보자면,
프래그먼트의 stack 관리가 쉽고, Navigation Graph 덕분에 프래그먼트간의 이동 그래프가 명확하게 UI로 확인이 가능하기 때문에, 조금더 앱의 플로우나 의도가 명확하게 보인다는 것이 큰 장점이라고 생각합니다.
또한 최근 compose도 지원하기 때문에, 안쓸 이유가 없겠지요!

사용하기 전에 Build.gradle(Module)에 추가해줍시다!

version은 최신 버전을 사용하도록 합니다. (2022.09.19 기준 2.5.2)

NavHost

NavHost를 먼저 지정해줍시다! (저는 그게 편한데... 아니여도 됩니다)
이전에 말했듯 NavHost는 container 역할로 프래그먼트를 담습니다.
(참고 - 요새는 one activity, multiple fragment를 만들 수 있으면 좋겠지만, one activity로는 해결하지 못하는 경우도 분명히 있습니다. 그럴 때에는 추가적인 activity가 필요한데, 각각의 activity는 NavHost에 연결된 Navigation Graph가 반드시 필요합니다. 즉, activity마다 한개 이상의 Navigation Graph를 가지고 있어야 한다는 이야기 입니다.)

<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">

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

</androidx.constraintlayout.widget.ConstraintLayout>

먼저 설명을 하자면, FragmentContainerView를 선언해주고, 그안에 name 은 반드시 위와 같이NavHostFragment를 써주고, defaultNavHost = true 그리고 navGraph에 nav_graph를 지정해줍니다.(navigation graph의 이름이므로, 다른 이름으로 지정해주어도 됩니다.)

여기서 defaultNavHost = false로 지정해주게되면, back key를 눌렀을 때, 이전 화면으로 넘어가지 않고 앱이 종료됩니다. 또한 하나의 xml 안에 fragmentContainerView가 2개인 경우, 한개만 defaultNavHost를 true로 해주어야 합니다.

안드로이드 Developer 에서는 navigation graph를 먼저 만드는 방법을 알려줍니다. 다만 이 과정을 안다는 가정하에 navGraph="@navigation/nav_graph"를 입력하고, 빨간줄에 alt+enter를 사용하면 자동으로 navigation/nav_graph를 생성해줍니다! 그래서 저는 NavHost먼저 작성하곤 합니다.

Navigation Graph

Navigation Graph는 프래그먼트의 다양한 정보를 가진 그래프입니다!

코드를 split 했을 때, 상단의 +버튼을 클릭해서 프래그먼트를 추가해줄 수 있습니다.
first fragment와 second fragment 를 추가해주었습니다.

여기서 중요한 점은 id와 name, 그리고 최상단에 startDestination과 id를 반드시 추가해주어야 합니다. label은 프래그먼트의 이름이라고 생각하시면될 것 같습니다.

tools:layout을 통해 레이아웃을 연결해주면, 디자인 탭에서 실제 layout을 볼 수 있으니 꼭 연결해주시면 좋습니다! (필수 사항은 아님)

후에 마우스로 프래그먼트를 연결해주면, action이라는 프래그먼트의 이동 정보가 생깁니다.

각각의 프래그먼트에 구분하기 쉽게 TextView로 Fragment 이름을 명시해주었습니다.

선을 연결해주니, action이 생기고 id와 destination이 생깁니다. id를 마음대로 변경해줄 수도 있습니다.

여기까지 진행했다면, 다른 프래그먼트로 이동은 하지 못해도, 실행시에 First Fragment가 나타나는 것을 볼 수 있습니다.

NavController

결국 프래그먼트를 이동하기 위해선 NavController가 필요합니다. 그래서, destination으로 navigating 하기 위해서는 findNavController라는 함수가 필요한데, 이는 확장함수로 선언이 되어있습니다.

즉 프래그먼트에서 그냥 findNavController를 사용하거나, View를 찾아 View.findNavController를 사용해도 됩니다.

이제 First Fragment에서 Second Fragment로 버튼을 클릭시에 넘어가는 법을 보겠습니다.
First Fragment layout에 버튼을 만들고 click Listener를 달았습니다.

그안에서 findNavController 를 통해 Seocnd Fragment로 Navigate 하는데, 이때 인자로 아까의 action의 id를 적어주면 됩니다. (쉽죠)

 view.findViewById<Button>(R.id.move_button).setOnClickListener {
            findNavController().navigate(R.id.action_firstFragment_to_secondFragment) }

safe args를 가지고 navigate 하기

먼저 safe args를 사용하려면, 프로젝트 build.gradle에 다음과 같이 추가해줍니다.

그리고 모듈단의 plugins에 다음을 추가해줍니다.

이후에 safe args를 사용할 수 있게 되는데, 자동으로 생성되는 클래스의 이름이 정해져있습니다.

만약, A fragment에서 B fragment로 이동한다고 가정할 때,
AFragmentDirections라는 파일이 생성되고, navigation graph에서 지정한 id로 함수가 생성됩니다.

id가 만약 action_firstFragment_to_secondFragment 라면 actionFirstFragmentToSecondFragment() 라는 함수가 생깁니다.

즉 아래와 같이 사용하면 됩니다!

view.findViewById<Button>(R.id.move_button).setOnClickListener {
            val action = FirstFragmentDirections.actionFirstFragmentToSecondFragment()
            findNavController().navigate(action)
        }

데이터 전달

그런데 만약 safe args, 즉 인자를 전달해준다면 아래와 같이 변경됩니다!

navigation graph에 받는 쪽에 argument를 지정해줍니다.

<fragment
        android:id="@+id/secondFragment"
        android:name="com.example.myapplication.SecondFragment"
        android:label="SecondFragment"
        tools:layout="@layout/fragment_second">

        <argument
            android:name="number"
            android:defaultValue="0"
            app:argType="integer" />
    </fragment>

그렇게 되면, 자동으로 인자를 넣어서 전달할 수 있게 됩니다.

후에 SecondFragment에서 받아서 사용할 수 있는데, 아래와 같이 사용합니다.

받는 쪽의, 즉 SecondFragmentArgs 라는 파일이 생성되고, 전달해준 1을 받아서 SecondFragment의 textView에 지정해주면 잘 나오는 것을 확인할 수 있습니다.

전달해줄 수 있는 타입은, 아래서 확인 하시면 됩니다~! (기본 타입, parcelable, serializable, enum type, reference 전달 가능)

defaultValue로 null을 주고싶다면 @null을 주면 됩니다.

또한 간편하게 bundle로도 데이터를 전달할 수 있는데, 아래와 같이 간편하게 넘겨도 됩니다.

추가적으로

global action이란, navigation 최상단에 action을 추가하여, 공통 action을 추가하는 것을 말하는데,
공통 action을 통해 argument를 전달하기 위해서는 navigation에 id를 반드시 지정해주어야 합니다.

이후 정리 목록

  • Nested graphs
  • toolbar(app bar)
  • bottom navigation
  • back stack 관리
반응형