Android/Jetpack

[Navigation] Navigation (개념 및 구성요소 : graph, navhost, controller, destination, action, safe args)

네모메모 2023. 2. 26. 11:36
반응형

 

 

이건 또 왜 번역 '탐색'일까

차라리 '네비' 그 자동차 '네비' 개념이 더 와닿겠어...ㅠㅠ

 

제발 destination을  '대상'이라고 표시하지마요 

님아 Actions 

을  '작업'이라고 표시하지마요 

읽는 내내 너무 헷갈려...ㅠㅠ

 

 


 

  • 1.동사 (지도 등을 보며) 길을 찾다[방향을 읽다] => 화면을 찾는다 => 화면 이동한다 
  • 2.동사 (바다·강 등을) 항해하다
  • 3.동사 (힘들거나 복잡한 상황을) 다루다[처리하다]

 

Navigation이란? 

 

   - "여러 Fragment 들을 포함한 하나의 Activity가 있는 앱"을 위해 설계됨

      ㄴ> Fragment (<< Navigation의 'destination')을 교체하여 화면이동한다.


   -
Activity은 Navigation 'graph'와 연결됨

   - Activity는 Fragment을 교체해야 하는 NavHostFragment를 포함함.

 

   - CASE)  여러 Activity들이 있는 앱이라면 -> 각 Activity별로 자체 NavigationGraph가 있다.

 

 

 


Safe Args 란? 

- 화면 navigate를 위해 Safe Args Gradle 플러그인을 사용하는 것이 좋다. (매우 좋더라)

- 화면이동 destiation 간 유형 안전한 이동 및 인수 전달을 사용 설정하는 간단한 객체 및 빌더 클래스를 생성해준다.

 

 - 각 action마다 action이 탐색을 시작하는 destination인 각 발신 [ Directions 클래스]를 생성함
 생성된 입니다. 

- [ Directions 클래스 ]

  : Safe Args 추가 시  요소의 id 속성을 기반으로 생성되는 클래스

 

- 클래스 이름은 발신 '{destination 클래스 이름}Directions'

     ㄴ > ex1) android:id=@+id/main_nav 값이 있는 <navigation> 요소가 있다면, Directions클래스 이름은 MainNavDirections

     ㄴ >  ex2) destination의 이름이 SpecifyAmountFragment라면 생성된 클래스의 이름은 SpecifyAmountFragmentDirections 
 

 

-  생성된 클래스에는 발신 destination에서 정의한 각 action의 정적 메서드가 포함됨.
   이 메서드는

     ㄴ> @param : 정의된 action 매개변수

     ㄴ> @return  : navigate()에 전달할 수 있는 NavDirections 객체를 반환하여 화면 이동 시 사용할 수 있다.

 

 

 


- [ 프로젝트에 Safe Args를 추가하기 ]

1) 최상위 build.gradle 파일에 다음의 classpath를 포함

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.5.3"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

 

2) 앱 또는 모듈의 build.gradle 파일에 다음 행을 추가

   ㄴ> 2-1) 자바 모듈 또는 자바와 Kotlin 혼합 모듈에 적합한 자바 언어 코드를 생성하려면

plugins {
    id("androidx.navigation.safeargs")
}

   ㄴ> 2-2) Kotlin 전용 모듈에 적합한 Kotlin 코드를 생성하려면 다음을 추가하세요.

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

 

3)  gradle.properties 파일 android.useAndroidX=true가 있어야 함

 

 

 

 


[Navigation 구성]

 

1. Navigation Graph

: Navigation 관련 정보를 볼 수 있는 XML리소스 파일

- 앱의 모든 탐색 (화면 이동) 경로를 보여준다. 

- "Navigation Graph는 사용자가 이동할 수 있는 이 NavHostFragment의 모든 destination을 지정한다."

- 주로 res/navigation 폴더에 xml파일로 존재 (루트가 <navigation>태그로 시작)

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/nav_graph">

</navigation>

 

 

- 이 xml에서 스토리보드처럼 모든 플로우를 보여주고 앱내의 Fragment를 한눈에 확인가능 (아래 이미지처럼)

