코틀린(Kotlin)/TIL

[코틀린(Kotlin)] 프래그먼트 데이터 전달

초보왕보초 2024. 1. 7. 15:01
728x90
  • 액티비티 → 프래그먼트 데이터 전달
  • 프래그먼트 → 프래그먼트 데이터 전달
  • 프래그먼트 → 액티비티 데이터 전달

 

 

 

1. 액티비티에서 프래그먼트로 데이터를 전달하는 경우

MainActivity.kt (데이터를 보내는 액티비티)

더보기
package com.android.ex_view2

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import com.android.ex_view2.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        binding.run {
            btnFragment1.setOnClickListener {
                // [1] Activity -> FirstFragment
                val dataToSend = "Hello First Fragment!  \n From Activity"
                // FirstFragment의 인스턴스 생성, newInstance 메서드에 데이터를 전달하여 프래그먼트를 설정(set)
                val fragment = FirstFragment.newInstance(dataToSend)
                setFragment(fragment)
            }

            btnFragment2.setOnClickListener {
                // [1] Activity -> SecondFragment
                val dataToSend = "Hello Second Fragment! \n From Activity"
                // 21라인과 동일
                val fragment = SecondFragment.newInstance(dataToSend)
                setFragment(fragment)
            }
        }
        setFragment(FirstFragment())
    }

    private fun setFragment(frag: Fragment) {
        supportFragmentManager.commit {
            replace(R.id.framLayout, frag)
            setReorderingAllowed(true)
            addToBackStack("")
        }
    }
}
  1. 프래그먼트의 인스턴스를 생성하고, newInstance 메서드를 통해 데이터를 전달한다
  2. Bundle 객체를 사용하여 데이터를 프래그먼트의 인자(arguments)로 설정하고
  3. 이 인자를 프래그먼트가 받아서 사용한다

 

FirstFragment.kt (데이터를 받는 프래그먼트)

더보기
package com.android.ex_view2

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.android.ex_view2.databinding.FragmentFirstBinding

private const val ARG_PARAM1 = "param1"

class FirstFragment : Fragment() {
    private var param1: String? = null
    private val binding by lazy { FragmentFirstBinding.inflate(layoutInflater) }

    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? {
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // [1] Activity -> FirstFragment
        binding.tvFrag1Text.text = param1
    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String) =
            FirstFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                }
            }
    }
}
  1. newInstance 메서드에서 전달받은 데이터를 Bundle에 담고, 프래그먼트의 인자로 설정한다
  2. onViewCreated에서 인자로 받은 데이터를 텍스트 뷰에 설정한다

 

결과

 

 

2. 프래그먼트에서 프래그먼트로 데이터를 전달하는 경우

1번과 방법은 동일

FirstFragment.kt (데이터를 보내는 프래그먼트)

더보기
package com.android.ex_view2

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.android.ex_view2.databinding.FragmentFirstBinding

private const val ARG_PARAM1 = "param1"

class FirstFragment : Fragment() {
    private var param1: String? = null
    private val binding by lazy { FragmentFirstBinding.inflate(layoutInflater) }

    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? {
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // [1] Activity -> FirstFragment
        binding.tvFrag1Text.text = param1

        // [2] Fragment -> Fragment
        binding.btnGoFrag2.setOnClickListener {
            val dataToSend = "Hello Fragment2! \n From Fragment1"
            val fragment2 = SecondFragment.newInstance(dataToSend)

            requireActivity().supportFragmentManager.beginTransaction()
                .replace(R.id.framLayout, fragment2)
                .addToBackStack(null)
                .commit()
        }
    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String) =
            FirstFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                }
            }
    }
}
  1. (1번에서 35 ~ 43만 추가됨 && 나머지 과정 동일) fragment_first.xml에 버튼을 추가해 준다(btn_goFrag2)
  2. 버튼 클릭 시 SecondFragment의 새 인스턴스를 생성하고, newInstance 메서드를 사용하여 데이터를 전달한다
  3. 데이터를 전달한 후, 프래그먼트 트랜잭션을 통해 두 번째 프래그먼트를 시작한다

 

SecondFragment.kt(데이터를 받는 프래그먼트)

