Android

(Android Studio) - 프래그먼트의 데이터 전달

돗개진 2024. 4. 17. 21:06

[ 프래그먼트 간 데이터 전달 ] 

 

프래그먼트 간의 데이터 전달은 안드로이드 애플리케이션의 유연성과 모듈성을 향상시키는 중요한 기능임. 각 시나리오에 따라 데이터를 전달하는 방법을 정리해 보자.

 


 

[ 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 메시지로 표시함.

 

 

 

< 실행 결과 >