[ NavigationGraph 화면 구성 ]

    ㄴ> destination(=대상  화면) : 앱의 다양한 콘텐츠 영역(화면)

    ㄴ> Action (=작업, 화살표로 표시됨):   대상에서 다른 대상으로 이동할 수 있는 방법을 "화살표로 표시" (=> 사용자가 택할 수 있는 경로를 나타내는 대상 간의 논리적 연결)

 

 

[ NavigationGraph 추가하기 ]

  1. res에서  마우스 오른쪽 버튼으로 클 > New > Android Resource File을 선택 > New Resource File 대화상자
  2. File name 필드에 'nav_graph'와 같은 파일 이름을 입력
  3. Resource type 드롭다운 목록에서 Navigation을 선택한 후 OK를 클릭하면 끝

 

 


1 - A) destination

[ destination의 구성 ]

대상을 클릭하여 선택하고 Attributes 패널에서 다음 속성을 확인합니다.

<?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"
    app:startDestination="@id/blankFragment">
    <fragment
        android:id="@+id/blankFragment"
        android:name="com.example.cashdog.cashdog.BlankFragment"
        android:label="@string/label_blank"
        tools:layout="@layout/fragment_blank" />
</navigation>

 

  • 태그(ex : <fragment>) : 대destination이 소스 코드에서 프래그먼트, 활동 또는 기타 맞춤 클래스로 구현되는지 여부를 나타냄
  • android:label 필드 : 사용자가 읽을 수 있는 destination 이름
    - NavGraph setupWithNavController()를 사용하여 Toolbar에 연결한 경우와 같이 UI에 이 필드 값이 표시
    - 값에 리소스 문자열을 사용하는 것이 좋다
  • android:id 필드 :  destination을 참조하는 데 사용되는 destinationID
  • android:name 필드 : destination과 연결된 클래스의 이름이 표시

 

 

[ Start destination이란? ] 

: (NavigationGraph 가 지정된) Activity를 열 때 처음으로 표시되는 화면이며 사용자가 앱을 종료할 때 마지막으로 보게 되는 화면

- destination app:startDestination 필드에서 지정함

app:startDestination="@id/destinationID"

 

 


 

2 - B) action

: destination 간의 논리적 연결

 

- 일반적으로 한 destination을 다른 destination에 연결함. (=사용자가 앱에서 선택할 수 있는 다양한 경로를 표시)


- NavigationGraph 에서 화살표로 표시함

 


 Global Action

 : "어디서든 특정 destination으로 이동할 수 있는 action"

 

- [ Global Action 사용하기 ] 

: 화면 이동 시 navigate() 메서드의 파라미터로

  ㄴ> (1) 
'Global Action ID값' 전달

viewTransactionButton.setOnClickListener { view ->
    view.findNavController().navigate(R.id.action_global_mainFragment)
}

 

  ㄴ> (2) Safe Args 사용 시에는 ->  'NavDirections 객체'를 전달 

: Directions 클래스의 'Global Action'메소드를 호출하면 ->  'NavDirections'객체를 전달받을 수 있다.

ㄴ> ex) 

<?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"
            android:id="@+id/main_nav"
            app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        ... >
         <argument
            android:name="safeArgs1명"
            app:argType="safeArgs1데이터타입" />
    </fragment>
    
    <action
        android:id="@+id/action_global_mainFragment"
        app:destination="@id/mainFragment"/>
    ...

</navigation>

위와 같은 경우에는 아래와 같이 navigate 사용

viewTransactionButton.setOnClickListener { view ->
    view.findNavController().navigate(
    	MainNavDirections.actionGlobalMainFragment(safeArgs1값)
    )
}

 

 

- [ Global Action 만들기 ] 

  1. Graph Editor에서 대상을 클릭하여 강조표시합니다.
  2. 대상을 마우스 오른쪽 버튼으로 클릭하여 컨텍스트 메뉴를 표시합니다.
  3. Add Action > Global을 선택합니다. 화살표(->)가 대상의 왼쪽에 나타납니다.
  4. Text 탭을 클릭하여 XML 텍스트 뷰로 이동합니다. 전역 작업의 XML은 다음과 유사합니다.

 


