1. Activity -> Fragment
: 프래그먼트의 인스턴스를 생성하고 newInstance 메소드를 통해 데이터를 전달함
- Bundle 객체를 사용해 데이터를 프래그먼트의 인자 (arguments)로 설정하고, 이 인자를 프래그먼트가 받아 사용함
1) MainActivity.kt (보내는 코드)
- Fragment1Btn 클릭 리스너 안에서 FirstFragment의 인스턴스를 생성하고,
newInstance 메소드에 데이터를 전달해 프래그먼트를 설정(set)함
- Fragment2Btn에 대해서도 동일한 방법으로 SecondFragment에 데이터를 전달함
//변경한 코드. 프래그먼트의 데이터 전달, 보내는 부분
binding.run {
fragment1Btn.setOnClickListener {
// [1] Activity -> FirstFragment
val dataToSend = "Hello First Fragment! \n From Activity"
//FragmentBtn1 클릭 리스너 안에서 FirstFragment의 인스턴스를 생성하고,
// newInstance 메소드에 데이터를 설정해 생성된 프래그먼트에 전달함
val fragment = FirstFragment.newInstance(dataToSend)
//생성한 프래그먼트를 setFragment 안에 넣어서 프래그먼트를 설정, 화면에 표시함
setFragment(fragment)
}
fragment2Btn.setOnClickListener {
// [1] Activity -> SecondFragment
val dataToSend = "Hello Second Fragment!\n From Activity"
val fragment = SecondFragment.newInstance(dataToSend)
setFragment(fragment)
}
}
//프로그램이 시작될 때 FirstFragment를 뿌리라고 코드 작성함
setFragment(FirstFragment())
2) FirstFragment.kt (받는 코드)
- newInstance 메소드에서 전달받은 데이터를 Bundle에 담고, 프래그먼트의 인자로 설정
- onViewCreated에서는 인자로 받은 데이터를 텍스트 뷰에 설정함
//newInstance() 메서드는 프래그먼트가 생성될 때 데이터를 설정하고,
// onCreate() 메서드는 프래그먼트가 초기화(onCreate)될 때 데이터를 읽어와 param1에 집어넣고,
// onViewCreated에서 해당 param1을 사용해 텍스트 바꿈
1. view: 이 매개변수는 프래그먼트의 뷰(View) 객체임 즉, 프래그먼트가 화면에 표시될 때 이 메서드가 호출되며, 이때 해당 프래그먼트의 뷰가 전달됨 이 뷰를 사용하여 프래그먼트 내의 UI 요소들을 조작하거나 초기화할 수 있음 2. savedInstanceState: 이 매개변수는 이전에 저장된 프래그먼트의 상태를 나타내는 Bundle 객체임 액티비티의 `onCreate()` 메서드에서의 `savedInstanceState`와 유사한 역할을 함 이전 상태가 저장되어 있을 경우, 이를 사용하여 프래그먼트가 다시 생성되었을 때 이전 상태를 복원할 수 있음. 주로 화면 회전 또는 액티비티가 재생성될 때 이전 상태를 복원하는 데 사용됨 `onViewCreated()` 메서드는 프래그먼트의 뷰가 생성되고 초기화된 후에 호출되며, 이 메서드 내에서는 프래그먼트의 UI 요소들을 초기화하거나 데이터를 표시하는 작업 등을 수행할 수 있음. |
private var param1: String? = null
companion object {
@JvmStatic
fun newInstance(param1: String) =
// [1] Activity -> FirstFragment
// FirstFragment의 새 인스턴스를 생성
//새로운 프래그먼트 인스턴스를 생성할 때, 해당 프래그먼트에 전달할 데이터를 bundle에 설정함
FirstFragment().apply {
//전달받은 데이터를 bundle에 담음
//새 bundle을 생성하고, 이를 사용해 프래그먼트에 전달할 데이터를 설정
arguments = Bundle().apply {
//프래그먼트의 인자로 설정함... argument에 저장되는데... 위에 onCreate 코드 확인!
//param1 에는 Main에서 보낸 string이 들어있음.
//Bundle에 데이터 추가, "ARG_PARAM1" 상수를 키로 사용
putString(ARG_PARAM1, param1)
//putString(ARG_PARAM2, param2)
}
}
//newInstance() 메서드는 프래그먼트가 생성될 때 데이터를 설정하고,
// onCreate() 메서드는 프래그먼트가 초기화(onCreate)될 때 데이터를 읽어와 param1에 집어넣고,
// onViewCreated에서 해당 param1을 사용해 텍스트 바꿈
}
// [1] Activity -> FirstFragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//인자로 받은 데이터를 텍스트 뷰에 저장
// view가 create 될 때 param1을 텍스트에 넣어줌
binding.tvFrag1Text.text = param1
}
//ARG_PARAM1 을 사용하려면
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val receivedData = arguments?.getString(ARG_PARAM1)
binding.tvFrag1Text.text = receivedData
}
// 실행 순서대로는
1. 버튼 클릭 이벤트 처리: - 사용자가 `fragment1Btn` 또는 `fragment2Btn` 중 하나를 클릭하면 해당 버튼에 지정된 클릭 리스너가 실행됨 2. 프래그먼트 인스턴스 생성: - 클릭된 버튼에 따라 다른 데이터를 생성함 - 클릭된 버튼이 `fragment1Btn`이면 "Hello First Fragment! \n From Activity" 문자열을, `fragment2Btn`이면 "Hello Second Fragment!\n From Activity" 문자열을 생성함 - 각각의 데이터를 가지고 `FirstFragment.newInstance(dataToSend)`또는 `SecondFragment.newInstance(dataToSend)`를 호출하여 새로운 프래그먼트 인스턴스를 생성함 3. 프래그먼트에 데이터 전달: - `newInstance` 메서드는 새로운 프래그먼트 인스턴스를 생성하고, 생성된 프래그먼트의 인자로 전달할 데이터를 Bundle에 담아 설정함. 4. 프래그먼트 화면에 표시: - 생성된 프래그먼트를 화면에 표시하기 위해 `setFragment(fragment)` 메서드를 호출함 - 이렇게 하면 해당 프래그먼트가 화면에 나타남 5. 프래그먼트에서 데이터 활용: - 생성된 프래그먼트는 화면에 나타나면서 `onViewCreated` 메서드가 호출됨 - 이 메서드 내부에서는 프래그먼트에 전달된 데이터를 활용하여 UI를 업데이트할 수 있음 - 코드에서는 `param1` 변수에 전달된 데이터가 설정되고, 이를 TextView에 표시함 이렇게 하면 액티비티에서 프래그먼트로 데이터를 전달하고, 해당 프래그먼트에서 이를 활용하여 화면에 표시할 수 있음 |
// 내가 사용한 코드들
- first랑 secondFragment 에 binding 코드 추가...
- 프래그먼트들의 onCreateView 부분 코드 변경
-> 이전 코드 : 프래그먼트의 레이아웃 XML 파일을 사용하여 UI를 생성함
inflater.inflate() 메서드를 사용하여 프래그먼트의 XML 레이아웃 파일(R.layout.fragment_second)을 인플레이트하고,
그 결과물인 View를 반환함
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_second, container, false)
}
-> 이후 코드 : 뷰 바인딩을 사용하여 프래그먼트의 UI를 생성함.
먼저 FragmentSecondBinding.inflate() 메서드를 사용하여 데이터 바인딩 객체를 생성하고,
이를 사용하여 프래그먼트의 XML 레이아웃 파일을 인플레이트함.
그 후에는 바인딩 객체의 root 속성을 반환하여 프래그먼트의 최상위 View를 반환함
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSecondBinding.inflate(inflater,container,false)
return binding.root
}
=> 주요 차이점은 두 번째 코드에서 뷰 바인딩을 사용하여 프래그먼트의 레이아웃을 인플레이트하고,
해당 바인딩 객체를 통해 UI 요소에 쉽게 접근할 수 있다는 점임
첫 번째 방법에서는 뷰 바인딩 사용하지 않았으니 해당 프래그먼트의 레이아웃을 직접적으로 조작하기 어려움.
-> 프래그먼트의 UI 요소를 변경하거나 조작하기 위해서는
프래그먼트의 onCreateView() 메서드에서 반환된 View 객체를 통해(findV~) 해당 UI 요소를 찾고 조작해야함
2. Fragment -> Fragment
: 첫 번째 프래그먼트에서 두 번째 프래그먼트의 newInstance 메소드를 사용해 인스턴스를 생성하고, 데이터를 전달함
1) FirstFragment.kt (보내는 코드)
- 버튼 클릭 시 SecondFragment 의 새 인스턴스 생성, newInstance 메소드를 사용해 데이터 전달 후
프래그먼트 트랜잭션을 통해 두 번째 프래그먼트를 시작함
// [1] Activity -> FirstFragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//인자로 받은 데이터를 텍스트 뷰에 저장
// view가 create 될 때 param1을 텍스트에 넣어줌
binding.tvFrag1Text.text = param1
// [2] Fragment -> Fragment
binding.btnGofrag2.setOnClickListener {
val dataToSend = "Hello Fragment2! \n From Fragment1"
//버튼 클릭 시 SecondFragment 의 새 인스턴스를 생성하고,
// newInstance 메소드를 사용해 데이터를 전달한 후
val fragment2 = SecondFragment.newInstance(dataToSend)
//프래그먼트 트랜잭션을 통해 두 번째 프래그먼트를 시작함
// 프래그먼트에서 프래그먼트로!
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, fragment2)
.addToBackStack(null)
.commit()
}
}
2) SecondFragment.kt (받는 코드)
- newInstance 메소드로 전달받은 데이터를 Bundle에 담고,
onCreate 또는 onViewCreated에서 Bundle로부터 데이터를 추출해 사용
private const val ARG_PARAM1 = "param1"
class SecondFragment : Fragment() {
private var _binding: FragmentSecondBinding? = null
private val binding get() = _binding!!
// TODO: Rename and change types of parameters
private var param1: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
//param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment SecondFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String) =
// [1] Activity -> FirstFragment
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
//putString(ARG_PARAM2, param2)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// [2] Fragment -> Fragment
binding.tvFrag2Text.text = param1
}
override fun onDestroy() {
super.onDestroy()
// Binding 객체 해제
_binding = null
}
}
3. Fragment -> Activity
: 콜백 인터페이스를 정의하고, 해당 인터페이스를 액티비티가 구현하도록 해야 함
- 프래그먼트는 이 인터페이스를 사용해 액티비티에 데이터를 전달함
// 인터페이스 만들 때 다른 파일들에서 우클릭 > new > interface 해서 만들어도됨
1) SecondFragment.kt (보내는 코드)
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
//private const val ARG_PARAM2 = "param2"
// [3] SecondFragment -> Activity
interface FragmentDataListener {
fun onDataReceived(data: String)
}
/**
* A simple [Fragment] subclass.
* Use the [SecondFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class SecondFragment : Fragment() {
private var _binding: FragmentSecondBinding? = null
private val binding get() = _binding!!
// TODO: Rename and change types of parameters
private var param1: String? = null
//private var param2: String? = null
// [3] SecondFragment -> Activity
// 해당 인터페이스 타입의 리스너를 만들었음
private var listener: FragmentDataListener? = null
// [3] SecondFragment -> Activity
// MainActivity와 SecondFragent를 연결해야 하니까... context 사용
override fun onAttach(context: Context) {
super.onAttach(context)
// 프래그먼트가 액티비티에 붙을 때 액티비티가 이 인터페이스를 구현했는지 확인
// context는 MainActivity에서 들어온 것
//context를 검사해서 MainActivity 안에 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)
//param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment SecondFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String) =
// [1] Activity -> FirstFragment
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
//putString(ARG_PARAM2, param2)
}
}
}
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는 위에서 FragmentDataListener 타입으로 만들어놓고 MainActivity의 context가 할당된 상태.
// 인터페이스의 onDataReceived 를 호출함, 그걸 상속받고 있는 메인에서 토스트가 뜸
listener?.onDataReceived(dataToSend)
}
}
override fun onDestroy() {
super.onDestroy()
// Binding 객체 해제
_binding = null
// [3] SecondFragment -> Activity
listener = null
}
}
2) MainActivity.kt (받는 코드)
- FragmentDataListener 인터페이스 구현, onDataReceived 메소드를 오버라이드해 프래그먼트로부터 데이터 받기
- 데이터 받으면 토스트 띄우기
// [3] SecondFragment -> Activity (FragmentDataListener 인터페이스 구현.. 아래줄)
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"
//FragmentBtn1 클릭 리스너 안에서 FirstFragment의 인스턴스를 생성하고,
// newInstance 메소드에 데이터를 설정해 생성된 프래그먼트에 전달함
val fragment = FirstFragment.newInstance(dataToSend)
//생성한 프래그먼트를 setFragment 안에 넣어서 프래그먼트를 설정, 화면에 표시함
setFragment(fragment)
}
fragment2Btn.setOnClickListener {
// [1] Activity -> SecondFragment
val dataToSend = "Hello Second Fragment!\n From Activity"
val fragment = SecondFragment.newInstance(dataToSend)
setFragment(fragment)
}
}
//프로그램이 시작될 때 FirstFragment를 뿌리라고 코드 작성함
setFragment(FirstFragment())
}
private fun setFragment(frag: Fragment) {
//동적
//setFragment가 불리면 전달받은 프래그먼트로 전환하는 것
supportFragmentManager.commit {
//frameLayout에 전달받은 프래그먼트를 뿌릴 것임
replace(R.id.frameLayout, frag)
setReorderingAllowed(true)
addToBackStack("")
}
}
// [3] SecondFragment -> Activity
//MainActivity에서 해당 인터페이스 상속받고, 그 안에 구현체도 있어야 함...
// FragmentDataListener 리스너를 걸어두고, 그 리스너를 통해 onDataReceived가 발생하면
// 그 안으로 스트링 데이터가 들어올거고, 그걸 토스트로 띄울 것임. (onDataReceived를 override 해서 구현체 만들었음)
override fun onDataReceived(data: String) {
// Fragment에서 받은 데이터를 처리
Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
}
}
- MainActivity에서 해당 인터페이스 상속받고, 그 안에 구현체도 있어야 함...
FragmentDataListener 리스너를 걸어두고, 그 리스너를 통해
onDataReceived가 발생하면 그 안으로 스트링 데이터가 들어올거고,
그걸 토스트로 띄울 것임. (onDataReceived를 override 해서 구현체 만들었음)
'Android Studio' 카테고리의 다른 글
[Android 앱개발 숙련] 알림 - 개념 (# 주석확인) (0) | 2024.04.12 |
---|---|
[Android 앱개발 숙련] 프래그먼트의 데이터 전달 전체코드 + 주석 (# ) (0) | 2024.04.12 |
[Android 앱개발 숙련] 프래그먼트 예제 실습 (# 추가하기) (0) | 2024.04.11 |
[Android 앱개발 숙련] 프래그먼트 Fragment - 개념 (# 추가하기) (0) | 2024.04.11 |
[Android 앱개발 숙련] RecyclerView 리사이클러 뷰 (# 추가하기) (0) | 2024.04.11 |