1-1. activity_main.xml
<?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"/>
<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>
1-2. MainActivity.kt
// [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()
}
}
2-1. fragment_first.xml
<?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:background="#F19B9B"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvFrag1Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="프래그먼트 1"
android:textAllCaps="false"
android:textSize="40sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Button
android:id="@+id/btnGofrag2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
tools:layout_editor_absoluteX="156dp"
tools:layout_editor_absoluteY="421dp"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
2-2. FirstFragment.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"
/**
* A simple [Fragment] subclass.
* Use the [FirstFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
private val binding get() = _binding!!
// TODO: Rename and change types of parameters
private var param1: String? = null
//private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 프래그먼트가 onCreate 될 때... 프래그먼트가 생성될 때 인자로 전달된 데이터 읽어옴
arguments?.let {
// 이 argument 안에 Bundle이라는 객체로 들어있는 it을 꺼내오는 것
// Bundle에서 getString 함
// 전역변수 param1에 받아온 string 값을 넣어줌
// let을 쓰는 이유 : argumenet가 null일 수도 있어서... null이 아닐때만 들어오게 ?.let임
// 전달받은 데이터가 있는 경우, "ARG_PARAM1" 상수를 사용해 해당 데이터를 가져와 param1 변수에 저장
param1 = it.getString(ARG_PARAM1)
//param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFirstBinding.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 FirstFragment.
*/
// TODO: Rename and change types and number of parameters
@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
// [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()
}
}
}
3-1. fragment_second.xml
<?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:background="#C785D3"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvFrag2Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="프래그먼트 2"
android:textAllCaps="false"
android:textSize="40sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Button
android:id="@+id/btnSendActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
tools:layout_editor_absoluteX="158dp"
tools:layout_editor_absoluteY="444dp"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
3-2. 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
}
}
'Android Studio' 카테고리의 다른 글
[Android 앱개발 숙련] 알림 - 예제, 권한 (# 확인...?) (0) | 2024.04.12 |
---|---|
[Android 앱개발 숙련] 알림 - 개념 (# 주석확인) (0) | 2024.04.12 |
[Android 앱개발 숙련] 프래그먼트의 데이터 전달 (# 추가하기) (0) | 2024.04.11 |
[Android 앱개발 숙련] 프래그먼트 예제 실습 (# 추가하기) (0) | 2024.04.11 |
[Android 앱개발 숙련] 프래그먼트 Fragment - 개념 (# 추가하기) (0) | 2024.04.11 |