더보기
package com.android.ex_view2

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.android.ex_view2.databinding.FragmentFirstBinding
import com.android.ex_view2.databinding.FragmentSecondBinding

private const val ARG_PARAM1 = "param1"

class SecondFragment : Fragment() {
    private var param1: String? = null
    private val binding by lazy { FragmentSecondBinding.inflate(layoutInflater) }

    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? {
        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) =
            SecondFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                }
            }
    }
}
  1. (받는 코드는 1번과 완전 동일함) 
  2. newInstance 메서드로 전달받은 데이터를 Bundle에 담는다
  3. onCreate 또는 onViewCreated에서 Bundle로부터 데이터를 추출하여 사용한다

 

결과

 

 

3. 프래그먼트에서 액티비티로 데이터를 전달하는 경우

  • 콜백 인터페이스를 정의하고, 해당 인터페이스를 액티비티가 구현하도록 해야 한다
  • 프래그먼트는 이 인터페이스를 사용하여 액티비티에 데이터를 전달한다

 

FragmentDataListener 인터페이스 생성

File → New → Kotlin Class/File → Interface를 이용하여 인터페이스 생성

package com.android.ex_view2

interface FragmentDataListener {
    fun onDataReceived(data: String)
}

 

SecondFragment.kt (데이터를 보내는 프래그먼트)

더보기
package com.android.ex_view2

import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.android.ex_view2.databinding.FragmentFirstBinding
import com.android.ex_view2.databinding.FragmentSecondBinding
import java.lang.RuntimeException

private const val ARG_PARAM1 = "param1"

class SecondFragment : Fragment() {

    // [3] SecondFragment -> Activity
    private var listener: FragmentDataListener? = null

    private var param1: String? = null

    private var _binding: FragmentSecondBinding? = null
    private val binding get() = _binding!!
//    private val binding by lazy { FragmentSecondBinding.inflate(layoutInflater) }

    override fun onAttach(context: Context) {
        super.onAttach(context)

        // [3] SecondFragment -> Activity
        // context is FragmentDataListener = MainActivity.kt 에 FragmentDataListener가 (구현 되어) 있는지 체크
        if (context is 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.tvFrag2Text.text = param1

        // [3] SecondFragment -> Activity
        binding.btnSendToActivity.setOnClickListener {
            val dataToSend = "Hello from SecondFragment!"
            listener?.onDataReceived(dataToSend)
        }
    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String) =
            SecondFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                }
            }
    }
}
  1. FragmentDataListener 인터페이스를 정의하고, 프래그먼트가 액티비티에 붙을 때(onAttach),
    액티비티가 이 인터페이스를 구현했는지 확인한다
  2. 버튼 클릭 리스너에서 onDataReceived 메서드를 호출하여 데이터를 액티비티에 전달한다

 

MainActivity.kt (데이터를 받는 액티비티)

더보기
package com.android.ex_view2

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import com.android.ex_view2.databinding.ActivityMainBinding

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 {
            btnFragment1.setOnClickListener {
                // [1] Activity -> FirstFragment
                val dataToSend = "Hello First Fragment!  \n From Activity"
                // FirstFragment의 인스턴스 생성, newInstance 메서드에 데이터를 전달하여 프래그먼트를 설정(set)
                val fragment = FirstFragment.newInstance(dataToSend)
                setFragment(fragment)
            }

            btnFragment2.setOnClickListener {
                // [1] Activity -> SecondFragment
                val dataToSend = "Hello Second Fragment! \n From Activity"
                // 21라인과 동일
                val fragment = SecondFragment.newInstance(dataToSend)
                setFragment(fragment)
            }
        }
        setFragment(FirstFragment())
    }

    private fun setFragment(frag: Fragment) {
        supportFragmentManager.commit {
            replace(R.id.framLayout, frag)
            setReorderingAllowed(true)
            addToBackStack("")
        }
    }

    // [3] SecondFragment -> Activity
    override fun onDataReceived(data: String) {
        // Fragment에서 받은 데이터를 처리
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
    }
}
  1. MainActivity는 FragmentDataListener 인터페이스를 구현하고, onDataReceived 메서드를 오버라이드해서
    프래그먼트로부터 데이터를 받는다
  2. 데이터를 받으면 Toast메시지로 표시하도록 한다

 

결과

 

728x90