오늘도 더 나은 코드를 작성하였습니까?

android Navigation (Destination 데이터 전달) 본문

Android Jetpack Architecture/Navigation

android Navigation (Destination 데이터 전달)

hik14 2021. 11. 29. 15:52

navigation을 사용하면 destination argument 를 정의하여 navi action에 데이터를 첨부할 수 있습니다.

 

일반적으로 destination 간에 최소한의 데이터만 전달하는 것이 좋습니다.

 

예를 들어 객체 자체를 전달하지 않고 key를 전달하여 객체를 검색해야 하는데 이는 모든 저장된 상태의 총 공간이 Android에서 제한되기 때문입니다. 대량의 데이터를 전달해야 하는 경우 프래그먼트 간 데이터 공유에 설명된 대로 ViewModel 사용을 해야한다.

 

 

Destination argument

 

  1. navi editer 에서 argument 를 받는 destination을 클릭합니다.
  2. Attributes 패널에서 Add(+)를 클릭합니다.
  3. 표시되는 Add Argument Link 창에서 인수 이름과 인수 유형, 인수의 null 허용 여부 그리고 필요하다면 기본값을 입력합니다.
  4. Add를 클릭합니다. 이제  Attributes 패널의 Arguments 목록에 인수가 표시됩니다.
  5. argument가 XML에 추가된 것을 알 수 있습니다. Text 탭을 클릭하여 XML 뷰로 전환하면 인수가 인수를 받는 destination에 추가된 것을 확인할 수 있습니다
 <fragment android:id="@+id/myFragment" >
     <argument
         android:name="myArg"
         app:argType="integer"
         android:defaultValue="0" />
 </fragment>

 

지원하되는 인수의 타입들

 

*resource에 대한 reference는 참조 유형에서만 지원됩니다. 다른 유형에서 resource reference를 사용하면 예외가 발생합니다.

 

 

- argument type이 null 값을 허용하는 경우 android:defaultValue="@null"을 사용하여 null의 default을 선언한다.

 

 - Path, deepLink 및 argument 가 포함된 URI는 문자열에서 구문 분석할 수 있다, Parcelables 및 Serializables와 같은 user custom 데이터 유형을 사용하여 불가능합니다.

 

- user custom복합 데이터를 전달하려면 ViewModel 또는 데이터베이스와 같은 다른 곳에 데이터를 저장하고 탐색하는 동안 key/id만 전달합니다. 그런 다음 탐색이 완료된 후 새 destination에서 데이터를 불러오도록 한다.

 

- <inferred type>을 선택하여 탐색 라이브러리가 제공된 값을 기반으로 유형을 결정하도록 할 수 있습니다.

 

- Array를 확인하여 인수가 선택된 Type 값의 배열이어야 함을 나타낼 수 있습니다. 

  • enum type 배열과 resource reference type 배열은 지원되지 않습니다.(원시타입 배열만 지원됨.)
  • 기본 유형이 null을 허용하는 값을 지원하는지와 상관없이 배열은 null을 허용하는 값을 지원합니다.  app:argType="integer[]"를 사용하면 app:nullable="true"를 사용하여 배열 자체가  null 임을 전달하는 것.
  • 배열은 단일 기본값 '@null'을 지원합니다. 배열은 다른 기본값을 지원하지 않는다.

action 에서 destination argument  재정의 하기

destination level argument 및 default 값은 목적지 destination으로 이동하는 모든 action에서 사용됩니다.

필요한 경우 action level 에서 argument를 정의하여 인수의 기본값을 재정의할 수 있습니다(또는 이미 존재하지 않는 경우 설정)

이 argument는 destination에 선언된 argument와 이름 및 유형이 같아야 합니다.

<action android:id="@+id/startMyFragment"
    app:destination="@+id/myFragment">
    <argument
        android:name="myArg"
        app:argType="integer"
        android:defaultValue="1" />
</action>

 

Safe Args를 사용하여 Type 안전성이 있는 데이터 전달

Navigation component 에는 Safe Args라고 하는 Gradle 플러그인이 있어 유형이 안전한 탐색 및 연결된 모든 인수에 대한 액세스를 위한 간단한 개체 및 빌더 클래스를 생성합니다. Safe Args는 형식 안전성을 보장하므로 데이터 탐색 및 전달에 강력히 권장됩니다.

 

