1. Fragment
ํ๋๊ทธ๋จผํธ | Android ๊ฐ๋ฐ์ | Android Developers
์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. ํ๋๊ทธ๋จผํธ ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ์. Fragment๋ ์ฑ UI์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ถ๋ถ์ ๋
developer.android.com
- ์กํฐ๋นํฐ ์์์ ๋์ํ๋ ๋ชจ๋ํ๋ ์ฌ์ฉ์ ์ธํฐํ์ด์ค
- ์ฑ UI์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ถ๋ถ์ ๋ํ๋ (ํ๋ฉด ํ๋๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ์๋ํ๋ ๋ถ๋ถํ๋ฉด ์ฌ๋ฌ๊ฐ๋ก ๊ตฌํ)
- ๋จ๋ ์ผ๋ก ์กด์ฌํ ์ ์์ผ๋ฉฐ, ๋ฐ๋์ ์กํฐ๋นํฐ ๋๋ ๋ค๋ฅธ ํ๋๊ทธ๋จผํธ๊ฐ ํ์ํจ
- ์์ฒด ๋ ์ด์์์ ์ ์ ๋ฐ ๊ด๋ฆฌํ๊ณ ์์ฒด ์๋ช ์ฃผ๊ธฐ๋ฅผ ๋ณด์ ํ๋ฉฐ ์์ฒด ์ ๋ ฅ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌ
- ์ฅ์ 1. ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํจ (ํ ํ๋๊ทธ๋จผํธ์ ๊ฐ์ฒด๋ ๋ค์์ ์กํฐ๋นํฐ์์ ์์ฑ ๊ฐ๋ฅ, UI ์์ ๋ ๊ฐ์)
- ์ฅ์ 2. ๊ฐ๋ฒผ์ (4๋ ์ปดํฌ๋ํธ์ฒ๋ผ ์๋๋ก์ด๋ ์์คํ ์ด ์ง์ ๊ด๋ฆฌํ์ง ์๊ณ ํ๋๊ทธ๋จผํธ ๋งค๋์ ๊ฐ ๊ด๋ฆฌ)
- ํ๋๊ทธ๋จผํธ์ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ๋ ํธ์คํธ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ์ ์ผ๋ถ๊ฐ ๋๊ฑฐ๋ ์ฌ๊ธฐ์ ์ฐ๊ฒฐ
- ํ์, BottomNavigationView, ViewPager2์ ๊ฐ์ ์ผ๋ถ Android Jetpack ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋๊ทธ๋จผํธ์ ํธํ
๐ก ์กํฐ๋นํฐ VS ํ๋๊ทธ๋จผํธ
- ์กํฐ๋นํฐ: ์์คํ ์ ์กํฐ๋นํฐ ๋งค๋์ ์์ ์ธํ ํธ๋ฅผ ํด์ํด ์กํฐ๋นํฐ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ
- ํ๋๊ทธ๋จผํธ: ์กํฐ๋นํฐ์ ํ๋๊ทธ๋จผํธ ๋งค๋์ ์์ ๋ฉ์๋๋ก ํ๋๊ทธ๋จผํธ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ
- ์กํฐ๋นํฐ๋ก ํ๋ฉด์ ๊ณ์ ๋๊ธฐ๋ ๊ฒ๋ณด๋ค, ํ๋๊ทธ๋จผํธ๋ก ์ผ๋ถ๋ง ๋ฐ๊พธ๋ ๊ฒ์ด ์์ ์ด์ฉ๋์ด ์ ๊ณ ์๋๊ฐ ๋น ๋ฆ
- ํ๋๊ทธ๋จผํธ ์ฌ์ฉ์ ์ฅ์
- Fragment๋ฅผ ์ฌ์ฉํ๋ฉด Activity๋ฅผ ์ ๊ฒ ๋ง๋ค ์ ์์
(Fragment ๊ณต๊ฐ์ View๋ง ์ง์ด๋ฃ์ผ๋ฉด, ์ฌ๋ฌ Activity๋ฅผ ๋ง๋ค์ง ์์๋ ์ฌ๋ฌ ํ๋ฉด์ ๋ณด์ฌ ์ค ์ ์์) - Acitivity์ ๋ณต์ก๋๋ฅผ ์ค์
- ์ฌ์ฌ์ฉํ ์ ์๋ ๋ ์ด์์์ ๋ถ๋ฆฌํด์ ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅ
- Fragment๋ฅผ ์ฌ์ฉํ๋ฉด Activity๋ฅผ ์ ๊ฒ ๋ง๋ค ์ ์์
๐ก ํ๋๊ทธ๋จผํธ ์ ์ํ๊ธฐ
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)
}
- ์กํฐ๋นํฐ๋ฅผ ๋ง๋ค ๋์ ๋น์ทํ๊ฒ, ํ๋์ Kotlin ํ์ผ๊ณผ ํ๋์ XML๋ ์ด์์์ผ๋ก ์ ์
- ํ๋๊ทธ๋จผํธ๋ฅผ ์์ฑํ๋ ค๋ฉด Fragment์ ์๋ธํด๋์ค(๋๋ ์ด์ ๊ธฐ์กด ์๋ธํด๋์ค)๋ฅผ ์์ฑ
- ํ๋๊ทธ๋จผํธ์ ๋ํด ๋ ์ด์์์ ์ ๊ณตํ๋ ค๋ฉด ๋ฐ๋์ onCreateView() ์ฝ๋ฐฑ ๋ฉ์๋๋ฅผ ๊ตฌํ (์๋ ์๋ช ์ฃผ๊ธฐ ์ฐธ๊ณ )
- inflate()ํจ์๋ฅผ ํตํด์ xml ํ์ผ๋ช ์ ์ ๋ ฅํ์ฌ ๋ ์ด์์์ ๋ก๋
- ํ๋๊ทธ๋จผํธ๋ ๋ถ๋ถ ํ๋ฉด์ด๋ฏ๋ก ํ๋ฉด์ ํ์๋ ๋ทฐ๋ค์ ์ ์ํ๋ XML ํ์ผ์ /res/layout ํด๋ ์์ ์์ฑ
- ์๋๋ก์ด๋ ์คํ๋์ค์์ File > New > Fragment > Fragment(Blank)๋ฅผ ์ด์ฉํด์ ์ฝ๊ฒ ์์ฑ ๊ฐ๋ฅํ๋ค.
๐ก ํ๋๊ทธ๋จผํธ๋ฅผ ์ ์ ์ผ๋ก ์กํฐ๋นํฐ์ ๋ ์ด์์ ํ์ผ์ ์ถ๊ฐํ๊ธฐ
<?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>
- ํ๋๊ทธ๋จผํธ๋ฅผ ์กํฐ๋นํฐ์ ๋ ์ด์์ ํ์ผ ์์์ ์ ์ธ
- <fragment> ์์ android:name ํน์ฑ์ ๋ ์ด์์ ์์์ ์ธ์คํด์คํํ Fragment ํด๋์ค๋ฅผ ์ง์
- โ
[์ค์] ๊ฐ ํ๋๊ทธ๋จผํธ์๋ ์กํฐ๋นํฐ๊ฐ ์ฌ์์๋๋ ๊ฒฝ์ฐ ํ๋๊ทธ๋จผํธ๋ฅผ ๋ณต๊ตฌํ๊ธฐ ์ํด ์์คํ
์ด ์ฌ์ฉํ ์ ์๋ ๊ณ ์ ํ ์๋ณ์๊ฐ ํ์ํ๋ค. ํ๋๊ทธ๋จผํธ์ ID๋ฅผ ์ ๊ณตํ๋ ๋ฐ์๋ ๋ค์๊ณผ ๊ฐ์ ์ธ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
- ๊ณ ์ ํ ID์ ํจ๊ป android:id ์์ฑ์ ์ ๊ณตํ๋ค.
- ๊ณ ์ ํ ๋ฌธ์์ด๊ณผ ํจ๊ป android:tag ์์ฑ์ ์ ๊ณตํ๋ค.
- ์์ ๋ ๊ฐ์ง ์ค ์ด๋ ๊ฒ๋ ์ ๊ณตํ์ง ์์ผ๋ฉด, ์์คํ ์ ์ปจํ ์ด๋ ๋ทฐ์ ID๋ฅผ ์ฌ์ฉํ๋ค.
๐ก ํ๋๊ทธ๋จผํธ๋ฅผ ๋์ ์ผ๋ก ์ฝํ๋ฆฐ ์ฝ๋์์ ์ถ๊ฐํ๊ธฐ
build.gradle์ ์๋ ์ฝ์
dependencies {
val fragment_version = "1.6.2"
...
implementation("androidx.fragment:fragment-ktx:$fragment_version")
}
supportFragmentManager.commit {
replace(R.id.frameLayout, frag)
setReorderingAllowed(true)
addToBackStack("")
}
- supportFragmentManager
์ฌ์ฉ์ ์ํธ์์ฉ์ ์๋ตํด Fragment๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์ญ์ ํ๋ ๋ฑ์ ์์ ์ ํ ์ ์๊ฒ ํด์ฃผ๋ ๋งค๋์ - replace
์ด๋ ํ๋ ์ ๋ ์ด์์์ ๋์ธ ๊ฒ์ด๋, ์ด๋ค ํ๋๊ทธ๋จผํธ๋ ์ค์ - setReorderingAllowed
์ ๋๋ฉ์ด์ ๊ณผ ์ ํ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋๋ก ํธ๋์ญ์ ๊ณผ ๊ด๋ จ๋ ํ๋๊ทธ๋จผํธ์ ์ํ ๋ณ๊ฒฝ์ ์ต์ ํ - addToBackStack
๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ ํด๋ฆญ์ ๋ค์ ์ก์ (์ด์ fragment๋ก ๊ฐ๊ฑฐ๋ ์ฑ์ด ์ข ๋ฃ๋๊ฑฐ๋)
๐ก ์ฌ์ฉ ์์
0. ์ ํ๋๊ทธ๋จผํธ ๋ ๊ฐ ์์ฑ
FirstFragment, SecondFragment
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"
android:layout_width="match_parent"
android:background="#F19B9B"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ํv๋๊ทธ๋จผํธ 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"/>
</androidx.constraintlayout.widget.ConstraintLayout>
2. 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"
android:layout_width="match_parent"
android:background="#C785D3"
android:layout_height="match_parent">
<TextView
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"/>
</androidx.constraintlayout.widget.ConstraintLayout>
3. ์ ์ ์ผ๋ก ์ถ๊ฐ
activity_main.xml ํ์ผ์ ์ด๊ณ , fragment๋ฅผ ํ์ํ FrameLayout๊ณผ ๋ฒํผ 2๊ฐ ์ถ๊ฐ
<?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>
4. MainActivity.kt์ Fragment ์ถ๊ฐ
MainActivity.kt ์์
class MainActivity : AppCompatActivity() {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.apply {
fragment1Btn.setOnClickListener{
setFragment(FirstFragment())
}
fragment2Btn.setOnClickListener {
setFragment(SecondFragment())
}
}
setFragment(FirstFragment())
}
private fun setFragment(frag : Fragment) {
supportFragmentManager.commit {
replace(R.id.frameLayout, frag)
setReorderingAllowed(true)
addToBackStack("")
}
}
}
2. Frament Life Cycle
ํ๋๊ทธ๋จผํธ(Fragment)๋ ์๋๋ก์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ UI ๋ถ๋ถ์ ๋ชจ๋ํํ์ฌ ์ฌ์ฌ์ฉํ ์ ์๋๋ก ํด์ฃผ๋ ๊ตฌ์ฑ ์์์ด๋ค. ํ๋๊ทธ๋จผํธ๋ ์์ฒด์ ์ธ ์๋ช ์ฃผ๊ธฐ(lifecycle)๋ฅผ ๊ฐ์ง๋ฉฐ, ์กํฐ๋นํฐ์ ์๋ช ์ฃผ๊ธฐ์ ๋ฐ์ ํ๊ฒ ์ฐ๊ฒฐ๋์ด ์๋ค. ํ๋๊ทธ๋จผํธ์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ์ดํดํ๋ ๊ฒ์ ์๋๋ก์ด๋ ์ฑ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ , ์ฌ์ฉ์์๊ฒ ๋ถ๋๋ฌ์ด ์ธํฐํ์ด์ค ๊ฒฝํ์ ์ ๊ณตํ๋ ๋ฐ ์ค์ํ๋ค. ๋ฐ๋ผ์ ํ๋๊ทธ๋จผํธ ์๋ช ์ฃผ๊ธฐ๋ฅผ ์ดํดํ๊ณ , ๋ฉ๋ชจ๋ฆฌ ๋ฆญ(๋์)์ด ๋ฐ์ํ์ง ์๋๋ก ์ฝ๋๋ฅผ ์์ฑํ์.
onAttach()
- ํ๋๊ทธ๋จผํธ๊ฐ ์กํฐ๋นํฐ์ ์ฐ๊ฒฐ๋ ๋ ํธ์ถ
- ์ด ์์ ์์ ํ๋๊ทธ๋จผํธ๋ ์กํฐ๋นํฐ์ ์์ง ์์ ํ ์ฐ๊ฒฐ๋์ง๋ ์์
onCreate()
- Fragment๊ฐ ์์ฑ(CREATED) ๋ ์ํฉ
- ์ด ์์ ์๋ ์์ง Fragment View ๊ฐ ์์ฑ๋์ง ์์๊ธฐ ๋๋ฌธ์ Fragment ์ View ์ ๊ด๋ จ๋ ์์ ์ ๋๋ฉด ์ ๋จ
- onCreate() ์ฝ๋ฐฑ ์์ ์๋ Bundle ํ์ ์ผ๋ก savedInstanceState ํ๋ผ๋ฏธํฐ๊ฐ ํจ๊ป ์ ๊ณต๋๋๋ฐ, ์ด๋ onSaveInstanceState() ์ฝ๋ฐฑ ํจ์์ ์ํด ์ ์ฅ๋ Bundle ๊ฐ
- savedInstanceState ํ๋ผ๋ฏธํฐ๋ ํ๋๊ทธ๋จผํธ๊ฐ ์ฒ์ ์์ฑ ๋์ ๋๋ง null ๋ก ๋์ด์ค๋ฉฐ, onSaveInstanceState() ํจ์๋ฅผ ์ฌ์ ์ํ์ง ์์๋๋ผ๋ ๊ทธ ์ดํ ์ฌ์์ฑ๋ถํฐ๋ non-null ๊ฐ์ผ๋ก ๋์ด ์ด
- ์ด๊ธฐํ ์์ , ๋ฆฌ์์ค ๋ฐ์ธ๋ฉ ๋ฑ์ ์ํ
onCreateView(), onViewCreated()
- ํ๋๊ทธ๋จผํธ์ ๋ ์ด์์์ ์ธํ๋ ์ดํธํ๋ ๊ณณ
- onCreateView()์ ๋ฐํ๊ฐ์ผ๋ก ์ ์์ ์ธ Fragment View ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ์ ๋๋ง Fragment View ์ Lifecycle ์์ฑ
- onCreateView()๋ฅผ ์ฌ์ ์ ํ์ฌ Fragment View๋ฅผ ์ง์ ์์ฑํ๊ณ inflate ํ ์ ์์ง๋ง, LayoutId๋ฅผ ๋ฐ๋ Fragment์ ์์ฑ์๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ๋ฆฌ์์ค ์์ด๋ ๊ฐ์ ํตํด onCreateView() ์ฌ์ ์ ์์ด๋ Fragment View ๋ฅผ ์์ฑํ ์ ์์
- onCreateView() ๋ฅผ ํตํด ๋ฐํ๋ View ๊ฐ์ฒด๋ onViewCreated() ์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ
- ํด๋น ์์ ์์ Fragment View์ Lifecycle ์ด INITIALIZED ์ํ๋ก ์ ๋ฐ์ดํธ
- ๋ทฐ๋ฅผ ์์ฑํ๊ณ ๋ ์ด์์์ ์ค์
onViewCreated()์์ View ์ด๊ธฐ๊ฐ ์ค์ , LiveData ์ต์ ๋น, RecyclerView/ViewPager2 ์ ์ฌ์ฉ๋ Adapter ์ธํ ๋ฑ
onViewStateRestored()
- ์ ์ฅํด๋ ๋ชจ๋ state ๊ฐ์ด Fragment์ View ๊ณ์ธต๊ตฌ์กฐ์ ๋ณต์ ๋์ ๋ ํธ์ถ
- ์ฌ๊ธฐ์๋ถํฐ๋ ์ฒดํฌ๋ฐ์ค ์์ ฏ์ด ํ์ฌ ์ฒดํฌ ๋์ด์๋์ง ๋ฑ ๊ฐ ๋ทฐ์ ์ํ๊ฐ์ ์ฒดํฌํ ์ ์์
- View lifecycle owner ๋ ์ด๋ INITIALIZED ์ํ์์ CREATED ์ํ๋ก ๋ณ๊ฒฝ
onStart()
- Fragment๊ฐ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง ์ค๋น๊ฐ ๋์์ ๋ ํธ์ถ
- ์ฃผ๋ก Fragment๊ฐ attach๋์ด์๋ Activity์ onStart() ์์ ๊ณผ ์ ์ฌ
- ์ด ์์ ๋ถํฐ๋ Fragment์ child FragmentManager๋ฅผ ํตํด FragmentTransaction์ ์์ ํ๊ฒ ์ํ
- Fragment View ์ Lifecycle ๋ํ STARTED๋ก ๋ณ๊ฒฝ
- ํ์ํ ๋ฆฌ์์ค๋ฅผ ํ ๋นํ๊ฑฐ๋, ์ ๋๋ฉ์ด์ ์ ์์
onResume()
- ๋ชจ๋ Animator์ Transitionํจ๊ณผ๊ฐ ์ข ๋ฃ๋๊ณ , ํ๋๊ทธ๋จผํธ๊ฐ ์ฌ์ฉ์์ ์ํธ์์ฉํ ์ ์๋ ์ํ๊ฐ ๋์์ ๋ ํธ์ถ
- ์ฃผ๋ก Fragment๊ฐ attach๋์ด์๋ Activity์ onResume() ์์ ๊ณผ ์ ์ฌ
- ์ฌ์ฉ์๊ฐ ํ๋๊ทธ๋จผํธ์ ์ํธ์์ฉ ํ๊ธฐ์ ์ ์ ํ ์ํ
- ๋ฐ๋๋ก onResume() ์ด ํธ์ถ๋์ง ์์ ์์ ์์๋ ์ ๋ ฅ์ ์๋ํ๊ฑฐ๋ ํฌ์ปค์ค๋ฅผ ์ค์ ํ๋ ๋ฑ์ ์์ ์ ๋จ
- ํ๋๊ทธ๋จผํธ๊ฐ ํฌ๊ทธ๋ผ์ด๋์ ์์ ๋ ์คํ๋๋ ์์ ์ ์ฌ๊ธฐ์ ์ฒ๋ฆฌ
onPause()
- ํ๋๊ทธ๋จผํธ๊ฐ ์ผ์์ ์ง๋ ๋ ํธ์ถ
- ์ฌ์ฉ์๊ฐ Fragment ๋ฅผ ๋ ๋๊ธฐ ์์ํ์ง๋ง Fragment๋ ์ฌ์ ํ visible ์ผ ๋ ํธ์ถ
- Fragment์ View ์ Lifecycle์ด PAUSED ๊ฐ ์๋ STARTED๊ฐ ๋จ
(์๋ฐํ ๋ฐ์ง๋ฉด Lifecycle ์ PAUSE ์ STOP ์ ํด๋นํ๋ ์ํ๊ฐ ์์)
onStop()
- Fragment๊ฐ ๋์ด์ ํ๋ฉด์ ๋ณด์ฌ์ง์ง ์๊ฒ ๋ ๋ ํธ์ถ
- ๋ถ๋ชจ ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ๊ฐ ์ค๋จ๋์ ๋ ๋ฟ๋ง ์๋๋ผ, ๋ถ๋ชจ ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ์ ์ํ๊ฐ ์ ์ฅ๋ ๋๋ ํธ์ถ
- Fragment ์ View ์ Lifecycle ์ CREATED ์ํ๊ฐ ๋จ
- API 28 ๋ฒ์ ์ ๊ธฐ์ ์ผ๋ก ํจ์ ํธ์ถ ์์
ON_STOP Event -> onStop() -> (์ํ ์ ์ฅ) -> onSaveInstanceState()
onDestroyView()
- ํ๋๊ทธ๋จผํธ์ ๋ทฐ์ ๊ด๋ จ๋ ๋ฆฌ์์ค๋ฅผ ์ ๋ฆฌํ ๋ ํธ์ถ
- ๋ชจ๋ exit animation ๊ณผ transition ์ด ์๋ฃ๋๊ณ , Fragment๊ฐ ํ๋ฉด์ผ๋ก๋ถํฐ ๋ฒ์ด๋ฌ์ ๊ฒฝ์ฐ ํธ์ถ
- Fragment View ์ Lifecycle ์ DESTROYED ์ํ๊ฐ ๋จ
- ์ด ์์ ๋ถํฐ๋ getViewLifecycleOwnerLiveData() ์ ๋ฆฌํด๊ฐ์ผ๋ก null ์ด ๋ฐํ
- ์ด ์์ ๋ถํฐ๋ ๊ฐ๋น์ง ์ปฌ๋ ํฐ์ ์ํด ์๊ฑฐ๋ ์ ์๋๋ก Fragment View์ ๋ํ ๋ชจ๋ ์ฐธ์กฐ๊ฐ ์ ๊ฑฐ๋์ด์ผ ํจ
(์์ง ํ๋๊ทธ๋จผํธ ์์ฒด๋ ๋ฉ๋ชจ๋ฆฌ์ ๋จ์์์ผ๋ฏ๋ก, ํ๋๊ทธ๋จผํธ ๋ทฐ์ ๋ํ ๋ชจ๋ ์ฐธ์กฐ๋ฅผ ์ ๊ฑฐํด์ผ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง)
onDestroy()
- Fragment ๊ฐ ์ ๊ฑฐ๋๊ฑฐ๋ FragmentManager ๊ฐ destroy ๋์ ๊ฒฝ์ฐ ํธ์ถ
- ํ๋๊ทธ๋จผํธ์ Lifecycle ์ DESTROYED ์ํ๋ก ๋ณ๊ฒฝ
- ํ๋๊ทธ๋จผํธ์ ์ํ๋ฅผ ์ ๋ฆฌํ๊ณ , ๋ชจ๋ ๋ฆฌ์์ค๋ฅผ ํด์
onDetach()
- ํ๋๊ทธ๋จผํธ๊ฐ ์กํฐ๋นํฐ๋ก๋ถํฐ ๋ถ๋ฆฌ๋ ๋ ํธ์ถ
- ํ๋๊ทธ๋จผํธ๊ฐ ์กํฐ๋นํฐ์์ ๋ชจ๋ ์ฐ๊ฒฐ์ ํด์
3. Fragment ๋ฐ์ดํฐ ์ ๋ฌ ๋ฐฉ์
- ํ๋๊ทธ๋จผํธ ๊ฐ์ ๋ฐ์ดํฐ ์ ๋ฌ์ ์๋๋ก์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฐ์ฑ๊ณผ ๋ชจ๋์ฑ์ ํฅ์์ํค๋ ์ค์ํ ๊ธฐ๋ฅ
1. Activity → Fragment
- ์กํฐ๋นํฐ์์ ํ๋๊ทธ๋จผํธ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ๋
- ํ๋๊ทธ๋จผํธ์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ newInstance ๋ฉ์๋๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ
- Bundle ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํ๋๊ทธ๋จผํธ์ ์ธ์(arguments)๋ก ์ค์ ํ๊ณ , ์ด ์ธ์๋ฅผ ํ๋๊ทธ๋จผํธ๊ฐ ๋ฐ์ ์ฌ์ฉ
MainActivity.kt (๋ณด๋ด๋ ์ฝ๋)
binding.run {
fragment1Btn.setOnClickListener{
val dataToSend = "Hello First Fragment! \n From Activity"
val fragment = FirstFragment.newInstance(dataToSend)
setFragment(fragment)
}
fragment2Btn.setOnClickListener {
val dataToSend = "Hello Second Fragment!\n From Activity"
val fragment = SecondFragment.newInstance(dataToSend)
setFragment(fragment)
}
- Fragment1Btn ํด๋ฆญ ๋ฆฌ์ค๋ ์์์ FirstFragment์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ,
newInstance ๋ฉ์๋์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ์ฌ ํ๋๊ทธ๋จผํธ๋ฅผ ์ค์ (set) - Fragment2Btn์ ๋ํด์๋ ๋์ผํ ๋ฐฉ๋ฒ์ผ๋ก SecondFragment์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ
FirstFragment.kt (๋ฐ๋ ์ฝ๋):
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
}
- newInstance ๋ฉ์๋์์ ์ ๋ฌ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ Bundle์ ๋ด๊ณ , ํ๋๊ทธ๋จผํธ์ ์ธ์๋ก ์ค์
- onViewCreated์์๋ ์ธ์๋ก ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ํ ์คํธ ๋ทฐ์ ์ค์
2. Fragment → Fragment
- ํ ํ๋๊ทธ๋จผํธ์์ ๋ค๋ฅธ ํ๋๊ทธ๋จผํธ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ๋
- ์ฒซ ๋ฒ์งธ ํ๋๊ทธ๋จผํธ์์ ๋ ๋ฒ์งธ ํ๋๊ทธ๋จผํธ์ newInstance ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ธ์คํด์ค๋ฅผ ์์ฑ, ๋ฐ์ดํฐ ์ ๋ฌ
FirstFragment.kt (๋ณด๋ด๋ ์ฝ๋)
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()
}
- ๋ฒํผ ํด๋ฆญ ์ SecondFragment์ ์ ์ธ์คํด์ค๋ฅผ ์์ฑ
- newInstance ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ํ ํ๋๊ทธ๋จผํธ ํธ๋์ญ์ ์ ํตํด ๋ ๋ฒ์งธ ํ๋๊ทธ๋จผํธ๋ฅผ ์์
SecondFragment.kt (๋ฐ๋ ์ฝ๋)
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 {
_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) =
// [1] Activity -> FirstFragment
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
override fun onDestroyView() {
super.onDestroyView()
// Binding ๊ฐ์ฒด ํด์
_binding = null
}
}
- newInstance ๋ฉ์๋๋ก ์ ๋ฌ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ Bundle์ ๋ด๊ณ , onCreate ๋๋ onViewCreated์์ Bundle๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ์ฌ ์ฌ์ฉ
3. Fragment → Activity
- ํ๋๊ทธ๋จผํธ์์ ์กํฐ๋นํฐ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ๋
- ์ฝ๋ฐฑ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ๊ณ , ํด๋น ์ธํฐํ์ด์ค๋ฅผ ์กํฐ๋นํฐ๊ฐ ๊ตฌํํ๋๋ก ํจ
- ํ๋๊ทธ๋จผํธ๋ ์ด ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ์ฌ ์กํฐ๋นํฐ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ
SecondFragment.kt (๋ณด๋ด๋ ์ฝ๋):
private const val ARG_PARAM1 = "param1"
interface FragmentDataListener {
fun onDataReceived(data: String)
}
class SecondFragment : Fragment() {
private var listener: FragmentDataListener? = null
private var param1: String? = 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 {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.tvFrag2Text.text = param1
binding.btnSendActivity.setOnClickListener{
val dataToSend = "Hello from SecondFragment!"
listener?.onDataReceived(dataToSend)
}
}
companion object {
@JvmStatic
fun newInstance(param1: String) =
// [1] Activity -> FirstFragment
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
override fun onDestroyView() {
super.onDestroyView()
// Binding ๊ฐ์ฒด ํด์
_binding = null
listener = null
}
}
- FragmentDataListener ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ๊ณ , ํ๋๊ทธ๋จผํธ๊ฐ ์กํฐ๋นํฐ์ ๋ถ์ ๋ (onAttach)
์กํฐ๋นํฐ๊ฐ ์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋์ง ํ์ธ - ๋ฒํผ ํด๋ฆญ ๋ฆฌ์ค๋์์ onDataReceived ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์กํฐ๋นํฐ์ ์ ๋ฌ
MainActivity.kt (๋ฐ๋ ์ฝ๋):
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()
}
}
- MainActivity๋ FragmentDataListener ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ,
onDataReceived ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ์ฌ ํ๋๊ทธ๋จผํธ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ - ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ผ๋ฉด Toast ๋ฉ์์ง๋ก ํ์
4. Fragment ๋ฐ์ดํฐ ์ ๋ฌ ๋ฐฉ์ (์ฌํ)
- FragmentManager์ Bundle๋ก Date๋ฅผ ๋ด์ ์ ๋ฌ
- Fragment Result API๋ฅผ ์ฌ์ฉํ์ฌ Data ์ ๋ฌ
- Fragment๊ฐ ๊ณตํต์ ViewModel(ex. HostActivity์ ViewModel)๋ก ์ ๋ฌ
- Jetpack์ Navigation์์ ์ ๊ณตํ๋ safe-args๋ก ์ ๋ฌ
๊ฐ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฅ๋จ์ ์ด ์์ผ๋ฉฐ ์ํฉ์ ๋ฐ๋ผ ์ ํฉํ ๋ฐฉ๋ฒ ์ฌ์ฉ
FragmentManager์ Bundle๋ก Data๋ฅผ ๋ด์ ์ ๋ฌ
- FragmentManager๋ Android์์ Fragment๋ฅผ ๊ด๋ฆฌํ๋ ์์คํ ์๋น์ค
- ์ด ๋ฐฉ๋ฒ์ Fragment ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๊ฐ์ฅ ์ง์ ์ ์ด๊ณ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ ์ค ํ๋
- ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ Fragment์ Bundle์ ์์ฑํ๊ณ , ๊ทธ ์์ ์ ๋ฌํ๋ ค๋ ๋ฐ์ดํฐ๋ฅผ ๋ด์
- ๊ทธ ํ์ setArguments() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ Bundle์ ์ ๋ฌํ๋ ค๋ Fragment์ ์ค์
- ์ ๋ฌ๋ฐ๋ Fragment์์๋ getArguments() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ Bundle์ ๋ฐ๊ณ , ๊ทธ ์์ ๋ด๊ธด ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ์ฌ ์ฌ์ฉ
- ๊ฐ๋จํ๊ณ ์ง๊ด์ ์ธ ๋ฐฉ๋ฒ์ด์ง๋ง, ๋ฐ์ดํฐ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ ๋ณต์กํ ๋ฐ์ดํฐ ์ ๋ฌ์๋ ์ ํฉํ์ง ์์
Fragment Result API๋ฅผ ์ฌ์ฉํ์ฌ Data ์ ๋ฌ
- Fragment Result API๋ Jetpack ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ๋ ๊ธฐ๋ฅ์ผ๋ก, Fragment ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฐ ์ฌ์ฉ
- ์ด ๋ฐฉ๋ฒ์ Fragment ๊ฐ์ ์์ฒญ๊ณผ ๊ฒฐ๊ณผ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฉ์ปค๋์ฆ์ ์ ๊ณต
- ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ Fragment์์๋ startActivityForResult() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ Fragment๋ฅผ ํธ์ถ
- ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ Fragment์์๋ setResult() ๋ฉ์๋๋ก ๊ฒฐ๊ณผ๋ฅผ ์ค์ , onActivityResult() ๋ฉ์๋๋ก ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌ
- requestKey๋ ์ฌ์ฉํ๋ Fragment์์ ์ด๋ค listener์๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ์ง ๊ฒฐ์ ํ๊ธฐ ์ํ ์๋ณ์๋ก ์ฌ์ฉ
- Fragment ๊ฐ์ ๋น๋๊ธฐ์ ์ธ ๋ฐ์ดํฐ ๊ตํ์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ ๋ฐฉ๋ฒ
Fragment๊ฐ ๊ณตํต์ ViewModel(ex. HostActivity์ ViewModel)๋ก ์ ๋ฌ
- ViewModel์ ์๋๋ก์ด๋ ์ํคํ ์ฒ ์ปดํฌ๋ํธ ์ค ํ๋๋ก, ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ณ UI์ ๊ด๋ จ๋ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌ
- Fragment๋ค์ด ๊ณต์ ํ๋ ViewModel์ ์์ฑํ์ฌ ๊ทธ ์์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
- Fragment๋ค์ ํด๋น ViewModel์ ์ฐธ์กฐํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฑฐ๋ ์ธ ์ ์์
(ViewModel์ Activity์ lifecycle๋ณด๋ค ์ค๋ ์ด์์๋ ๊ฒ์ด ๋ณด์ฅ๋๊ธฐ ๋๋ฌธ์,
์์ ํ๊ฒ ๊ณตํต์ Activity์ ViewModel์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ์ ๋ฌ์ด ๊ฐ๋ฅ) - ์ฌ๋ฌ Fragment ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ๊ณ ์ ์งํ๊ธฐ ์ฉ์ดํ๋ฉฐ, ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ์ ์งํ๋ ๋ฐ ๋์์ด ๋๋ ๋ฐฉ๋ฒ
Jetpack์ Navigation์์ ์ ๊ณตํ๋ safe-args๋ก ์ ๋ฌ
- Jetpack์ Navigation์ Android ์ฑ์์ ํ๋ฉด ๊ฐ์ ์ด๋์ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ
- safe-args๋ Navigation ๊ทธ๋ํ์์ ๋ชฉ์ ์ง ๊ฐ์ ์์ ํ๊ฒ ์ธ์๋ฅผ ์ ๋ฌํ๊ธฐ ์ํ ๋ฉ์ปค๋์ฆ์ ์ ๊ณต
- Navigation ๊ทธ๋ํ์์ ๊ฐ ๋ชฉ์ ์ง์ ํ์ํ ๋งค๊ฐ๋ณ์๋ฅผ ์ ์, safe-args ํ๋ฌ๊ทธ์ธ์ ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฝ๋ ์๋ ์์ฑ
- ์ปดํ์ผ ํ์์ ์์ ์ฑ์ ๋ณด์ฅํ๊ณ ,๋ช ์์ ์ผ๋ก ์ ์๋ ์ธ์๋ฅผ ํตํด ๋ชฉ์ ์ง ๊ฐ์ ๋ฐ์ดํฐ ์ ๋ฌ์ ๊ฐ๋จํ๊ณ ์์ ํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ ๋ฐฉ๋ฒ
- Jetpack์ Navigation์ ์ฌ์ฉํ๋ ํ๋ก์ ํธ์์ ์ ์ฉํ๋ ๊ฒ์ ์ถ์ฒ
์ฐธ๊ณ ๋ธ๋ก๊ทธ1 (๋งํฌ)
์ฐธ๊ณ ๋ธ๋ก๊ทธ2 (๋งํฌ)