#1 ์ด์ ๊ธ
[Android] Navigation - ํ๊ฒฝ ์ค์
#1 Navigation#1-1 ์กํฐ๋นํฐ ๋ฐ ํ๋๊ทธ๋จผํธ ๊ตฌ์ฑ์ ํธ๋ ๋์์ฆ ์๋๋ก์ด๋ ๊ฐ๋ฐ์ ํธ๋ ๋๋ ํ๋์ ์กํฐ๋นํฐ, ์ฌ๋ฌ ๊ฐ์ ํ๋๊ทธ๋จผํธ๋ค. ์ด๋ ๊ตฌ๊ธ์ ๊ถ์ฅ์ฌํญ์ด๊ธฐ๋ ํ๋ค. ์ด ํธ๋ ๋์์ ๋จ์ผ ์กํฐ
kenel.tistory.com
์ ๊ฒ์๊ธ์์ ์ด์ด์ง๋ค. ๋ณธ ๊ฒ์๊ธ์์ Navigation์ ์ฌ์ฉํ๋ ์ํ ์ฑ์ ๋ง๋ค์ด๋ณธ๋ค.
#2 Navigation ์ฌ์ฉํ๊ธฐ - Destination ํ๋๊ทธ๋จผํธ
#2-1 Navigation graph์ Destination ํ๋๊ทธ๋จผํธ ๋ฑ๋กํ๊ธฐ
์ฐ๋ฆฌ๊ฐ activity_main.xml์ ๋ค๋ฃฐ ๋ Code ๋ชจ๋, Split ๋ชจ๋, Design ๋ชจ๋ ์ค ํ๋๋ฅผ ์ ํํด ํธ์งํ๊ณค ํ๋ค. nav_graph.xml๋ ๊ทธ๋ ๋ค. nav_graph.xml์ Design ๋ชจ๋๋ก ์ฐ๋ค. ๊ทธ๋ฆฌ๊ณ New Destination ๋ฒํผ์ ํด๋ฆญํ๋ค.
Create new destination ํด๋ฆญ.
Fragment (Blank) ์ ํํ Next ๋ฒํผ ํด๋ฆญ.
์ ์ ํ ์ด๋ฆ(์ฌ๊ธฐ์๋ HomeFragment)์ ์ง์ด์ฃผ๊ณ Finish ๋ฒํผ ํด๋ฆญ.
Navigation graph์ Destination ํ๋๊ทธ๋จผํธ๊ฐ ๋ฑ๋ก๋๋ค. ์ด์ ๋์์, res/layout ๋๋ ํ ๋ฆฌ์ fragment_home.xml์ด ์๋ ์์ฑ๋์๊ณ , MainActivity.kt๊ฐ ์๋ ๋๋ ํ ๋ฆฌ์๋ HomeFragment.kt๊ฐ ์๋ ์์ฑ๋์๋ค.
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.example.navigationbasics.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home" />
</navigation>
nav_graph.xml์ Code ๋ชจ๋๋ก ๋ณด๋ฉด ์์ ๊ฐ๋ค.
#2-2 ์๋ ์์ฑ๋ fragment_home.xml ๊ฐ๋จํ ์์ ํ๊ธฐ
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeFragment">
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="์ด๋ฆ์ ์
๋ ฅํด์ฃผ์ธ์."
android:inputType="text"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="์ ์ก"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/editText"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
์๋์์ฑ๋ ์ฝ๋๋ FrameLayout์ด๋ค. ์ฌ์ฉํ๊ธฐ ํธํ ConstraintLayout์ผ๋ก ๋ฐ๊ฟจ๋ค. ์ถ๊ฐ๋ก, EditText์ Button๋ ๋ฃ์๋ค. ๋ง์ง๋ง์ผ๋ก ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ํด <layout> ํ๊ทธ๋ก ๊ฐ์ธ๊ณ xmlns ์์ฑ์ ์ต์ธ๊ณฝ ํ๊ทธ์ธ <layout>๋ก ์ฎ๊ธด๋ค.
#2-3 ์๋ ์์ฑ๋ HomeFragment.kt์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ์ ์ฉํ๊ธฐ
// package com.example.navigationbasics
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
// 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 [HomeFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class HomeFragment : Fragment() {
// 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)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
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 HomeFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
HomeFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
/* interfacer_han: ๋ฐ๋ก ์์ ์๋ ์ฝ๋์ ๋์์ ์๋ ์๋ ์ฝ๋์ ๊ฐ๋ค.
@JvmStatic
fun newInstance(param1: String, param2: String): HomeFragment {
val fragment = HomeFragment()
val bundle = Bundle()
bundle.putString(ARG_PARAM1, param1)
bundle.putString(ARG_PARAM2, param2)
fragment.arguments = bundle
return fragment
}
*/
}
}
์ฝ๋ ์๋ซ ๋ถ๋ถ์ ์๋ ๋ด ์ฃผ์์ ์ ์ธํ๋ฉด Navigation ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ํด ์๋ ์์ฑ๋ ๋ ๊ฒ ๊ทธ๋๋ก์ Fragment ํด๋์ค ์ฝ๋๋ค. ์ด ์ฝ๋๋ฅผ ์๋์ ๊ฐ์ด ์์ ํ๋ค.
...
class HomeFragment : Fragment() {
...
private lateinit var binding: FragmentHomeBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_home, container, false)
return binding.root
}
...
}
#2-4 Destination ํ๋๊ทธ๋จผํธ ํ๋ ๋ ์ถ๊ฐ (SecondFragment)
#2-1 ~ #2-3์ ๊ฐ์ ๋ฐฉ์์ผ๋ก SecondFragment๋ผ๋ ์ด๋ฆ์ Destination ํ๋๊ทธ๋จผํธ๋ฅผ ์ถ๊ฐํ๋ค. fragment_second.xml์๋ <TextView> ์์ ฏ ๋ฑ ํ๋๋ง ๋ฃ์ด๋๋ค.
#3 Navigation ์ฌ์ฉํ๊ธฐ - ํ๋๊ทธ๋จผํธ ์ ํ
#3-1 Action ๋ง๋ค๊ธฐ
nav_graph.xml์ Design ๋ชจ๋๋ก ์ฐ๋ค. HomeFragment๋ฅผ ํด๋ฆญํ๋ฉด ์ค๋ฅธ์ชฝ์ ํ๋์ ์์ด ์๊ธด๋ค.
๊ทธ ํ๋์ ์์ ๋๋๊ทธํด์ SecondFragment์ ์ฐ๊ฒฐํ๋ค. ์ด๋ฌ๋ฉด ๋ ํ๋๊ทธ๋จผํธ ๊ฐ์ Action์ด ์์ฑ๋๋ค. Action์ด๋, ํ๋๊ทธ๋จผํธ ์ ํ์ ํ๋ฆ์ด๋ค.
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.example.navigationbasics.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_secondFragment"
app:destination="@id/secondFragment" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.navigationbasics.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
</navigation>
Code ๋ชจ๋๋ก ๋ณด๋ฉด ์์ ๊ฐ๋ค. Action์ ์๋ ์์ฑ๋ id๋ฅผ ๋์ฌ๊ฒจ๋ด๋์.
#3-2 Action ์ฌ์ฉํ๊ธฐ (HomeFragment.kt ์์ )
...
import androidx.navigation.findNavController
...
class HomeFragment : Fragment() {
...
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_home, container, false)
binding.button.setOnClickListener {
it.findNavController().navigate(R.id.action_homeFragment_to_secondFragment)
}
return binding.root
}
...
}
HomeFragment์ button์ ๋๋ฅด๋ฉด ๋ฐฉ๊ธ ๋ง๋ Action์ด ์คํ๋๊ฒ ํ๋ ์ฝ๋๋ค.
#3-3 ์คํ ํ์ธ
ํ๋๊ทธ๋จผํธ๊ฐ ์ ์ ํ๋๋ค.
์ด ๋ค์์ผ๋ก HomeFragment์์ SecondFragment๋ก ์ ํํ ๋ ๋ฐ์ดํฐ๋ ๊ฐ์ด ์ ๋ฌํ๋ ์ฝ๋๋ฅผ ์์ฑํด๋ณธ๋ค.
#4 ํ๋๊ทธ๋จผํธ ๊ฐ ๋ฐ์ดํฐ ์ ๋ฌ
#4-1 HomeFragment.kt ์์
...
class HomeFragment : Fragment() {
...
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
binding.button.setOnClickListener {
val userName = if(!TextUtils.isEmpty(binding.editText.text.toString())) {
binding.editText.text.toString()
} else {
"์ด๋ฆ ์์"
}
val bundle: Bundle = bundleOf("user_name" to userName)
it.findNavController().navigate(R.id.action_homeFragment_to_secondFragment, bundle)
}
...
}
...
}
navigate() ๋ฉ์๋์ ์ค๋ฒ๋ก๋ฉ ๊ด๊ณ์ด๋ฉด์ Bundle์ ์ธ์๋ก ๋ฐ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค. ์ฌ์ค ์ด๋ ๊ฒ ์ง์ ์ ์ผ๋ก ๋ชฉ์ ์ง(Destination) ํ๋๊ทธ๋จผํธ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ ๊ถ์ฅ๋์ง ์๋ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ํ๋ค. ์ ์์ ์ธ ๋ฐฉ๋ฒ์ ViewModel์ ํตํ ๋ฐ์ดํฐ ์ ๋ฌ์ด๋ค.
#4-2 SecondFragment.kt ์์
...
class SecondFragment : Fragment() {
...
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_second, container, false)
var userName: String? = requireArguments().getString("user_name")
binding.textView.text = userName.toString()
return binding.root
}
...
}
Fragment.requireArguments()๋ฅผ ํตํด ์ ๋ฌ๋ Bundle์ ๋ฐ๋๋ค. ๊ทธ๋ฆฌ๊ณ fragment_second.xml์ <TextView>์ ํ์ํ๋ค.
#4-3 ์คํ ํ์ธ
#5 ์์ฝ
Navigation library๋ ํ๋๊ทธ๋จผํธ๋ฅผ ์ง๋์ฒ๋ผ ๋ณผ ์ ์๊ฒ ๋ง๋ค์ด์ค๋ค.
#6 ์์ฑ๋ ์ฑ
android-practice/navigation/NavigationBasics at master · Kanmanemone/android-practice
Contribute to Kanmanemone/android-practice development by creating an account on GitHub.
github.com
'๊นจ์ ๊ฐ๋ ๐ > Android' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] RecyclerView - ๊ธฐ์ด (0) | 2024.02.02 |
---|---|
[Android] Navigation - ์ ๋๋ฉ์ด์ (1) | 2024.01.27 |
[Android] Navigation - ํ๊ฒฝ ์ค์ (0) | 2024.01.25 |
[Android] Fragment์ ์๋ช ์ฃผ๊ธฐ (0) | 2024.01.24 |
[Android] Activity์ ์๋ช ์ฃผ๊ธฐ (0) | 2024.01.23 |