마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Navigation에서 받은 argument를 어떻게 유지할수 있을까요?

0 추천

A->B로 화면을 전환할때 String 데이터를 넘깁니다. Navigation Graph의 action와 safeargs를 활용합니다.

B에서는 A에서 받은 데이터를 해당화면의 타이틀로 설정합니다.

B에서 C로 화면전환을 하고 C화면에서 어떤 버튼을 클릭하면서 다시 B 화면으로 돌아가는 기능입니다.

(여기서 C에서도 D로 데이터를 보내야하지만 아직 구현은안했습니다)

 

그런데 C에서 B로 돌아오면, A에서 B로 화면전환이 된것이 아니기 때문에 B의 argument 값은

nav_graph에서 설정했던 디폴트 값으로 설정이 됩니다..

이것을 화면이 계속해서 전환되어도 어떻게 유지할 수 있을까요?

 

nav_graph.xml

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/calendar">
    <!-- 화면 A -->
    <dialog
        android:id="@+id/bodyPartDialog"
        android:name="com.example.writeweight.fragment.BodyPartDialogFragment"
        android:label="BodyPartDialogFragment"
        tools:layout="@layout/fragment_body_part_dialog">
        <action
            android:id="@+id/action_bodyPartDialog_to_write"
            app:destination="@id/write"/>
    </dialog>
    <!-- 화면 B -->
    <fragment
        android:id="@+id/write"
        android:name="com.example.writeweight.fragment.WritingRoutineFragment"
        android:label="WritingRoutineFragment"
        tools:layout="@layout/fragment_writing_routine">
        <argument
            android:name="title"
            app:argType="string"
            android:defaultValue="" />
        <action
            android:id="@+id/action_write_to_workoutListTabFragment"
            app:destination="@id/workoutListTabFragment" />
    </fragment>

    <!-- 화면 C -->
    <fragment
        android:id="@+id/workoutListTabFragment"
        android:name="com.example.writeweight.fragment.WorkoutListTabFragment"
        android:label="fragment_workout_list_tab"
        tools:layout="@layout/fragment_workout_list_tab" >
        <action
            android:id="@+id/action_workoutListTabFragment_to_write"
            app:destination="@id/write"
            app:popUpTo="@id/write"
            app:popUpToInclusive="true"/>
    </fragment>
</navigation>

 

화면 A

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view: View = inflater.inflate(R.layout.fragment_body_part_dialog, container, false)

        startBtn?.setOnClickListener { v ->
            if (BodyPartCustomView.getSelectedCount() == 0) Toast.makeText(context, "부위를 선택해주세요", Toast.LENGTH_SHORT).show()
            else {
                title = BodyPartCustomView.getTitle()
                // safeargs를 이용한 작성페이지로 title 데이터 전달
                action = BodyPartDialogFragmentDirections.actionBodyPartDialogToWrite(title)
                findNavController()?.navigate(action)
                BodyPartCustomView.clearTitleData() // companion 타입이므로 초기화
                dismiss()
            }
        }
        return view
    }

 

화면 B

class WritingRoutineFragment : Fragment() {
    private var _binding: FragmentWritingRoutineBinding? = null
    private val binding get() = _binding!!
    private val viewModel: WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() }
    private val args by navArgs<WritingRoutineFragmentArgs>() // argument 받음.
    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        _binding = FragmentWritingRoutineBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.title.observe(viewLifecycleOwner) { titleData -> // viewmodel 의 title 값이 변경됐을때 titleData 로 넘어옴
            // UI 업데이트
            binding.title.text = titleData // 타이틀 세팅
        }
        viewModel.setValue(args) // 뷰모델에 받은 argument 값 넣기
    }

}

 

화면 B의 ViewModel

class WriteRoutineViewModel : ViewModel() {
    private var _title: MutableLiveData<String> = MutableLiveData()

    val title: LiveData<String> = _title

    fun setValue(_data: WritingRoutineFragmentArgs) {
        _title.value = _data.title
    }
}

 

codeslave (3,940 포인트) 님이 2021년 7월 6일 질문
codeslave님이 2021년 7월 6일 수정

1개의 답변

0 추천
argument를 통해 넘기는 값은 프레그먼트가 백스택에 있는 한 유지가 됩니다. 프레그먼트의 argument가 configuration change에도 유지되는 것과 같습니다. 저도 1년 넘게 Navigation Component를 사용 중이지만, 이 부분은 아주 잘 동작합니다.

만약 기존에 저장된 argument를 화면 전환시에 다른 값으로 사용하고 싶다면 ViewModel을 이용하시면 됩니다. 아니며 간단한 타이틀 정도라면 XML에 저장된 액션대신에 NavgationOption을 동적으로 만들어서 findNavController.navigate함수를 호출하셔도 됩니다.
spark (224,800 포인트) 님이 2021년 7월 6일 답변
선생님이 말씀하시는 argument가 Bundle의 argument는 아니져? navgraph의 argument가 맞나요? 맞다면 왜 유지가 안될까요ㅜ..popUpTo와 popUpToInclusive 속성을 사용해서 C에서 B화면으로 올때 백스택을 모두 지우고 새로 B를 만들어서 그런걸까요..

그리고 지금은 타이틀 만이지만.. 나중에 리사이클러 아이템도 추가될 예정이라..그부분에 대해서도 미리 염두를 해둬야하거든요.. 타이틀이 안되면 그부분도 안될것이라..
Bundle 맞아요. 타이틀 정도는 괜찮아 보이자만 리스트 아이템과 같은 사이즈가 큰 데이터라면 직접 넘기지 않아도 된다면 키값에 해당하는 데이터만 넘기도 새로운 화면에서 데이터를 불러와서 사용하세요.
아 navgraph에서 설정하는 argument도 bundle로 저장돼서 넘어가나보네요..
선생님근데 저는 왜 화면을 전환했다가 다시 돌아오면 유지가 안되나요..?
...