[Android] Fragment 간 데이터 전달을 구현해보자

neoneoneo·2024년 4월 8일
0

android

목록 보기
12/16

배경

안드로이드 공부를 하면서 fragment라는 개념을 처음 접하였다. fragment는 단순히 activity 위에 그릴 수 있는 조각 조각의 화면이고, activity와 비슷하게 작동할 것이라 마음대로 생각했지만 실상은 그렇지 않았다..

fragment의 LifeCycle만 봐도 activity와는 약간 다르고, 데이터를 전달하는 방식에도 차이가 있다.

이전 포스트에서는 개념적인 부분에 대해 정리하였다면, 이번 포스팅에서는 새로 나온 과제와 함께 fragement 간 데이터를 전달하는 예제를 만들어보고 이해하는 것을 목표로 한다.

다시 떠올려보는 Fragment 데이터 통신 방식

이전 포스팅에서는
1. ViewModel
2. Fragment Result API

위 두 가지에 대해 정리하였다.

이번 포스팅에서는 두 개를 더 해서 FragmentManager와 findNavController()로 전달하는 것까지 예제를 만들어보자!

사전 참고 사항

데이터를 보내는 fragment : HomeFragment
데이터를 받는 fragment : DashboardFragment

1. Fragment Result API

데이터 보내기

val bundle = Bundle().apply {
	putString("name", data[0].name)
	putString("description", data[0].description)
	}
binding.btnSend.setOnClickListener {
	setFragmentResult("requestKey", bundle)
}
  • bundle에 보낼 데이터를 key와 value 형태로 저장해준다.
  • setFragmentResult()안에 보낼 데이터의 key와 bundle을 작성하여 데이터를 던진다.

데이터 받기

setFragmentResultListener("requestKey") { _, result ->
	val name = result.getString("name")
	val description = result.getString("description")
	binding.tvName.text = "이름 : $name"
	binding.tvDes.text = "설명 : $description"
}
  • setFragmentResultListenet()안에 key를 넣어주고,
  • , result ->로 값에 대해서만 접근할 것이라는 것을 명시한다. 앞의 는 key 값에 대한 부분인데, 여기에서 딱히 key 값을 다루지는 않을 것이므로 _ 처리해두어 코드의 가독성을 높인다.
  • result.getString()으로 key 값을 통해 문자열 값을 받아온다.
  • 받아온 값들을 사용하여 화면에 띄워준다.

결과 및 이 방식의 특징

  • 다른 fragment로 갔다가 돌아가면 받아온 데이터 값이 비워져 있다. 데이터가 유지되지 않는 것이다..!
  • 전달 버튼을 눌렀을 때 바로 다른 fragment로 화면을 바꿔주려면 별도로 코드를 작성해야한다.

2. ViewModel

ViewModel class 생성

class HomeViewModel : ViewModel() {
    private val _text = MutableLiveData<String>().apply {
        value = "This is home Fragment"
    }
    val text: LiveData<String> = _text
    //공유할 데이터 정의
    private val _flowerName = MutableLiveData<String>()
    val flowerName: LiveData<String> get() = _flowerName
    private val _flowerDescription = MutableLiveData<String>()
    val flowerDescription: LiveData<String> get() = _flowerDescription
  	//데이터 정보를 받아갈 함수 정의
    fun setFlowerInfo(name: String, description: String) {
        _flowerName.value = name
        _flowerDescription.value = description
    }
}
  • MutableLiveData<String>로 공유할 데이터를 먼저 선언해둔다
    • 가변(mutable)한 데이터 홀더
  • flowerName: LiveData<String> get() : 선언된 데이터를 LiveDat 타입 변수에 넣어준다.
    • LiveData는 데이터의 변경을 관찰할 수 있는 클래스
  • setFlowerInfo() 에서는 선언된 각각의 공유할 데이터를 .value로 받아와서 _FlowerName과 _FlowerDescription에 넣어주도록 한다.
    • 이렇게 받아온 데이터는 다시 flowerName과 floserDescription으로 저장된다.

데이터 보내기

binding.btnSend.setOnClickListener {
	homeViewModel = ViewModelProvider(requireActivity()).get(HomeViewModel::class.java)
	homeViewModel.setFlowerInfo(data[0].name, data[0].description)
}
  • 사용할 ViewModel을 초기화해준다.
    • ViewModelPrivider() : ViewModel 인스턴스 생성
    • requireActivity() : 현재 프래그먼트가 속한 액티비티를 LifecycleOwner로 사용
    • .get() : HomeViewModel 클래스의 인스턴스 반환

데이터 받기