2. NavHost

 : Navigation Graph에서 destination을 표시하는 빈 컨테이너

   ( = NavController를 통한 Navigation을 위한 single Context 또는 컨테이너)

 

- "사용자가 앱을 탐색(navigate)하는 동안 NavHostdestination이 교체된다"

 

NavHost로 사용하려면 'interface NavHost'에서 파생되어야 한다.

   ㄴ> NavHost 구현한 "NavHostFragment" 써서 destination의 교체를 처리

   ㄴ>

public interface NavHost
ㄴ> Known direct subclasses : NavHostFragment
ㄴ> Known indirect subclasses: DynamicNavHostFragment

 

- 하나의  NavHost만 기본값으로 지정가능
    ㄴ>  동일한 레이아웃에 여러 호스트가 있는 ( ex 창이 2개인 레이아웃라고 하는데 뭔지 모르겠다 등) 경우도호스트만 기본 NavHost로 지정해야 합니다.

 

 

[ Activity에 NavHost 추가하기 ] 

 Ex) Activity의 xml레이아웃 

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

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        .../>

</androidx.constraintlayout.widget.ConstraintLayout>

ㄴ>

  • android:name 속성 :  NavHost구현의 클래스 이름
    (android:name = "androidx.navigation.fragment.NavHostFragment")
  • app:navGraph 속성 : NavHostFragment NavigationGraph와 연결
  • app:defaultNavHost="true" 속성을 사용하면 NavHostFragment가 시스템 뒤로 버튼을 가로챈다.

 

 

[ NavHost의 역할 ]

NavController상태를 저장 및 복원 

- 루트 뷰에서 Navigation.setViewNavController를 호출

- NavController.popBackStack을 수동으로 호출하거나 NavController를 구성할 때 ->  NavHostController.setOnBackPressedDispatcher를 호출하여 시스템 뒤로 버튼 이벤트를 NavController로 라우팅함.

 

 

 

[ NavHost의 선택적 고려사항 ]

-  <특정 Lifecycle 주기와 연결하기>
    :
NavHostController.setLifecycleOwner를 호출

 

- <NavController.getViewModelStoreOwnerNavigationGraph 범위에서 ViewModel을 사용하기 >
    : NavHostController.setViewModelStore를 호출

 

 

 

 


 

3. NavController 

 : NavHost에서 App의 Navigation을 관리하는 객체

 

사용자가 앱 내에서 이동할 때 "NavHost에서 destination 콘텐츠의 전환을 조종하는 역할"

- NavController직접 노출되지 않고 외부에서 로만 액세스할 수 있어야 함

 

 

 


 

[ destination으로 이동 (=코드에서 화면 이동시키기)] 

 : [NavController객체를 가져와] 메소드 호출하여 이동함
   +) NavController객체는 NavHost 내에서 앱 탐색을 관리 & 각 NavHost에는 해당하는 자체 NavController가 존재


  ㄴ> [ (컴포넌트별) NavController 객체를 가져오는 방법 ]

     1) Fragment에서 findNavController() 메소스 호출

 

     2) View에서 findNavController() 메소스 호출

 

     3-1)  Activity에서 findNavController(viewId: Int) 메소스 호출


     3-2Activity에서 (1)FragmentContainerView를 사용하여 NavHostFragment를 만들 때나 
              (2) FragmentTransaction을 통해 NavHostFragment를 Activity에 수동으로 추가하는 경우, NavHostFragment에서 직접 NavController를 검색해야 한다. ex

val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController

 

 

※ 주의 : 3-2)에서 Navigation.findNavController(Activity, @IdRes int)를 통해 Activity의 onCreate()에서 NavController를 검색하려고 하면 실패함

 

 

 

 

 


[출처] 

 

- [공식] https://developer.android.com/guide/navigation/navigation-getting-started?hl=ko 

- https://jae-young.tistory.com/46

 

 

 

 

반응형