본문 바로가기
Android/Jetpack

[Android/Kotlin] Navigation Component 구현

by juwon2 2024. 5. 28.

 

Navigation Component란???

- 안드로이드 Jetpack의 일부로, 앱 내에서 화면전환(네비게이션)을 관리하는데 도움을 주는 라이브러리!!

- 프래그먼트나 엑티비티 간의 이동, 인자(데이터)전달, 애니메이션, 앱바와의 통합을 간편하게 처리할 수 있도록 한다.

 

 

 

💻 구현하기

이제 직접 구현해보면서 알아보자.

 

먼저  build.gradle에 아래 코드들을 추가해준다

plugins {
    id 'kotlin-kapt'
}


dependencies {
    implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
    implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
}

build.gradle(Module)

 

 

그리고 Activity와 Fragment들을 생성해준다

 

1. Navigation Graph 생성

 

res 폴더 아래에 navigation 디렉터리를 만들고, nav_graph.xml 파일을 생성한다

 

 

Naviagtion Graph (XML파일)

- 앱의 모든 화면과 화면간의 이동경로(트랜지션)을 정의하는 XML파일

- 각 화면은 Fragment, Activity 또는 특정한 목적지를 나타냄

<?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/mapFragment">

    <fragment
        android:id="@+id/mapFragment"
        android:name="nbc.group.recipes.presentation.fragment.MapFragment"
        android:label="fragment_map"
        tools:layout="@layout/fragment_map" />
    <fragment
        android:id="@+id/bookmarkFragment"
        android:name="nbc.group.recipes.presentation.fragment.BookmarkFragment"
        android:label="fragment_bookmark"
        tools:layout="@layout/fragment_bookmark" />
</navigation>

 nav_graph.xml

 

navigation

  • app:startDestination="@+id/mapFragment" 속성을 추가한다
    앱 실행 시 가장 처음 뜨는 프래그먼트를 지정하는 속성이다

fragment

  • 프래그먼트들을 모두 정의해둔다
  • name: 프래그먼트 파일의 경로를 넣어준다
  • layout: 프래그먼트의 xml파일을 연결해준다

action으로는 화면 이동을 정의할 수 있음 (화면간의 연결)

 

 

굳이 코드를 작성해줄 필요없고 +폰아이콘을 누르면 선택할 수 있는 프래그먼트가 나와서 클릭하면 자동으로 코드가 짜여진다.

 

2. Menu 생성

 

res 폴더 아래에 menu 디렉터리를 만들고, nav_menu.xml 파일을 생성한다

 

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/mapFragment"
        android:icon="@drawable/ic_home"
        android:title="home"/>

    <item
        android:id="@+id/bookmarkFragment"
        android:icon="@drawable/ic_bookmark"
        android:title="bookmark"/>

</menu>

nav_menu.xml

 

주의할점!! -> id를 내비게이션 그래프의 프래그먼트 id와 동일하게 작성해야 맵핑을 통해 바텀내비게이션바로 만들어줄 수 있다.

  • icon: 바텀내비게이션에 표시될 아이콘 이미지를 연결한다.
  • title: 바텀내비게이션에 표시될 레이블 텍스트를 작성한다.

 

 

3. 클릭 시 색상지정

아이템을 클릭한 상태와 클릭하지 않은 상태에 색을 다르게 주기 위한 파일을 drawble에 생성해준다

android:state_checked="true"가 클릭한 상태, false가 클릭하지 않은 상태다

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 클릭했을 경우 -->
    <item android:state_checked="true" android:color="@color/green1" />
    <!-- 클릭하지 않은 경우 -->
    <item android:state_checked="false" android:color="@color/black" />
</selector>

bottom_navigation_color.xml

 

 

# activtiy_main 레이아웃

<?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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".presentation.MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fcv"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph"
        app:layout_constraintBottom_toTopOf="@id/bottomNav_bar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>


    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNav_bar"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        app:itemIconTint="@drawable/bottom_navigation_color"
        app:itemTextColor="@drawable/bottom_navigation_color"
        app:labelVisibilityMode="labeled"
        app:menu="@menu/menu"
        app:itemActiveIndicatorStyle="@android:color/transparent"
        app:itemBackground="@android:color/transparent"
        android:background="@color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/fcv"/>



