프래그먼트(Fragment)
* 프래그먼트란?
- 프래그먼트는 한 개의 액티비티 화면 안에서 특정 영역만 교체하는 것(액티비티와 분리되어 독립적으로 동작 불가능)
- 여러 개의 프래그먼트를 하나의 액티비티에 조합할 수 있고, 하나의 프래그먼트를 여러 액티비에서 재사용할 수도 있음
* 액티비티와 프래그먼트 비교
Activity : 시스템의 액티비티 매니저에서 인텐트(intent)를 해석해 액티비티 간 데이터 전달
Fragement : 액티비티의 프래그먼트 매니저에서 메소드(method)로 프래그먼트간 데이터 전달
* 프래그먼트 사용 이유
- Activity로 화면을 계속 넘기는 것보다 Fragement로 일부만 바꾸는 게 자원 이용량이 적어 속도가 빠르기 때문
- 복잡도 감소
- 재사용할 수 있는 레이아웃을 분리해서 관리 가능 등
* 프래그먼트 정의 : Activity와 동일하게 하나의 코틀린 소스와 하나의 xml파일로 정의
gradle -> dependencies에 fragment 설정 추가
val fragment_version = "1.8.1"
implementation("androidx.fragment:fragment-ktx:$fragment_version")
Kotlin 소스 파일 생성
class FirstFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false)
}
* 프래그먼트를 액티비티 레이아웃 파일에 추가하는 방법은 총 두 가지
1. 프래그먼트 레이아웃 안에 선언하여 정적으로 추가
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<fragment
android:name="com.skmns.fragmentbasic.FirstFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment" />
</LinearLayout>
2. Kotlin 코드에서 동적으로 프래그먼트 추가하기
supportFragmentManager.commit {
replace(R.id.frameLayout, frag)
setReorderingAllowed(true)
addToBackStack("")
}
- supportFragmentManager : Fragment를 추가, 삭제 같은 작업을 할 수 있게 해주는 매니저
- replace : 어느 프레임 레이아웃에 띄울것이냐, 어떤 프래그먼트냐
- setReorderingAllowed : 애니메이션과 전환이 올바르게 작동하도록 트랜잭션과 관련된 프래그먼트의 상태 변경을 최적화
- addToBackStack : 뒤로가기 버튼 클릭시 다음 액션 (이전 fragment로 가거나 앱이 종료되거나)
(보통은 정적보단 동적을 많이 사용)
프래그먼트 데이터 전달
* 프래그먼트 데이터 전달 방식은 총 3가지
- Activity에서 Fragment로 데이터 전달
- Fragment에서 Fragment로 데이터 전달
- Fragment에서 Activity로 데이터 전달
1. Activity에서 Fragement로 데이터 전달
* 데이터 전달하는 Activity(MainActivity)
class MainActivity : AppCompatActivity() {
private val binding by lazy {ActivityMainBinding.inflate(layoutInflater)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.run {
fragment1Btn.setOnClickListener {
val dataToSend = "Hello First Fragment! \n From Activity"
val fragment = FirstFragment.newInstance(dataToSend)
setFragement(fragment)
}
fragment2Btn.setOnClickListener {
val dataToSend = "Hello Second Fragment! \n From Activity"
val fragment = SecondFragment.newInstance(dataToSend)
setFragement(fragment)
}
}
setFragement(FirstFragment())
}
private fun setFragement(frag : Fragment){
supportFragmentManager.commit {
replace(R.id.frameLayout, frag)
setReorderingAllowed(true)
addToBackStack("")
}
}
}
1 | 변수 dataTosend에는 출력하고자 하는 문구 작성 |
2 | 변수 fragment에는 dataTosend를 프래그먼트(FirstFragment.newInstance)로 넘겨주는 내용 작성 |
3 | setFragment를 통해 변수 fragment가 프래그먼트에 넘겨짐 |
4 | 밑에 fragment2Btn도 동일하게 진행 |
* 데이터 전달받는 Fragment(FirstFragment)
private const val ARG_PARAM1 = "param1"
class FirstFragment : Fragment() {
private val binding by lazy {FragmentFirstBinding.inflate(layoutInflater)}
private var param1: String? = null
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? {
// Inflate the layout for this fragment
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//1. Activity -> Fragment
binding.tvFrag1Text.text = param1
}
companion object {
@JvmStatic
fun newInstance(param1: String) =
FirstFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
}
1 | 액티비티(MainActivity)에서 변수 fragment가 newInstance로 들어옴 |
2 | newInstance의 파라미터인 param1에 변수 fragment가 들어옴 |
3 | param1에 변수 fragment가 담겨진 채로 밑에 onViewCreated에 들어감 |
4 | tvFrag1Text의 text에 param1(=fragment) 내용이 들어감 |
5 | SecondFragment도 FirstFragment와 동일하게 작성 |
* Fragment을 생성할 때 binding과 onViewCreated를 제외한 나머지는 자동 생성되니 그 점 참고
2. Fragment에서 Fragement로 데이터 전달
* 데이터를 전달하는 Fragment(FirstFragment ->)
private const val ARG_PARAM1 = "param1"
class FirstFragment : Fragment() {
private val binding by lazy {FragmentFirstBinding.inflate(layoutInflater)}
private var param1: String? = null
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? {
// Inflate the layout for this fragment
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//2. FirstFragment -> SecondFragment
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()
}
}
companion object {
@JvmStatic
fun newInstance(param1: String) =
FirstFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
}
1 | 변수 dataTosend에는 출력하고자 하는 문구 작성 |
2 | 변수 fragment2에는 dataTosend를 프래그먼트(SecondFragment.newInstance)로 넘겨주는 내용 작성 |
3 | 서포트프래그먼트매니저를 이용하여 변수 fragment2를 이동시킴 |
* 데이터를 전달받는 Fragment(-> SecondFregment)
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? {
// Inflate the layout for this fragment
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.tvFrag2Text.text = param1
}
companion object {
@JvmStatic
fun newInstance(param1: String) =
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
1 | 코드는 전반적으로 1번 유형과 동일 |
2 | binding에 관한 변수 선언 추가 |
3 | 맨 아래에 onDestroyView() 내용 추가 |
3. Fragment에서 Activity로 데이터 전달
interface를 하나 생성해야 함
* 인터페이스(FragmentDataListener)
interface FragmentDataListener {
fun onDataReceived(data : String)
}
* 데이터를 전달하는 Fragment(SecondFragment)
private const val ARG_PARAM1 = "param1"
class SecondFragment : Fragment() {
private var param1: String? = null
private var listener: FragmentDataListener? = null
private var _binding: FragmentSecondBinding? = null
private val binding get() = _binding!!
override fun onAttach(context: Context) {
super.onAttach(context)
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? {
// Inflate the layout for this fragment
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//2. FirstFragment -> SecondFragment로 데이터 전달
binding.tvFrag2Text.text = param1
binding.btnSendActivity.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)
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
listener = null
}
}
* 데이터를 전달받는 Activity(MainActivity)
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 {
val dataToSend = "Hello First Fragment! \n From Activity"
val fragment = FirstFragment.newInstance(dataToSend)
setFragement(fragment)
}
fragment2Btn.setOnClickListener {
val dataToSend = "Hello Second Fragment! \n From Activity"
val fragment = SecondFragment.newInstance(dataToSend)
setFragement(fragment)
}
}
setFragement(FirstFragment())
}
private fun setFragement(frag : Fragment){
supportFragmentManager.commit {
replace(R.id.frameLayout, frag)
setReorderingAllowed(true)
addToBackStack("")
}
}
override fun onDataReceived(data: String) {
Toast.makeText(this,data,Toast.LENGTH_SHORT).show()
}
}
'Android 앱개발 공부 > TIL(Today I Learned)' 카테고리의 다른 글
[Android] TIL 37일차 (0) | 2024.07.16 |
---|---|
[Android] TIL 36일차 (0) | 2024.07.15 |
[Android] TIL 34일차 (0) | 2024.07.11 |
[Android] TIL 33일차 (0) | 2024.07.10 |
[Android] TIL 32일차 - 2 (0) | 2024.07.09 |