안드로이드 공부를 하면서 fragment라는 개념을 처음 접하였다. fragment는 단순히 activity 위에 그릴 수 있는 조각 조각의 화면이고, activity와 비슷하게 작동할 것이라 마음대로 생각했지만 실상은 그렇지 않았다..
fragment의 LifeCycle만 봐도 activity와는 약간 다르고, 데이터를 전달하는 방식에도 차이가 있다.
이전 포스트에서는 개념적인 부분에 대해 정리하였다면, 이번 포스팅에서는 새로 나온 과제와 함께 fragement 간 데이터를 전달하는 예제를 만들어보고 이해하는 것을 목표로 한다.
이전 포스팅에서는
1. ViewModel
2. Fragment Result API
위 두 가지에 대해 정리하였다.
이번 포스팅에서는 두 개를 더 해서 FragmentManager와 findNavController()로 전달하는 것까지 예제를 만들어보자!
데이터를 보내는 fragment : HomeFragment
데이터를 받는 fragment : DashboardFragment
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 값을 통해 문자열 값을 받아온다.
- 받아온 값들을 사용하여 화면에 띄워준다.
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에 쥐고 있는다.
- 받아온 데이터를 사용하여 화면에 뿌려준다.
보내는 쪽에서 데이터를 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()
으로 저장되어 있는 데이터를 받아와 처리해준다.
보내는 쪽에서 데이터를 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 값을 통해 문자열 값을 받아온다.
- 받아온 값들을 사용하여 화면에 띄워준다.
[TIL-240408]