[ 프래그먼트 간 데이터 전달 ]
프래그먼트 간의 데이터 전달은 안드로이드 애플리케이션의 유연성과 모듈성을 향상시키는 중요한 기능임. 각 시나리오에 따라 데이터를 전달하는 방법을 정리해 보자.
[ 1 ] Activity -> Fragment
: 액티비티에서 프래그먼트로 데이터를 전달할 때는 프래그먼트의 인스턴스를 생성하고 newInstance 메소드를 통해 데이터를 전달한다. Bundle 객체를 사용하여 데이터를 프래그먼트의 인자(arguments)로 설정하고 이 인자를 프래그먼트가 받아 사용함
< MainActivity.kt >
= 데이터를 보내는 코드
binding.run {
fragment1Btn.setOnClickListener{
// [1] Activity -> FirstFragment
val dataToSend = "Hello First Fragment! \n From Activity"
val fragment = FirstFragment.newInstance(dataToSend)
setFragment(fragment)
}
fragment2Btn.setOnClickListener {
// [1] Activity -> SecondFragment
val dataToSend = "Hello Second Fragment!\n From Activity"
val fragment = SecondFragment.newInstance(dataToSend)
setFragment(fragment)
}
- 위 코드에서는 MainActivity 에서 FirstFragment 와 SecondFragment 로 데이터를 전달하고 있다. 데이터를 전달하는 방식은 버튼의 onClickListener 를 설정하였고 newInstance를 통해 데이터를 전달한다.
< FirstFragment.kt >
= 데이터를 받는 코드
private var param1: String? = null
companion object {
@JvmStatic
fun newInstance(param1: String) =
// [1] Activity -> FirstFragment
FirstFragment().apply {
arguments = Bundle().apply { // arguments 에 Bundle 로 데이터 전달
putString(ARG_PARAM1, param1)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// [1] Activity -> FirstFragment
binding.tvFrag1Text.text = param1
}
- 위 코드는 newInstance 메소드에서 전달받은 데이터를 Bundle 에 담고 프래그먼트의 인자로 설정한다. onViewCreated 에서는 인자로 받은 데이터를 텍스트 뷰에 설정함
< 실행 결과 >
[ 2 ] Fragment -> Fragment
한 프래그먼트에서 다른 프래그먼트로 데이터를 전달할 때는 첫 번째 프래그먼트에서 두 번째 프래그먼트의 newInstance 메소드를 사용하여 인스턴스를 생성하고 데이터를 전달함.
< FirstFragment.kt >
= 데이터를 보내는 코드
// [2] Fragment -> Fragment
binding.btnGofrag2.setOnClickListener{
val dataToSend = "Hello Fragment2! \n From Fragment1"
val fragment2 = SecondFragment.newInstance(dataToSend)
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, fragment2)
.addToBackStack(null)
.commit()
}
- 위 코드에서는 두 번째 프래그먼트를 이동할 때 사용하는 버튼에 onClickListener 를 설정했다. 데이터를 받을 두 번째 프래그먼트의 newInstance 메소드를 사용하여 인스턴스를 생성하고 데이터를 전달함
< SecondFragment.kt >
= 데이터를 받는 코드
private const val ARG_PARAM1 = "param1"
class SecondFragment : Fragment() {
private var param1: String? = null
private var _binding: FragmentSecondBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// [2] Fragment -> Fragment
binding.tvFrag2Text.text = param1
}
companion object {
@JvmStatic
fun newInstance(param1: String) =
// [1] Activity -> FirstFragment
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
override fun onDestroyView() {
super.onDestroyView()
// Binding 객체 해제
_binding = null
}
}
- 위 코드는 newInstance 메소드로 전달받은 데이터를 Bundle 에 담고 onCreate 또는 onViewCreated 에서 Bundle 로부터 데이터를 추출하여 사용함, param1 이라는 전역 변수에 받아온 데이터를 저장하고 사용함
< 실행 결과 >
[ 3 ] Fragment -> Activity
프래그먼트에서 액티비티로 데이터를 전달할 때는 콜백 인터페이스를 정의하고 해당 인터페이스를 액티비티가 구현하도록 함. 프래그먼트는 이 인터페이스를 사용하여 액티비티에 데이터를 전달함
< SecondFragment.kt >
= 데이터를 보내는 코드
private const val ARG_PARAM1 = "param1"
class SecondFragment : Fragment() {
private var param1: String? = null
private var _binding: FragmentSecondBinding? = null
private val binding get() = _binding!!
// [3] 에 필요
private var listener: FragmentDataListener? = null
// onAttach() MainActivity 와 SecondFragment 를 연결시키기 위함
// context 는 MainAcitivy 에서 온 것임
override fun onAttach(context: Context) {
super.onAttach(context)
// [3] SecondFragment -> Activity
if (context is FragmentDataListener) { // is 구문은 타입을 체크함, Main 안에 FragmentDataListener 가 있는지 확인
listener = context
} else {
throw RuntimeException("$context must implement FragmentDataListener")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// [2] Fragment -> Fragment
binding.tvSecondFragment.text = param1
// [3] SecondFragment -> Activity
binding.sendActivity.setOnClickListener {
val dataToSend = "Hello from SecondFragment" // 전달할 데이터
listener?.onDataReceived(dataToSend)
// listener 는 main 에서 온 context 가 할당되어 있음, onDataReceived() 는 Main 상속받고 있고 거기로 전달됨
}
}
companion object {
@JvmStatic
fun newInstance(param1: String) =
// [1] Activity -> FirstFragment
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
override fun onDestroy() {
super.onDestroy()
// Binding 객체 해제
_binding = null
listener = null
}
}
- 위 코드는 FragmentDataListener 라는 인터페이스를 정의하고 프래그먼트가 액티비티에 붙을 때 onAttach 액티비티가 이 인터페이스를 구현했는지 확인함. 버튼 클릭 리스너에서 onDataReceived 메소드를 호출하여 데이터를 액티비티에 전달함
< FragmentDataListener.kt >
= 인터페이스
package com.example.s_fragment
// [3] SecondFragment -> Activity 로 보내려면 interface 가 필요함
interface FragmentDataListener {
fun onDataReceived(data: String)
}
- 위처럼 메소드를 정의만 하고 구현되지 않은 인터페이스이기 때문에 onDataReceived 메소드는 데이터를 받는 MainActivity 에서 오버라이딩을 해 줘야 한다.
< MainActivity.kt >
= 데이터를 받는 코드
// FragmentDataListener 를 상속받아 onDataReceived() 메소드 override 해 줘야 함
class MainActivity : AppCompatActivity(), FragmentDataListener {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.run {
fragment1Btn.setOnClickListener{
// [1] Activity -> FirstFragment
val dataToSend = "Hello First Fragment! \n From Activity"
val fragment = FirstFragment.newInstance(dataToSend)
setFragment(fragment)
}
fragment2Btn.setOnClickListener {
// [1] Activity -> SecondFragment
val dataToSend = "Hello Second Fragment!\n From Activity"
val fragment = SecondFragment.newInstance(dataToSend)
setFragment(fragment)
}
}
setFragment(FirstFragment()) // 프로그램이 시작될 때 첫 화면으로 뜰 프래그먼트 화면 뿌리기
}
private fun setFragment(frag : Fragment) {
supportFragmentManager.commit {
replace(R.id.frameLayout, frag) // 인수로 받은 프래그먼트를 R.id.frameLayout 에 replace 함
setReorderingAllowed(true)
addToBackStack("")
}
}
// [3] SecondFragment -> Activity
override fun onDataReceived(data: String) {
// Fragment에서 받은 데이터를 처리
Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
}
}
- MainActivity 는 FragmentDataListener 인터페이스를 구현하고 onDataReceived 메소드를 오버라이드하여 프래그먼트로부터 데이터를 받는다. 받은 데이터는 Toast 메시지로 표시함.
< 실행 결과 >
'Android' 카테고리의 다른 글
(Android / Kotlin) - SharedPreferences (2) | 2024.04.30 |
---|---|
(Android Studio) - Dialog 사용하기 (0) | 2024.04.23 |
(Android Studio) - RecyclerView 사용 방법 (0) | 2024.04.16 |
(Android Studio) - Adapter View (0) | 2024.04.12 |
(Android Studio) - View Binding (0) | 2024.04.11 |