[Kotlin] Activity, fragment 사이 데이터 결과 전달 2가지 방법 Fragment Result Api, ViewModel - Under Tech Blog
반응형
Fragment Result Api - 프래그먼트 간 결과전달 BottomSheetDialog : 부모와 자식 관계 간 전달 예제
지금까지는 위와 같이 선택창을 띄운후, 프래그먼트의결과 값을 전달할때, intent로 결과 값을 전달 하였습니다. 하지만 이때 위 방법을 사용했을때 문제는, 코드가 길어지고 코드 재사용 효율성이 떨어지게 됩니다. 이문제를 해결하기위해
1. ViewModel을 이용하여 Fragment -> Activity로 전달
2. Fragment result API를 통한 Fragment A -> Fragment B 로 전달하는
두가지 방법을 알아보도록하겠습니다.
ViewModel을 이용하여 Fragment-> Activity로 전달
ViewModel을 이용한 데이터 전달 방법은 아래와 같습니다.
1. ViewModel을 생성해줍니다.
class SubjectSelectViewModel(
application: Application, val savedStateHandle: SavedStateHandle
) : AndroidViewModel(application) {
val mutableSelectedSubject = MutableLiveData<SubjectItem>()
val selectedSubject: LiveData<SubjectItem> get() = mutableSelectedSubject
fun setSelectSubject(subjectItem: SubjectItem) {
//여기서 postValue는 Asynctask로 백그라운드에서 유저가 클릭한 Subject가 무엇인지
// mutableSelctedSubject 라이브데이터에 업데이트시켜줍니다.
//이렇게 하면 MainActivity에서 Observe가 가능해집니다.
mutableSelectedSubject.postValue(subjectItem)
}
}
**위 코드에서 주의 할점은 postValue함수를 통해 Asynctask로 mutableSelectedSubject를 업데이트 해줬다는 점입니다.(자세한 설명은 위 코멘트를 확인해주세요)**
2. 데이터를 전달할 Fragment와 데이터를 받을 Activity에 ViewModel들을 인스턴스해줍니다.
- MainActivity
class MainActivity : AppCompatActivity() {
lateinit var subjectSelectViewModel: SubjectSelectViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
subjectSelectViewModel = ViewModelProvider(this).get(SubjectSelectViewModel::class.java)
//observe를 통해 liveData가 바뀔때마다 체크하는 함수
subjectSelectViewModel.selectedSubject.observe(this, Observer{
})
}
}
- Fragment
class SelectSubjectPopupDialog: BottomSheetDialogFragment() {
lateinit var subjectSelectViewModel: SubjectSelectViewModel
lateinit var selectSubjectAdapter: SelectSubjectAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
var view: View = inflater.inflate(R.layout.select_subject_dialog_fragment, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//viewModelProvider에 오너를 requireActivity()로 해주어야합니다.
subjectSelectViewModel = ViewModelProvider(requireActivity()).get(SubjectSelectViewModel::class.java)
button.setOnClickListener{
val subjectItem = ""
//subjectitem은 리사이클러뷰에서 선택된 아이템을 인터페이스를 통해 콜백 해주도록하겠습니다.
//리사이클러뷰 아이템을 가져오는 콜백 구현은 아래 링크된 포스트에서 다루도록 하겠습니다.
subjectSelectViewModel.setSelectSubject(subjectItem)
}
}
}
우선 ViewModel을 이용했을때 주의 해야할점은 우리가 ViewModel을 인스턴스해줄때, 생명주기를 맞게 설정을 해줘야합니다.
위에서 subjectSelectViewModel을 인스턴스할때 provider안에 생명주기를 requireActivity로 해준걸 확인할수있습니다. 만약 다르게 해줄경우, 다른 뷰모델을 생성하게됩니다. 그렇게되면 Fragment에서 Activity로 전달이 불가능해집니다.
Fragment Result API - 프래그먼트 A, B 사이 간 결과 값 전달
이번에는 Fragment ResultAPI로 같은 선상에있는 fragment끼리 값을 전달할때 사용법을 설명해보도록 하겠습니다.
이방법은 ViewModel을 사용했을때보다 훨씬 코드가 간결해집니다.
Fragment A - 결과 값을 받을 곳
Fragment B - 결과 값을 보낼 곳
먼저 결과 값을 받을 Fragment A를 구현하겠습니다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// fragment-ktx artifact 코틀린 extention을 gradle에 추가해주면 리스너를 사용할수있게됩니다.
setFragmentResultListener("requestKey") { requestKey, bundle ->
//결과 값을 받는곳입니다.
val result = bundle.getString("bundleKey")
}
}
우선 "setFragmentListenter("전달받을 결과 값이름 ") 을 정해줍니다. Fragment B가 바뀌면, 리스너 안에 코드가 실행됩니다.
결과 값을 보낼 Fragment B를 구현하겠습니다.
button.setOnClickListener {
val result = "result"
// 버튼을 누르면, 전달될 값
setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}
button이 눌리면 "setFragmentResult"를 통해 아래 값들을 전달하도록 하겠습니다.
여기서 requestKey를 setFragmentListenter에서 써줬던 Key와 일치시켜줘야합니다.
result String 값을 전달시키겠습니다.
그럼 B fragment에서 버튼이 눌릴때마다, FragmentAd에서 반응하는것을 볼수있습니다.
Fragment Result API -상위 및 하위 프래그먼트 간 결과 전달
위에서는 같은 선상에 있는 프래그먼트 사이에서 결과 전달을했다면, 이번에는 상위 및 하위 프래그먼트간 결과 전달입니다.
- 하위 프래그먼트 결과 전송(하위 프래그먼트에 구현)
하위 프래그먼트에서 결과 값을 보낼때 아래 와 같이 setFragmentResult 앞에 ChildFragmentManager을 먼저 불러온것을 확인할수있습니다.
childFragmentManager.setFragmentResult("requestKey", bundleOf("bundleKey" to result))
- 호스트 액티비티 결과 수신(메인 액티비티에 구현)
호스트 액티비티는 supportFragmentManager를 통해 결과 값을 수신하는것을 확인할수있습니다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportFragmentManager
.setFragmentResultListener("requestKey", this) { requestKey, bundle ->
// We use a String here, but any type that can be put in a Bundle is supported
val result = bundle.getString("bundleKey")
// Do something with the result
}
}
}