Android Jetpack의 Navigation은 UI 전환을 쉽게 구현하는데 도와주는 라이브러리입니다. 개발자가 정의한 UI Graph를 기반으로 화면을 쉽게 전환하고, 유지보수가 매우 쉬워집니다.
이 글에서는 네비게이션(Navigation)을 사용한 간단한 앱을 만들어 봄으로 써, Navigation의 기본적인 사용 방법을 설명합니다.
프로젝트 생성
Kotlin
과 Use AndroidX artifacts
를 선택해주세요.
(Android Studio 버전이 3.4 미만이라면 이 기능이 없습니다. 마이그레이션 기능을 이용하거나 직접 의존성을 변경해줘야 합니다)
AndroidX로 프로젝트를 생성하지 않았다면, 메뉴에서 [Refactor] -> [Migrate to AndroidX...]
를 누르시면 AndroidX를 사용하는 프로젝트로 마이그레이션이 됩니다.
네비게이션을 사용하려면 앱 gradle에서 dependency를 추가해야 합니다.
dependencies {
....
implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha07'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha07'
}
Fragment 생성
앞으로 3개의 화면을 순환하는 앱을 만들어볼 것인데요.
3개의 화면을 각각 London
, Paris
, Italy
라고 할께요.
3개의 화면은 Fragment로 구현할 것이고 각각의 파일에 class를 만들겠습니다.
아래 코드를 참고하여 클래스와 레이아웃 파일을 모두 구현해줍니다.
LondonScreen.kt
class ItalyScreen : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_italy_screen, container, false)
return view
}
}
ParisScreen.kt
class ParisScreen : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_paris_screen, container, false)
return view
}
}
ItalyScreen.kt
class ItalyScreen : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_italy_screen, container, false)
return view
}
}
/res/layout/fragment_italy_screen.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=".ItalyScreen">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Italy"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/to_london_from_italy"/>
<Button
android:id="@+id/to_london_from_italy"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Go to London"
android:textColor="@color/colorAccent"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="@+id/to_paris_from_italy"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Go to Paris"
android:textColor="@color/colorAccent"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@+id/to_london_from_italy"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
/res/layout/fragment_london_screen.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=".LondonScreen">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="London"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/to_paris_from_london"/>
<Button
android:id="@+id/to_paris_from_london"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Go to Paris"
android:textColor="@color/colorAccent"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="@+id/to_italy_from_london"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Go to Italy"
android:textColor="@color/colorAccent"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@+id/to_paris_from_london"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
/res/layout/fragment_paris_screen.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=".ParisScreen">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Paris"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/to_italy_from_paris"/>
<Button
android:id="@+id/to_italy_from_paris"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Go to Italy"
android:textColor="@color/colorAccent"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="@+id/to_london_from_paris"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Go to London"
android:textColor="@color/colorAccent"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@+id/to_italy_from_paris"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Navigation Host 정의
Navigation을 구현할 때 Host를 정의해야 합니다. Host는 화면이 보이는 객체입니다. 여기서는 MainActivity를 Host로 설정할 것입니다.
MainActivity.kt
와 activity_main.xml
를 구현해주세요.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
/res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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:background="@android:color/background_light"
tools:context=".MainActivity">
<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"/>
</FrameLayout>
activity_main.xml
는 FrameLayout
안에 frament
view가 존재하도록 구현되어있습니다.
defaultNavHost
와 navGraph
는 fragment
를 Host로 설정하는 속성입니다.
코틀린 파일에서 Host를 설정하지 않고 레이아웃 파일에서만 그래프와 호스트를 설정할 수 있습니다.
- defaultNavHost를 true로 설정하면 Back key를 눌렀을 때 이전 화면으로 전환됩니다. 만약 false로 설정하고 Back key를 누르면 앱을 종료합니다.
- navGraph는 graph를 정의한 파일을 의미합니다.
Graph 정의
예를들어 A라는 화면에서 B화면으로 이동할 수 있다면, 그 관계를 그래프에 정의해야 합니다.
/res/navigation/
폴더를 생성하고 그 폴더에 nav_graph.xml
을 생성해주세요.
이 파일이 View의 navigation 관계가 정의된 그래프입니다.
/res/navigation/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"
app:startDestination="@+id/london_screen">
<fragment
android:id="@+id/london_screen"
android:name="com.codechacha.navigation.LondonScreen"
android:label="London screen"
tools:layout="@layout/fragment_london_screen">
</fragment>
<fragment
android:id="@+id/paris_screen"
android:name="com.codechacha.navigation.ParisScreen"
android:label="Paris screen"
tools:layout="@layout/fragment_paris_screen">
</fragment>
<fragment
android:id="@+id/italy_screen"
android:name="com.codechacha.navigation.ItalyScreen"
android:label="Italy screen"
tools:layout="@layout/fragment_italy_screen">
</fragment>
</navigation>
코드를 보면 navigation
태그 아래에 fragment
태그가 3개 있습니다.
fragment
는 우리가 순환할 화면입니다.
name
을 보면 위에서 생성한 LondonScreen
, ParisScreen
, ItalyScreen
이 설정되어있습니다.
navigation
태그에 startDestination
속성이 있는데 처음 보여질 화면이 어떤 것인지를 의미합니다.
여기서는 london_screen
으로 설정되어있기 때문에 처음 보이는 화면이 LondonScreen
이 됩니다.
이제 실행해보면 LondonScreen이 보입니다.
시작 화면은 잘 보이지만 GO TO PARIS
버튼을 눌러도 화면이 이동하지 않습니다.
그래프에서 화면 이동 관계를 설정하지 않았기 때문입니다.
nav_graph.xml
에서 왼쪽 하단의 Design
버튼을 누르면 UI로 쉽게 이동 관계를 설정할 수 있습니다.
다시 Text
버튼을 눌러 코드를 확인해보면... fragment
태그 아래에 action
코드들이 자동 삽입된 것을 볼 수 있습니다.
action에는 id와 이동하려는 화면을 의미하는 desination이 정의되어 있습니다.
/res/navigation/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"
app:startDestination="@+id/london_screen">
<fragment
android:id="@+id/london_screen"
android:name="com.codechacha.navigation.LondonScreen"
android:label="London screen"
tools:layout="@layout/fragment_london_screen">
<action android:id="@+id/action_london_screen_to_paris_screen"
app:destination="@id/paris_screen"/>
<action android:id="@+id/action_london_screen_to_italy_screen"
app:destination="@id/italy_screen"/>
</fragment>
<fragment
android:id="@+id/paris_screen"
android:name="com.codechacha.navigation.ParisScreen"
android:label="Paris screen"
tools:layout="@layout/fragment_paris_screen">
<action android:id="@+id/action_paris_screen_to_italy_screen"
app:destination="@id/italy_screen"/>
<action android:id="@+id/action_paris_screen_to_london_screen"
app:destination="@id/london_screen"/>
</fragment>
<fragment
android:id="@+id/italy_screen"
android:name="com.codechacha.navigation.ItalyScreen"
android:label="Italy screen"
tools:layout="@layout/fragment_italy_screen">
<action android:id="@+id/action_italy_screen_to_paris_screen"
app:destination="@id/paris_screen"/>
<action android:id="@+id/action_italy_screen_to_london_screen"
app:destination="@id/london_screen"/>
</fragment>
</navigation>
이제 fragment에서 버튼을 눌렀을 때 이동하는 코드를 작성하면 됩니다. 버튼을 눌렀을 때 화면을 변경하고 싶으면 Navigation Controller 객체를 이용해야 합니다. Controller는 Host에 보여지고 있는 View를 변경해주는 역할을 맡고 있습니다.
Fragment 파일들을 열고 버튼 리스너를 정의해주세요.
Navigation.findNavController
는 Controller를 가져오는 코드이고, navigate
는 정의된 action의 destination으로 이동하라는 의미입니다.
LondonScreen.kt
class LondonScreen : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_london_screen, container, false)
view.findViewById<Button>(R.id.to_paris_from_london).setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_london_screen_to_paris_screen)
}
view.findViewById<Button>(R.id.to_italy_from_london).setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_london_screen_to_italy_screen)
}
return view
}
}
ItalyScreen.kt
class ItalyScreen : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_italy_screen, container, false)
view.findViewById<Button>(R.id.to_london_from_italy).setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_italy_screen_to_london_screen)
}
view.findViewById<Button>(R.id.to_paris_from_italy).setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_italy_screen_to_paris_screen)
}
return view
}
}
ParisScreen.kt
class ParisScreen : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_paris_screen, container, false)
view.findViewById<Button>(R.id.to_italy_from_paris).setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_paris_screen_to_italy_screen)
}
view.findViewById<Button>(R.id.to_london_from_paris).setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_paris_screen_to_london_screen)
}
return view
}
}
이제 실행해보세요. 버튼을 누르면 우리가 의도한 대로 화면이 이동합니다. 그리고 Back key를 누르면 이전 화면으로 이동됩니다. 처음 화면에서 Back key를 누르면 앱이 종료됩니다.
Animation
지금까지 구현한 앱은 화면이 변경될 때 애니메이션 없이 바로 전환이 되는데요.
action마다 애니메이션을 정의할 수 있습니다. 애니메이션 정의는 navigation이 정의된 그래프 파일에서 할 수 있지만, 그 전에 애니메이션들을 구현해야 합니다.
/res/anim/
폴더를 만들고 4개의 애니메이션 파일을 구현해주세요.
/res/anim/slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="700"/>
</set>
/res/anim/slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="700"/>
</set>
/res/anim/slide_out_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0%" android:toXDelta="-100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="700"/>
</set>
/res/anim/slide_out_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0%" android:toXDelta="100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="700"/>
</set>
애니메이션을 파일로 정의하였으면 그래프 파일에 설정하면 됩니다.
nav_graph.xml
에 각각의 Action에 애니메이션을 설정합니다.
/res/navigation/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"
app:startDestination="@+id/london_screen">
<fragment
android:id="@+id/london_screen"
android:name="com.codechacha.navigation.LondonScreen"
android:label="London screen"
tools:layout="@layout/fragment_london_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left">
<action android:id="@+id/action_london_screen_to_paris_screen"
app:destination="@id/paris_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
<action android:id="@+id/action_london_screen_to_italy_screen"
app:destination="@id/italy_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
</fragment>
<fragment
android:id="@+id/paris_screen"
android:name="com.codechacha.navigation.ParisScreen"
android:label="Paris screen"
tools:layout="@layout/fragment_paris_screen">
<action android:id="@+id/action_paris_screen_to_italy_screen"
app:destination="@id/italy_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
<action android:id="@+id/action_paris_screen_to_london_screen"
app:destination="@id/london_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
</fragment>
<fragment
android:id="@+id/italy_screen"
android:name="com.codechacha.navigation.ItalyScreen"
android:label="Italy screen"
tools:layout="@layout/fragment_italy_screen">
<action android:id="@+id/action_italy_screen_to_paris_screen"
app:destination="@id/paris_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
<action android:id="@+id/action_italy_screen_to_london_screen"
app:destination="@id/london_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
</fragment>
</navigation>
- enterAnim : action을 수행할 때 이동할 화면(destination)에 대한 애니메이션입니다.
- exitAnim : action을 수행할 때 현재 화면에 대한 애니메이션입니다.
- popExitAnim : 지난 화면으로 돌아갈 때(pop) 종료되는 화면에 대한 애니메이션입니다.
- popEnterAnim : 지난 화면으로 돌아갈 때 이동할 화면에 대한 애니메이션입니다.
앱을 실행하고 버튼을 누르면 애니메이션이 적용된 것을 확인할 수 있습니다.
Global Action
사실 nav_graph.xml
에는 중복된 Action들이 3개나 있습니다.
컨트롤 CV로 일단 구현을 해놨는데요, 구현이 끝났으니 코드를 깔끔하게 정리해보려합니다.
Global action은 말 그대로 Global로 action을 정의해서 여러 객체에서 공통으로 사용하자는 것입니다.
Global action을 생성하는 방법은 코드로 할 수 있고, 그래프 에디터로도 할 수 있습니다.
nav_graph.xml
에서 왼쪽 하단에 Design버튼을 누르면 그래프 에디터가 실행됩니다.
View를 클릭한 상태에서 Right Click -> Add Action -> Global
을 누르면 Global action이 생성됩니다.
3개의 fragment의 Global action을 모두 생성하고 xml을 열어 보면 아래처럼 navigation 태그 하위에 Global action 코드가 생성됩니다.
<action android:id="@+id/action_global_london_screen"
app:destination="@id/london_screen"/>
<action android:id="@+id/action_global_paris_screen"
app:destination="@id/paris_screen"/>
<action android:id="@+id/action_global_italy_screen"
app:destination="@id/italy_screen"/>
기존에 생성한 Fragment 내의 Action을 모두 삭제하고, Global action에 애니메이션을 추가하면 다음과 같은 코드가 됩니다.
/res/navigation/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"
app:startDestination="@+id/london_screen">
<fragment
android:id="@+id/london_screen"
android:name="com.codechacha.navigation.LondonScreen"
android:label="London screen"
tools:layout="@layout/fragment_london_screen">
</fragment>
<fragment
android:id="@+id/paris_screen"
android:name="com.codechacha.navigation.ParisScreen"
android:label="Paris screen"
tools:layout="@layout/fragment_paris_screen">
</fragment>
<fragment
android:id="@+id/italy_screen"
android:name="com.codechacha.navigation.ItalyScreen"
android:label="Italy screen"
tools:layout="@layout/fragment_italy_screen">
</fragment>
<action android:id="@+id/action_global_london_screen"
app:destination="@id/london_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
<action android:id="@+id/action_global_paris_screen"
app:destination="@id/paris_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
<action android:id="@+id/action_global_italy_screen"
app:destination="@id/italy_screen"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"/>
</navigation>
Local action을 모두 삭제하고, Global action을 생성했기 때문에 Fragment 코드도 변경해줘야 합니다. 아래 코드처럼 Fragment에서 사용한 Local action을 모두 Global action으로 변경해주세요.
view.findViewById<Button>(R.id.to_london_from_italy).setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_global_london_screen)
}
view.findViewById<Button>(R.id.to_paris_from_italy).setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_global_paris_screen)
}
이제 앱을 실행해보면 이전과 동일하게 동작하는 화면을 볼 수 있습니다.
AppBarConfiguration
현재 보여지고 있는 화면의 Label을 AppBar에 보여주고 싶을 수 있습니다.
AppBarConfiguration을 이용하면 네비게이션 그래프에 정의된 Label을 AppBar에 출력할 수 있습니다.
더불어 AppBar에 뒤로가기(<-) 버튼까지 보여줍니다.
뒤로가기 버튼은 startDestination
로 정의된 처음 화면이 아닐 때만 보여줍니다.
NavController
와 AppBarConfiguration
를 멤버변수로 생성해야 합니다.
멤버변수로 선언하는 이유는 다른 곳에서 사용하기 때문입니다.
NavController는 호스트 리소스를 인자로 넘겨주어 생성합니다.
AppBarConfiguration는 Controller의 graph 변수를 인자로 넘겨주어 생성합니다.
navController = Navigation.findNavController(this, R.id.my_nav_host_fragment)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController
는 두 객체를 AppBar에 적용합니다.
setupActionBarWithNavController(navController, appBarConfiguration)
AppBar에 생성되는 뒤로가기 버튼을 눌렀을 때, 뒤로 이동하려면 onSupportNavigateUp
를 오버라이드해야 합니다.
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
전체적인 코드는 이렇게 됩니다.
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navController = Navigation.findNavController(this, R.id.my_nav_host_fragment)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
실행해보면 현재 화면의 Label과 뒤로가기 버튼이 생성됩니다. 첫 화면으로 지정된 화면 일 때는 뒤로가기 버튼이 보이지 않습니다. 지금 예제는 무한으로 순환하기 때문에 조금 어색할 수 있겠네요. 실제로 순환하는 그래프를 만들어서는 안된다고 기억하시면 될 것 같네요.
다음 글 Android Jetpack Navigation을 이용하여 Drawer 구현에서는 Navigation을 이용하여 Drawer를 구현하는 방법에 대해서 알아보겠습니다.
참고
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력