Study Record

[안드로이드] Navigation 을 이용한 Fragment 데이터 전달, Safe Arg 본문

안드로이드

[안드로이드] Navigation 을 이용한 Fragment 데이터 전달, Safe Arg

초코초코초코 2023. 6. 27. 19:17
728x90

 

😶 개요

Fragment 사이의 데이터 전달은 Bundle 을 이용한다. Bundle 을 사용하면 key, value 값으로 데이터를 저장하면 해당 Fragment 에서 argmnent 변수로 Bundle 객체를 참고할 수 있다.

 

 

Bundle 객체로 Fragment 로 데이터 전달하기

val bundle = Bundle().apply {
    this.putInt("test1", 3)
    this.putString("test2", "testtest")
}

StartFragment().apply {
    arguments = bundle
}

 

 

전달받은 데이터 해당 Fragment 에서 확인하기

arguments?.getInt("test1")
arguments?.getString("test2")

 

 

하지만 Bundle 객체를 이용하는 방식은 Bundle 은 프래그먼트에 Bundle 객체를 설정하지 않는다면 null 값을 반환하거나 default 값으로 반환하여 잘못된 작동을 할 수 있다.

 

따라서 이러한 문제들을 앱을 실행하기 전 컴파일 단계에서 발견하기 쉽게 도와주는 것이 Safe Args plugin 이다.

 

 

😶 Safe Args 기본 환경 설정

 

 

build.gradle(Project) - plugins 앞에 선언한다.

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.6.0"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

 

 

build.gradle(Moudle)

plugins {
    ...
    id 'androidx.navigation.safeargs.kotlin'
}

 

 

 

Navigation graph 파일의 argument 선언을 해준다.

<fragment
    android:id="@+id/gameWonFragment"
    android:name="com.example.android.navigation.GameWonFragment"
    android:label="fragment_game_won"
    tools:layout="@layout/fragment_game_won" >
    <action
        android:id="@+id/action_gameWonFragment_to_gameFragment"
        app:destination="@id/gameFragment" />
    <argument
        android:name="nameCorrect"
        app:argType="integer"
        android:defaultValue="2"/>
    <argument
        android:name="namCheck"
        app:argType="boolean"
        android:defaultValue="true"/>
</fragment>

app:argType 으로 쓸 수 있는 것은 "integer", "float", "long", "boolean", "string", "reference" 가 있다. android:name 속성으로 인자의 이름을 정해주고 android:defaultValue 로 기본값을 설정할 수 있다.

 

 

Navigation Graph 설정을 마친 후 ReBuild 를 하면 몇 개의 클래스가 생성된다.

Navigation Graph 에서 탐색 이동의 대상이 되는 프래그먼트에 "Directions" 라는 단어가 추가된 이름의 새로운 클래스가 생성된다.

또한, 해당 프래그먼트에 <argument> 가 있다면 "Args" 가 붙은 클래스가 생성된다.

 

 

 

😶 탐색하기

 

다음과 같이 Navigation Graph 파일(navigation xml 파일)이 startFragment 와 workFragment 가 있고 startFragment > workFragment 로 이동하며 workFragment 에는 workArg1 과 workArg2 의 이름을 갖는 인자가 있다고 가정한다.

<?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/navigation_test"
    app:startDestination="@id/startFragment">

    <fragment
        android:id="@+id/startFragment"
        android:name="com.example.test2.StartFragment"
        android:label="StartFragment" >
        <action
            android:id="@+id/action_startFragment_to_workFragment"
            app:destination="@id/workFragment" />
    </fragment>
    <fragment
        android:id="@+id/workFragment"
        android:name="com.example.test2.WorkFragment"
        android:label="WorkFragment" >
        <argument
            android:name="workArg1"
            app:argType="integer"
            android:defaultValue="2"/>
        <argument
            android:name="workArg2"
            app:argType="boolean"
            android:defaultValue="true"/>
    </fragment>
</navigation>

Navigation Graph 파일이 완성된 후 ReBuild 를 하면 (debug) 파일이 생긴다.

 

 

defug 관련 파일에는 위에서 설명한 탐색 대상이 되는 프래그먼트에는 Directions 가 붙은 프래그먼트와 workFragment 의 인자와 관련된 새로운 클래스들이 생성된 것을 볼 수 있다.

 

 

 

 

startFragment 에서 workFragment 로 탐색하는 코드는 StartFragmentDirections 클래스를 이용하면 된다. 버튼을 눌렀을 때 workFragment 로 넘어가는 예시 코드이다.

binding.workBtn.setOnClickListener {
    it.findNavController()
        .navigate(StartFragmentDirections.actionStartFragmentToWorkFragment(3, false))
}

 

actionStartFragmentToWorkFragment(workArg1, workArg2) 로 되어있는데 이렇게 함수형태로 값들을 전달하기 때문에 Bundle 객체를 직접 사용할 때보다 컴파일 단계에서 오류를 확인하기 쉽다.

 

 

😶 수신 Fragment 에서 인자 사용하기 (ex. WorkFragment)

 

수신 대상 Fragment 에서는 해당 프래그먼트의 이름에 "Args" 가 붙은 WorkFragmentArgs 클래스로 정의한 argument 를 가져올 수 있다.

val workArg : WorkFragmentArgs = WorkFragmentArgs.fromBundle(requireArguments())

workArg.workArg1
workArg.workArg2

 

 

😶 처음 시작 Fragment 에 인자를 전달해야 할 경우

 

프로그래밍 방식으로 Navigation Graph 를 따로 설정하면서 Bundle 객체를 전달한다. 주의해야 할 점은 xml 파일의 app:navGraph 속성을 지정하면 안 된다. (중복으로 실행되기 때문)

val args: Bundle = Bundle().apply {
    putInt("testArg", 4)
}
navController.setGraph(R.navigation.graph, args)

args 로 넘겨주는 Bundle 객체는 navigation 의 탐색 시작 프래그먼트의 argument 로 사용된다.

 

 

예시)

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
    }

    override fun onResume() {
        super.onResume()
        val args = Bundle().also {
            it.putString("test", "like")
        }
        binding.mainFg.findNavController()
            .setGraph(R.navigation.navigation_test, args)
    }
}

 

 

 

+ argument 마다 default 값 다르게 하기

<action> 태그 안에 <argument> 태그로 수신 대상 프래그먼트의 인자 기본값을 다르게 설정할 수 있다.

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

 

 

 

 

 

대상 간 데이터 전달  |  Android 개발자  |  Android Developers

대상 간 데이터 전달 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 탐색을 사용하면 대상 인수를 정의하여 탐색 작업에 데이터를 첨부할 수 있습니다. 예

developer.android.com

 

728x90