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

Android fragment에서 fragment dialog으로 이동후 되돌아올때 이벤트

0 추천

안녕하세요 제가 fragment에서 navigation component로 dialog로 이동후 완료가 되었을때 다시 fragment로 돌아가 이벤트를 실행하고 싶습니다.​​​​​ 그 이벤트는 pagingAdapter refresh인데 refresh가 제대로 먹히지 않은 것 같습니다. 도움을 주세요 

 

// dialog code

     viewModel.successAcceptData.observe(viewLifecycleOwner) {

                    if (it?.isEmpty() == true)
                        binding.progressBar.visibility = VISIBLE
                    else {
                        binding.progressBar.visibility = View.GONE
                        Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show()
                        val action = AcceptDialogDirections.actionAcceptDialogToAdminMainFragment(
                            ACCEPTED
                        )
                        findNavController().navigate(action)

                    }
                }

 

// fragment 코드 

override fun onResume() {
    super.onResume()
    if(args.type==ACCEPTED) {
        Log.d(TAG, "onCreateView: ${args.type}")
        acceptAdapter.refresh()
    }
}
hifl (670 포인트) 님이 2021년 9월 13일 질문

1개의 답변

0 추천

Navigation Component를 사용할 때 Fragment - Fragment, Fragment - DialogFragment 간에 결과값을 주고 받는 것은 위의 방법으로는 해결되지 않습니다. 님의 사용하신 코드는 결과값을 받는 것이 아니라 네비게이션 시에 데이터를 전달하는 코드로 다르다고 볼 수 있습니다. startActivityForResult를 생각해보시면 이해가 가실 겁니다.

자세한 방법은 https://developer.android.com/guide/navigation/navigation-programmatic 에 나와 있고, DialogFragment로 부터 결과값을 받으려면, 아래처럼 하시면 됩니다.

// DialogFragment
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)


// DialogFragment로부터 응답을 기다리는 Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val navController = findNavController();
    // After a configuration change or process death, the currentBackStackEntry
    // points to the dialog destination, so you must use getBackStackEntry()
    // with the specific ID of your destination to ensure we always
    // get the right NavBackStackEntry
    val navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment)

    // Create our observer and add it to the NavBackStackEntry's lifecycle
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME
            && navBackStackEntry.savedStateHandle.contains("key")) {
            val result = navBackStackEntry.savedStateHandle.get<String>("key");
            // Do something with the result
        }
    }
    navBackStackEntry.lifecycle.addObserver(observer)

    // As addObserver() does not automatically remove the observer, we
    // call removeObserver() manually when the view lifecycle is destroyed
    viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            navBackStackEntry.lifecycle.removeObserver(observer)
        }
    })
}

 

위의 코드를 Kotlin Extension function으로 만들면 편리합니다.

const val DIALOG_RESULT_KEY = "DialogResult"

inline fun <T> Fragment.getDialogNavResult(@IdRes navId: Int, key: String = DIALOG_RESULT_KEY, crossinline onChanged: (T?) -> Unit) {
    val backStackEntry = findNavController().getBackStackEntry(navId)
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME && backStackEntry.savedStateHandle.contains(key)) {
            val result = backStackEntry.savedStateHandle.get<T>(key)
            onChanged(result)
            backStackEntry.savedStateHandle.remove<T>(key)
        }
    }
    backStackEntry.lifecycle.addObserver(observer)

    viewLifecycleOwner.lifecycle.addObserver(
        LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_DESTROY) {
                backStackEntry.lifecycle.removeObserver(observer)
            }
        }
    )
}

fun <T> Fragment.setNavResult(key: String = DIALOG_RESULT_KEY, data: T) {
    findNavController().previousBackStackEntry?.also { stack ->
        stack.savedStateHandle.set(key, data)
    }
}

 

이제 위의 Extension function을 이용하여 다음과 같이 호출하면 됩니다.

 

// DialogFragment

viewModel.successAcceptData.observe(viewLifecycleOwner) {
     val denied = it?.isEmpty() == true
     binding.progressBar.isVisible = denied
     if (denied) return@observe
              
     Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show()

     setNavResult(data = ACCEPTED)
     findNavController().popBackStack()
}


// Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
     super.onViewCreated(view, savedInstanceState)
     getDialogNavResult<ACCEPTED데이터타입>(navId = R.id.your_fragment) { result -> 
          if (result == ACCEPTED) {
             acceptAdapter.refresh()
          }
     }
}

 

 

spark (227,470 포인트) 님이 2021년 9월 13일 답변
spark님이 2021년 9월 13일 수정
navId를 구하는 부분은 하드코딩하셔도 되고 아마
findNavController().currentDestination.id를 하셔도 될 것 같습니다. 한번 테스트해 보세요.
당신은 혹시 천사입니까..?? 감사합니다 ㅠㅠ 한번 테스트 해보겠습니다.
당신은 혹시 천사입니까..?? 감사합니다 ㅠㅠ 한번 테스트 해보겠습니다. 감사합니다 고쳤어요 당신덕ㅂ
고쳤습니다.. 와 감사해요 ㅠㅠㅠㅠㅠ
...