homeViewModel = ViewModelProvider(requireActivity()).get(HomeViewModel::class.java)
homeViewModel.flowerName.observe(viewLifecycleOwner) { name ->
	binding.tvName.text = "이름 : $name"
}
homeViewModel.flowerDescription.observe(viewLifecycleOwner) { description ->
	binding.tvDes.text = "이름 : $description"
}
  • 마찬가지로 사용할 ViewModel을 초기화해준다.
  • homeViewModel에 정의된 flowerName과 flowerDescription을 "관찰 observe"한다.
    • observe는 LiveData를 관찰하고 있는 화면 구성 요소가 활성 상태일 때만 데이터 변경을 수신하도록 하는 라이프사이클 관리 기능을 하며 LiveData를 관찰하면, 화면 구성 요소의 활성 및 비활성 상태에 따라 데이터 변경을 안전하게 처리 할 수 있도록 한다.
    • 활성 상태에 있는 데이터를 받아 name와 description에 쥐고 있는다.
  • 받아온 데이터를 사용하여 화면에 뿌려준다.

결과 및 이 방식의 특징

  • 다른 fragment로 이동해도 Dashboard에 전달된 데이터가 그대로 남아있다.
  • 마찬가지로 전달 버튼을 눌렀을 때 바로 다른 fragment로 화면을 바꿔주려면 별도로 코드를 작성해야한다.

3. FragmentManager

보내는 쪽에서 데이터를 Bundle에 담아 전달하고, 받는 쪽에서는 arguments를 통해 그 데이터를 다시 꺼낸다.

데이터 보내기

private lateinit var fragmentManager: FragmentManager
  • 일단 onCreateView 밖에 fragmentManager를 선언해준다.
binding.btnSend.setOnClickListener {
	val bundle = Bundle().apply {
		putString("name", data[0].name)
		putString("description", data[0].description)
}
val fragmentDashboard = DashboardFragment()
fragmentDashboard.arguments = bundle
fragmentManager = requireActivity().supportFragmentManager
fragmentManager.beginTransaction()
	.replace(R.id.container, fragmentDashboard)
	.addToBackStack(null)
	.commit()
}
  • bundle에 보낼 데이터를 key와 value 형태로 저장해준다.
  • 보낼 대상이 되는 fragment의 인스턴스를 생성하고, arguments 안에 전달할 데이터인인 bundle을 넣어준다.
  • onCreateView 밖에서 선언해준 fragmentManager를 requireActivity().supportFragmentManager로 설정하여 현재 액티비티의 FragmentManger를 가져온다.
  • fragmentManager.beginTransaction()로 트랜잭션을 시작하고, replace에는 특정 fragment와 교체할 View 요소를 넣어준다.
  • .addToBackStack(null)은 트랜잭션을 백 스택을 추가하여 뒤로 가기 버튼을 눌렀을 때 이전 fragment로 돌아가도록 한다.
  • .commit()은 해당 트랜잭션을 커밋하여 실행하는 역할이다.

데이터 받기

val name = arguments?.getString("name")
binding.tvName.text = "이름 : $name"
  • arguments?.getString()으로 저장되어 있는 데이터를 받아와 처리해준다.

결과 및 이 방식의 특징

  • 전달을 누르고 트랜잭션이 일어나면 home 탭은 유지된 채로 fragment만 교체된다... 그니까 데이터 전달은 잘 되었는데, bottom navigation bar와는 연동이 자연스럽게 되지 못 했다(왜냐? 안해줬으니까..)

4. findVavController()

보내는 쪽에서 데이터를 Bundle에 담아 전달하고, 받는 쪽에서는 arguments를 통해 그 데이터를 다시 꺼낸다.

데이터 보내기

val bundle = Bundle().apply {
	putString("name", data[0].name)
	putString("description", data[0].description)
}
binding.btnSend.setOnClickListener {
	it.findNavController().navigate(R.id.action_navigation_home_to_navigation_dashboard, bundle)
}
  • bundle에 보낼 데이터를 key와 value 형태로 저장해준다.
  • findNavController().navigete()로 보낼 view와 저장된 bundle을 명시하여 데이터를 던진다.

데이터 받기

val bundle = arguments
val name = bundle?.getString("name")
val description = bundle?.getString("description")
binding.tvName.text = "이름 : $name"
binding.tvDes.text = "설명 : $description"
  • bundle에 arguments를 넣어주면서 초기화한다.
    • arguments는 fragment 간 데이터를 전달하는 데에 사용되는 property이다.
  • bundle?.getString()으로 key 값을 통해 문자열 값을 받아온다.
  • 받아온 값들을 사용하여 화면에 띄워준다.

결과 및 이 방식의 특징

  • 처음에 Dashboard fragment로 가면 값이 null로 들어가 있게 된다. 이를 막기 위해서는 Dashboard fragment에서 null일 때에는 "" 공란을 보이도록 하는 등 별도의 처리를 해주면 될 것 같다.
  • 다른 fragment로 갔다오면 데이터가 유지되지 않는다. 다시 null로 표시된다.
    • 근데..? 전달을 눌러 Dashboard fragment로 이동하고 다시 Home을 눌르면 Dashboard 탭이 유지되고 있고, 날라간 줄 알았던 데이터가 유지되고 있다... 이건 왜 이러는걸까.. 아직 Jetpack 공부를 안해서 Navigation 자체에 대한 이해가 부족한 것 같다. 나중에 Jetpack을 공부할 때 더 찾아보고 글을 업데이트 해보겠다.

참고한 글들


[TIL-240408]

0개의 댓글