</androidx.constraintlayout.widget.ConstraintLayout>

activtiy_main.xml

  • android:name="androidx.navigation.fragment.NavHostFragment"
    - 탐색 그래프에서 대상을 표시하는 빈 컨테이너인 NavHost 역할을 하도록 설정한다
    - NavHostFragment - 네비게이션 그래프를 관리하는 컨테이너로, 화면 전환의 중심역할!
  • app:navGraph="@navigation/nav_graph"
    NavHostFragment와 내비게이션 그래프를 연결시킨다
  • app:defaultNavHost="true"
    뒤로가기 버튼을 누르면 NavHostFragment로 돌아오게 설정한것
    기본값은 false이고, false로 설정할경우 뒤로가기 버튼을 누르면 앱이 종료된다

 

- BottomNavigationView

  • app:labelVisibilityMode="labeled"
    아이콘 밑의 레이블을 보이게 설정한다. 숨기고 싶으면 unlabeled로 설정한다.

 

  •   app:itemIconTint="@drawable/bottom_navigation_color"
      app:itemTextColor="@drawable/bottom_navigation_color".                                                                                      아이콘과 레이블의 색상을 지정해주는 속성인데, 아이템을 클릭한 상태와 클릭하지 않은 상태에 색을 다르게 주기 위해 위에서 만들었던 selector를 연결해준다.
  • app:itemBackground="@android:color/transparent"
    아이템을 클릭할 때 검은 원이 퍼져나가는 ripple효과를 제거하기 위한 속성이다.

 

 

# MainActivtiy 

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var navController: NavController

	...
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        initNavigation()
    }

    override fun onSupportNavigateUp(): Boolean {
        navController = findNavController(R.id.fcv)
        return navController.navigateUp() || super.onSupportNavigateUp()
    }

    private fun initNavigation() {
        val navHostFragment = supportFragmentManager.findFragmentById(R.id.fcv) as NavHostFragment
        val navController = navHostFragment.findNavController()
        binding.bottomNavBar.setupWithNavController(navController)
    }
}

MainActivtiy.kt

 

NavController

- 화면 전환을 제어하는 객체!

- 위에 작성한 레이아웃(NavHostFragment)와 연결되어, 화면 이동 및 상태를 관리!!!

 

 

이렇게하면 화면전환이 잘 구현된다.

 

 

 

4. Safe Args 사용해서 데이터 전달 

참고로 Safe Args 사용해서 데이터 전달할수도 있다 

(원래는 bundle로 데이터를 많이 전달했는데!!!! 이 방법이 더 유용하고 간단한 방법인듯하다)

 

- 이동하면서 action데이터를 바로 전달한다

val action = FirstFragmentDirections.actionFirstFragmentToSecondFragment("Sample Data")
findNavController().navigate(action)  //이동하면서 action 데이터를 바로 전달함

 

데이터 수신하기

val args = SecondFragmentArgs.fromBundle(requireArguments())
val receivedData = args.someArgument

 

 

Safe Args 적용했을때 장점

Safe Args를 사용하면???

→ postId의 타입이 String임을 보장: 컴파일 타임에 확인되므로, 런타임 오류가 발생할 가능성이 줄어듬 (타입오류 방지가능)

Bundle 객체의 명시적 관리 필요 없음: 코드가 간결 + 데이터 전달 흐름을 더 명확히 볼 수 있

 

 

 

그럼 Navigation Component를 적용하면 장점은??

간편한 네비게이션 관리

  • 코드로 화면 전환을 직접할 필요 없이, 그래프를 통해 한눈에 구조파악 가능!
  • Safe Args를 활용해, 데이터 전달시 타입오류 방지가능!

BackStack 자동 관리

  • 네비게이션 컴포넌트가 BackStack(뒤로가기 스택)을 자동으로 관리해줌!! (스택이 쌓이지 않게 자동관리)

모듈화

  • 각 화면이 독립적으로 설계되어, 유지보수 용이

UI 컴포넌트와 통합 용이

  • ToolBar, BottomNavigationView, DrawerLayout 등과 쉽게 통합할 수 있음

 

 

📚참고자료

참고자료(1)

참고자료(2)

참고자료(3)