네비게이션 컴포넌트
- 네비게이션 컴포넌트는 앱의 화면 전환을 쉽게 구현하고 화면 흐름을 시각화해서 보여주는 라이브러리다.
- 개발자가 정의한 UI Graph를 기반으로 하면 간의 관계를 한 눈에 볼 수 있다.
Back Stack
- 안드로이드에서는 액티비티를 백 스택을 통해 관리 - 백 스택은 네비게이션의 상태를 나타낸다.
- 액티비티의 백 스택은 시작점을 가지며, open된 순서대로 스택을 쌓이고, 현재 화면은 스택의 최상단에 위치한다.
- 새로 액티비티가 생성이 되면 백 스택에 Push가 되면서 기존에 있던 액티비티는 아래로 감춰지게 되고, 뒤로가기를 눌러서 현재 액티비티를 종료시키면, 종료된 액티비티가 pop되면서 그 아래에 있던 액티비티가 push되어 포그라운드되는 구조다.
네비게이션
- 네비게이션은 사용자가 앱 내의 다양한 콘텐츠를 탐색하고, 이동할 수 있도록 하는 상호작용을 의미한다.
- 네비게이션 컴포넌트는 단순한 버튼 클릭부터 AppBar, Navigation Drawer와 같은 복잡한 패턴에 이르기까지 여러 가지 Navigation을 구현할 수 있는 안드로이드 jetpack 라이브러리다.
네비게이션의 기본 원칙
- 앱은 반드시 고정된 시작점을 가져야 한다.
고정된 시작점은 앱의 첫번째 화면으로, 사용자가 back 버튼을 눌러서 도착하는 마지막 화면이다. - 스택은 네비게이션의 상태를 나타낸다.
스택은 후입선출 구조로, 오픈된 순서대로 스택에 쌓이고, 현재 화면은 스택의 최상단에 위치한다. - Up 버튼(Actionbar의 뒤로가기 버튼)으로는 앱을 종료할 수 없다.
앱의 시작점 또는 Top-Level-destination에서는 Up 버튼이 보이지 않으며, 시스템 back 버튼으로 종료해야 한다.
네비게이션 구성 요소
1. Navigation Graph
화면 이동에 대한 모든 정보를 정의하는 리소스 파일이다.
res 폴더 내부에 navigation 폴더에 navigation editor로 작성한 xml 파일이다.
2.NavHost
Navigation Host는 Navigation Graph에 정의된 화면들을 보여주는 컨테이너로서,
Navigation graph에 정의한 내용대로 fragments들을 호스팅한다.
3.NavController
NavController는 화면 이동에 대한 컨트롤러 역할을 한다.
NavController는 NavHost에서 얻을 수 있다. NavHostFragment는 개별적으로 NavController를 가지고 있다.
Fragment, Activity, View에서 NavController을 가져오려면 다음 메서드 중 하나를 사용한다.
- Fragment.findNavController()
- View.findNavController()
- Activity.findNavController(viewId:Int)
네비게이션 기능 구현 단계
- 네비게이션 라이브러리 의존성 추가
- 프로젝트에 네비게이션 추가
- 네비게이션 그래프에 목적지 생성
- 시작 목적지 설정
- 목적지 연결
- 목적지 UI 레이아웃 작성
- Navigation Host 정의
- UI 위젯에 목적지를 지정
- ActionBar UI 컴포넌트를 연결
예제코드
먼저 build.gradle에 아래와 같은 의존성을 추가한다.
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.2'
그리고 프로젝트에 nav_graph.xml파일을 추가한다.
이후 nav_graph.xml 파일에서 Create new destination을 진행한다.
총 4개의 Fragment를 만들었다.
그럼 이제 아래와 같은 nav_graph.xml 파일을 작성한다.
<?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/AFragment">
<fragment
android:id="@+id/AFragment"
android:name="com.example.finalexam.fragment.nav.navexam01.ui.AFragment"
android:label="fragment_a"
tools:layout="@layout/fragment_a" >
<action
android:id="@+id/action_AFragment_to_BFragment"
app:destination="@id/BFragment" />
<action
android:id="@+id/action_AFragment_to_CFragment"
app:destination="@id/CFragment" />
</fragment>
<fragment
android:id="@+id/BFragment"
android:name="com.example.finalexam.fragment.nav.navexam01.ui.BFragment"
android:label="fragment_b"
tools:layout="@layout/fragment_b" >
<action
android:id="@+id/action_BFragment_to_DFragment"
app:destination="@id/DFragment" />
<action
android:id="@+id/action_BFragment_to_CFragment"
app:destination="@id/CFragment" />
</fragment>
<fragment
android:id="@+id/CFragment"
android:name="com.example.finalexam.fragment.nav.navexam01.ui.CFragment"
android:label="fragment_c"
tools:layout="@layout/fragment_c" >
<action
android:id="@+id/action_CFragment_to_DFragment"
app:destination="@id/DFragment" />
</fragment>
<fragment
android:id="@+id/DFragment"
android:name="com.example.finalexam.fragment.nav.navexam01.ui.DFragment"
android:label="fragment_d"
tools:layout="@layout/fragment_d" >
<action
android:id="@+id/action_DFragment_to_AFragment"
app:destination="@id/AFragment" />
</fragment>
</navigation>
그리고 아래와 같은 레이아웃들을 만든다. 레이아웃 xml 코드가 너무 많으므로 이는 생략하겠다.
다음으로 first_nav_exam_main_activity.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"
android:padding="8dp"
tools:context=".fragment.nav.navexam01.FirstNavExamMainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
이제 코틀린 코드를 작성하면 된다.
다음과 같이 FirstNavExamMainActivity 클래스를 작성한다.
class FirstNavExamMainActivity : AppCompatActivity() {
private val binding by lazy{
FirstNavExamMainActivityBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainerView) as NavHostFragment
var navController = navHostFragment.navController
}
}
이제 프래그먼트를 작성해야하는데 A프래그먼트 코드만 확인해보자.
class AFragment : Fragment() {
private var _binding : FragmentABinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentABinding.inflate(inflater, container, false)
val view = binding.root
binding.btnAToB.setOnClickListener{
findNavController().navigate(R.id.action_AFragment_to_BFragment)
}
binding.btnAToC.setOnClickListener{
findNavController().navigate(R.id.action_AFragment_to_CFragment)
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
이런 방식으로 각 프래그먼트 버튼에 NavController을 등록하면 된다.
그러면 아래와 같이 동작한다.
이상으로 포스팅을 마칩니다. 감사합니다.
댓글