Safe Args를 enable 후 생성된 코드에는 각 전송 및 수신 destination뿐만 아니라 각 action에 대해 다음과 같은 type 안전 클래스 및 메서드가 포함됩니다.

 

XXXDirections

- action시작된 각 destionation에 대해 클래스가 생성됩니다. 이 클래스의 이름은 "Directions"라는 단어가 추가된 destionation의 이름입니다.  예를 들어, 발신 대상이 SpecifyAmountFragment라는 프래그먼트인 경우 생성된 클래스는 SpecifyAmountFragmentDirections 이다.

 

Action A To B

- argument 를 전달하는 데 사용되는 각 action에 대해 이름이 action을 기반으로 하는 내부 클래스가 생성됩니다. 예를 들어 action의 이름이 ConfirmationAction인 경우 클래스의 이름은 ConfirmationAction입니다. action에 defaultValue가 없는 인수가 포함되어 있으면 연결된 action 클래스를 사용하여 인수 값을 설정합니다.

 

Args

-수신 대상에 대한 클래스가 생성됩니다. 이 클래스의 이름은 "Args"라는 단어가 추가된 destination의 이름입니다. 예를 들어 destination fragment의 이름이 ConfirmationFragment인 경우 생성된 클래스의 이름은 ConfirmationFragmentArgs입니다.

이 클래스의 fromBundle() 메서드를 사용하여 인수를 검색합니다.

 

발신쪽

override fun onClick(v: View) {
   val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
   val amount = amountTv.text.toString().toInt()
   val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
   v.findNavController().navigate(action)
}

수신쪽

val args: ConfirmationFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val tv: TextView = view.findViewById(R.id.textViewAmount)
    val amount = args.amount
    tv.text = amount.toString()
}

수신쪽 destination의 코드에서 getArguments() 메서드를 사용하여 Bunble을 검색하고 해당 콘텐츠를 사용합니다.

-ktx 종속성을 사용할 때 Kotlin 사용자는 by navArgs() 속성 대리자를 사용하여 인수에 액세스할 수도 있습니다.

 

Global Action과 함께 Safe Args 사용

global action과 함께 Safe Args를 사용하는 경우 다음 예제와 같이 루트 <navigation> 요소에 대해 android:id 값을 제공해야 합니다.

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

    ...

</navigation>

Navigation은 android:id 값을 기반으로 하는 <navigation> 요소에 대한 Directions 클래스를 생성합니다. 예를 들어 android:id=@+id/main_nav인 <navigation> 요소가 있는 경우 생성된 클래스는 MainNavDirections라고 합니다.

 

<navigation> 요소 내의 모든 destination은 이전 섹션에서 설명한 것과 동일한 방법을 사용하여 연결된 모든 global action에 액세스하기 위한 method를 생성한다. 

 

Bundle 객체를 사용하여 destination 간에 데이터 전달

 

Bundle 객체를 생성하고 Navigate()를 사용하여 대상에 전달합니다.

val bundle = bundleOf("amount" to amount)
view.findNavController().navigate(R.id.confirmationAction, bundle)

 

수신 대상의 코드에서 getArguments() 메서드를 사용하여 번들을 검색하고 그 내용을 사용합니다.

val tv = view.findViewById<TextView>(R.id.textViewAmount)
tv.text = arguments?.getString("amount")

 

Start Destination 에 Data 전달

앱의 start destination에 데이터를 전달할 수 있습니다.

 

1. 데이터를 보유하는 Bundle을 명시적으로 구성해야 합니다.

2. 메서드 중 하나를 사용하여 Bundle을 start destination에 전달합니다.

 

프로그래밍 방식으로 NavHost를 생성하는 경우
NavHostFragment.create(R.navigation.graph, args)를 호출합니다. args는 데이터를 보유하는 Bundle 객체 입니다.

 NavController.setGraph()의 다음 overload된 메서드중  하나를 호출하여  start destination 인수를 설정할 수 있습니다.

  • Using the ID of the graph: navController.setGraph(R.navigation.graph, args)
  • Using the graph itself: navController.setGraph(navGraph, args)

start destination에서 데이터를 검색하려면 getArguments()를 호출