-
[Android] Fragment 데이터 전달안드로이드 2024. 4. 11. 20:57
✏️ TIL(Today I Learned)
프래그먼트의 데이터 전달에 대해 알아보기 위해 프로젝트를 만들었다.
우선 액티비티에서 프래그먼트로 데이터를 전달하는 것을 확인하기 위해 메인 엑티비티와 프래그먼트를 2개 만들었다.
activity_main.xml에 FrameLayout을 만들어서 이곳에 프래그먼트를 위치할 것이다.<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <FrameLayout android:id="@+id/frameLayout" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="10dp" android:layout_marginEnd="10dp" app:layout_constraintBottom_toTopOf="@+id/fragment1_btn" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> </FrameLayout> <Button android:id="@+id/fragment1_btn" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="Frag1" android:textAllCaps="false" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/fragment2_btn" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/frameLayout" /> <Button android:id="@+id/fragment2_btn" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="Frag2" android:textAllCaps="false" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/fragment1_btn" app:layout_constraintTop_toBottomOf="@+id/frameLayout" /> </androidx.constraintlayout.widget.ConstraintLayout>
MainActivity에서 supportFragmentManager를 사용해 frameLayout에 동적으로 프래그먼트 추가했다.[1] Activity ➝ FirstFragment 데이터 보내기
그리고 각 버튼의 클릭 리스너 안에서 FirstFragment의 인스턴스를 생성하고, newInstance 메소드에 데이터를 전달하여 프래그먼트를 설정(set)했다. 클릭 시, 데이터 전달과 동시에 해당 프래그먼트로 전환된다.
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) setReorderingAllowed(true) addToBackStack("") } } // [3] SecondFragment -> Activity override fun onDataReceived(data: String) { // Fragment에서 받은 데이터를 처리 Toast.makeText(this, data, Toast.LENGTH_SHORT).show() } }
[1] Activity ➝ FirstFragment 데이터 받기
newInstance 메소드에서 전달받은 데이터를 Bundle에 담고, 프래그먼트의 인자로 설정면 된다.
onViewCreated에서는 인자로 받은 데이터를 텍스트 뷰에 설정했다.
// FirstFragment private const val ARG_PARAM1 = "param1" private var param1: String? = null companion object { @JvmStatic fun newInstance(param1: String) = // [1] Activity -> FirstFragment FirstFragment().apply { arguments = Bundle().apply { putString(ARG_PARAM1, param1) } } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // [1] Activity -> FirstFragment binding.tvFrag1Text.text = param1 }
그럼 Frag1버튼 클릭 시, 프래그먼트 가운데의 텍스트뷰에 받은 데이터가 표시된다.
[2] FirstFragment ➝ SecondFragment 데이터 보내기
프래그먼트에서 다른 프래그먼트로 데이터를 전달하기 위해서는 보내는 프래그먼트에서 "받을 프래그먼트의 newInstance 메소드"를 사용하여 인스턴스를 생성한 뒤, 데이터를 전달하면 된다.
아래 코드에서는 버튼 클릭 시 SecondFragment의 새 인스턴스를 생성하고, newInstance 메소드를 사용하여 데이터를 전달한 후 프래그먼트 트랜잭션을 통해 두 번째 프래그먼트를 시작한다.
// FirstFragment override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // [1] Activity -> FirstFragment binding.tvFrag1Text.text = param1 // [2] Fragment -> Fragment binding.btnSendFrag2.setOnClickListener{ val dataToSend = "Hello Fragment2! \n From Fragment1" val fragment2 = SecondFragment.newInstance(dataToSend) requireActivity().supportFragmentManager.beginTransaction() .replace(R.id.frameLayout, fragment2) .addToBackStack(null) .commit() } }
[2] SecondFragment ➝ FirstFragment 데이터 받기
newInstance 메소드로 전달받은 데이터를 Bundle에 담고, onCreate 또는 onViewCreated에서 Bundle로부터 데이터를 추출하여 사용하면 된다.
// SecondFragment 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) } } }
Send Fragment2 버튼 클릭 시, 두번째 프래그먼트로 전환되면서 데이터를 전달받아 텍스트뷰에 표시된다.
[3] SecondFragment ➝ MainActivity 데이터 보내기
프래그먼트에서 액티비티로 데이터를 전달할 때는 콜백 인터페이스를 정의하고, 해당 인터페이스를 액티비티가 구현하도록한다. 프래그먼트는 이 인터페이스를 사용하여 액티비티에 데이터를 전달한다.FragmentDataListener 인터페이스를 정의한 뒤 프래그먼트가 액티비티에 붙을 때(onAttach), 액티비티는 해당 인터페이스가 구현됐는지 확인한다. 버튼 클릭 리스너에서 onDataReceived 메소드를 호출하여 데이터를 액티비티에 전달한다.
private const val ARG_PARAM1 = "param1" interface FragmentDataListener { fun onDataReceived(data: String) } class SecondFragment : Fragment() { // [3] SecondFragment -> Activity private var listener: FragmentDataListener? = null override fun onAttach(context: Context) { super.onAttach(context) // [3] SecondFragment -> Activity if (context is FragmentDataListener) { listener = context } else { throw RuntimeException("$context must implement FragmentDataListener") } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // [2] Fragment -> Fragment binding.tvFrag2Text.text = param1 // [3] SecondFragment -> Activity binding.btnSendActivity.setOnClickListener{ val dataToSend = "Hello from SecondFragment!" listener?.onDataReceived(dataToSend) } } }
[3] MainActivity ➝ SecondFragment 데이터 받기
MainActivity는 FragmentDataListener 인터페이스를 구현하고, onDataReceived 메소드를 오버라이드하여 프래그먼트로부터 데이터를 받는다. 데이터를 받으면 Toast 메시지로 표시했다.
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) setReorderingAllowed(true) addToBackStack("") } } // [3] SecondFragment -> Activity override fun onDataReceived(data: String) { // Fragment에서 받은 데이터를 처리 Toast.makeText(this, data, Toast.LENGTH_SHORT).show() } }
send Activity 버튼 클릭 시, 아래 사진처럼 MainActivity에서 받은 데이터가 토스트로 표시된다.
📝 공부한 Kotlin 정리
프래그먼트 (Fragment)
01. 프래그먼트(Fragment)
프래그먼트는 액티비티 위에서 동작하는 모듈화된 사용자 인터페이스이다.
- 액티비티와 분리되어 독립적으로 동작할 수 없음
- 여러 개의 프래그먼트를 하나의 액티비티에 조합하여 창이 여러 개인UI를 구축할 수 있음
- 하나의 프래그먼트를 여러 액티비티에서 재 사용할 수 있음
02. 액티비티 vs 프래그먼트 비교
- Activity
시스템의 액티비티 매니저에서 인텐드를 해석해 액티비티간 데이터를 전달
- Fragment
액티비티의 프래그먼트 매니저에서 메소드로 프래그먼트간 데이터를 전달
03. 프래그먼트 생명주기
프래그먼트(Fragment)는 안드로이드 애플리케이션의 UI 부분을 모듈화하여 재사용할 수 있도록 해주는 구성 요소이다.
프래그먼트는 자체적인 생명주기(lifecycle)를 가지며, 액티비티의 생명주기와 밀접하게 연결되어 있다. 프래그먼트의 생명주기를 이해하는 것은 안드로이드 앱을 효율적으로 관리하고, 사용자에게 부드러운 인터페이스 경험을 제공하는 데 중요하다.
프래그먼트의 주요 생명주기 메서드:
- onAttach()
- 프래그먼트가 액티비티에 연결될 때 호출된다.
- 이 시점에서 프래그먼트는 액티비티와 아직 완전히 연결되지는 않았다.
- onCreate()
- 프래그먼트가 생성될 때 호출된다.
- 초기화 작업, 리소스 바인딩 등을 수행할 수 있다.
- onCreateView()
- 프래그먼트의 레이아웃을 인플레이트하는 곳이다.
- 뷰를 생성하고, 레이아웃을 설정한다.
- onActivityCreated()
- 액티비티의 onCreate() 메서드가 완료된 후 호출된다.
- 액티비티와 프래그먼트의 뷰가 모두 생성된 상태이므로, 뷰와 관련된 초기화를 수행한다.
- onStart()
- 프래그먼트가 사용자에게 보여질 준비가 되었을 때 호출된다.
- 필요한 리소스를 할당하거나, 애니메이션을 시작할 수 있다.
- onResume()
- 프래그먼트가 사용자와 상호작용할 수 있는 상태가 되었을 때 호출된다.
- 프래그먼트가 포그라운드에 있을 때 실행되는 작업을 여기서 처리한다.
- onPause()
- 프래그먼트가 일시정지될 때 호출된다.
- 상태 저장, 스레드 중지 등의 작업을 수행한다.
- onStop()
- 프래그먼트가 더 이상 사용자에게 보이지 않을 때 호출된다.
- 리소스 해제, 스레드 정지 등을 수행한다.
- onDestroyView()
- 프래그먼트의 뷰와 관련된 리소스를 정리할 때 호출된다.
- onDestroy()
- 프래그먼트가 파괴될 때 호출된다.
- 프래그먼트의 상태를 정리하고, 모든 리소스를 해제한다.
- onDetach()
- 프래그먼트가 액티비티로부터 분리될 때 호출된다.
- 프래그먼트가 액티비티와의 모든 연결을 해제한다.
각 단계에서는 프래그먼트의 생명주기에 맞춰 적절한 작업을 수행해야, 메모리 누수를 방지하고 애플리케이션의 성능을 최적화할 수 있다.
04. 프래그먼트를 사용하는 이유
- Activity의 복잡도를 줄일 수 있다.
- Activity를 적게 만들 수 있다.
- Activity로 화면을 계속 넘기는 것보다는 Fragment로 일부만 바꾸는 것이 자원 이용량이 적어 속도가 빠르다.
- 최소 1개의 Activity안에서 Fragment공간에 view만 넣으면 여러 Activity를 만들지 않아도 여러 화면을 보여줄 수 있다.
- Fragment를 사용하면 재사용할 수 있는 레이아웃을 분리해서 관리가 가능하다.
'안드로이드' 카테고리의 다른 글
[Android] [당근마켓 클론 코딩] 메인페이지 구현2 - 키워드 알림 (Notification) (0) 2024.04.17 [Android] [당근마켓 클론 코딩] 메인페이지 구현1 (RecyclerView/ addItemDecoration()/ Spinner/ ArrayAdapter) (0) 2024.04.16 [Android] 간단한 RecyclerView (0) 2024.04.11 [Android] MVVM 패턴 회원가입 유효성 검사 (0) 2024.04.09 [Android] 인스타그램 클론 코딩 팀 프로젝트 KPT 회고록 (0) 2024.04.08