관리 메뉴

나만을 위한 블로그

[Android] Navigation Component란? Navigation 사용법 본문

Android

[Android] Navigation Component란? Navigation 사용법

참깨빵위에참깨빵 2022. 6. 19. 01:49
728x90
반응형

Navigation의 뜻은 아래와 같다.

 

항해, 길을 찾다, (힘들거나 복잡한 상황을) 다루다

 

그럼 Navigation Component는 시작지에서 어떤 목적지로 이동하는 걸 도와주는 컴포넌트라고 생각된다.

안드로이드 디벨로퍼에서 설명하는 네비게이션 컴포넌트는 아래와 같다.

 

https://developer.android.com/guide/navigation?hl=ko 

 

탐색  |  Android 개발자  |  Android Developers

Android Jetpack의 탐색 구성요소를 사용하여 앱에서 탐색 구현

developer.android.com

네비게이션은 사용자가 앱 내의 여러 컨텐츠를 탐색하고 그곳에 들어갔다 나올 수 있게 하는 상호작용을 의미한다. 안드로이드 제트팩의 네비게이션 컴포넌트는 단순한 버튼 클릭에서 좀 더 복잡한 패턴(앱 바, 네비게이션 윈도우)에 이르기까지 여러 탐색을 구현하게 도와준다. 네비게이션 컴포넌트는 기존 원칙(네비게이션 원리)을 준수해서 일관적이고 예측 가능한 사용자 환경을 보장한다
네비게이션 컴포넌트는 3가지 주요 부분으로 구성된다.

- 네비게이션 그래프 : 모든 네비게이션 관련 정보가 하나의 중심 위치에 모여 있는 XML 리소스. 여기엔 대상이라고 부르는 앱 내의 모든 개별 컨텐츠 영역과 앱에서 갈 수 있는 모든 이용 가능한 경로가 포함된다
- NavHost : 네비게이션 그래프에서 대상을 표시하는 빈 컨테이너. 대상 컴포넌트에는 프래그먼트 대상을 표시하는 기본 NavHost 구현인 NavHostFragment가 포함된다
- NavController : NavHost에서 앱 탐색을 관리하는 객체. 사용자가 앱 안에서 이동할 때 NavHost에서 대상 컨텐츠의 전환을 오케스트레이션한다

앱을 탐색하는 동안 네비게이션 그래프에서 특정 경로를 따라 이동할지, 특정 대상으로 직접 이동할지 NavController에게 전달한다. 그러면 NavController가 NavHost에 적절한 대상을 표시한다
네비게이션 컴포넌트는 아래를 포함한 여러 장점이 있다

- 프래그먼트 트랜잭션 처리
- '위로', '뒤로' 작업의 올바른 처리
- 애니메이션, 전환에 표준화된 리소스 제공
- 딥링크 구현, 처리
- 네비게이션 UI 패턴(탐색 창, 하단 탐색) 포함
- Safe Args : 대상 사이에서 데이터를 탐색하고 전달 시 타입 안정성을 제공하는 그래프 플러그인
- ViewModel 지원 : 네비게이션 그래프에 대한 뷰모델을 확인해 그래프 대상 사이에 UI 관련 데이터를 공유한다

 

액티비티 안에서 사용하는 프래그먼트를 다룰 때 쓸 수 있어 보이는 라이브러리다.

그럼 이것을 어떻게 사용하는지 확인해보자.

 

먼저 라이브러리인 만큼 의존성을 앱 수준 gradle에 넣어야 한다. 그리고 프로젝트 수준 gradle에도 네비게이션 의존성을 넣어야 한다. 프로젝트 수준 gradle은 이렇게 했다.

 

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    ext {
        navigation_version = '2.4.2'
    }
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.2"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        jcenter() // Warning: this repository is going to shut down soon
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

 

그리고 앱 수준 gradle에 네비게이션 의존성을 추가한다.

 

def navigation_version = '2.3.2'
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"

 

다 했으면 왼쪽을 보자. 세로로 Resource Manager라고 써진 탭이 있는데 이걸 누르고 Navigation 탭을 누른 뒤 왼쪽 위의 +를 눌러 Navigation Resource File을 클릭해 네비게이션 그래프를 만든다. 난 미리 만들어놔서 밑에 만들어뒀던 네비게이션 그래프들이 보인다. 이 포스팅에선 nav_graph2를 기준으로 설명한다.

 

 

nav_graph2를 만들었다면 이 안에 들어갈 프래그먼트를 만들어야 한다.

FirstFragment, SecondFragment, ThirdFragment를 만든다.

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".navigation2.FirstFragment">

    <Button
        android:id="@+id/test_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="버튼"/>

</androidx.constraintlayout.widget.ConstraintLayout>
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.core.os.bundleOf
import androidx.navigation.NavController
import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController
import com.example.kotlinprac.R

class FirstFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val button = view.findViewById<Button>(R.id.test_button)
        button.setOnClickListener {
            findNavController().navigate(R.id.action_firstFragment_to_secondFragment, bundleOf("item" to "123"))
        }
    }
}

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".navigation2.SecondFragment">

    <TextView
        android:id="@+id/second_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="Second Fragment"
        android:textSize="40sp"
        android:textColor="@color/black"/>

    <Button
        android:id="@+id/second_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/second_textview"
        android:text="3번 프래그먼트로 이동"/>

</androidx.constraintlayout.widget.ConstraintLayout>
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.core.os.bundleOf
import androidx.navigation.fragment.findNavController
import com.example.kotlinprac.R

class SecondFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_second, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val item = arguments?.getString("item")
        Toast.makeText(requireActivity(), "item : $item", Toast.LENGTH_SHORT).show()

        val button = view.findViewById<Button>(R.id.second_button)
        button.setOnClickListener {
            findNavController().navigate(
                R.id.action_secondFragment_to_thirdFragment, bundleOf("intValue" to 123)
            )
        }
    }
}

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".navigation2.ThirdFragment">

    <TextView
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="Third Fragment"
        android:textSize="40sp"
        android:textColor="@color/black"/>

</androidx.constraintlayout.widget.ConstraintLayout>
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.example.kotlinprac.R

class ThirdFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_third, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val receivedValue = arguments?.getInt("intValue") ?: -1
        Toast.makeText(requireActivity(), "SecondFragment에서 받은 값 : $receivedValue", Toast.LENGTH_SHORT).show()
    }
}

 

Safe Args도 사용했기 때문에 관련 코드들이 보인다. 빨간 줄이 많이 보일텐데 nav_graph2를 아래와 같이 작성한다.

 

<?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_graph2"
    app:startDestination="@id/firstFragment">
    <fragment
        android:id="@+id/firstFragment"
        android:name="com.example.kotlinprac.navigation2.FirstFragment"
        android:label="fragment_first"
        tools:layout="@layout/fragment_first" >
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment" />
        <argument
            android:name="title"
            app:argType="string"/>
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.example.kotlinprac.navigation2.SecondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" >
        <action
            android:id="@+id/action_secondFragment_to_thirdFragment"
            app:destination="@id/thirdFragment" />
        <argument
            android:name="argumentTest"
            app:argType="integer" />
    </fragment>
    <fragment
        android:id="@+id/thirdFragment"
        android:name="com.example.kotlinprac.navigation2.ThirdFragment"
        android:label="fragment_third"
        tools:layout="@layout/fragment_third" />
</navigation>

 

이렇게 하고 화면을 보면 화면 3개가 에디터 안에 들어있을 텐데 적당히 보기 좋게 정리해준다.

 

 

Safe Args를 추가하려면 아무 프래그먼트나 누르고 오른쪽 Attributes 창의 밑을 보면 Argument Default Values 탭이 있다.

그 탭의 오른쪽에 + 버튼을 누르면 팝업창이 뜨면서 이름, 타입, 배열과 null 여부, 기본값을 정할 수 있다. 본인한테 필요한 값을 정의해준다.

그 후 빌드해서 버튼을 누르면 아래와 같이 작동할 것이다.

 

 

3번 탭에서 뒤로가기를 누르면 2번 -> 1번 프래그먼트 순으로 화면이 알아서 이동하는 걸 볼 수 있다. 네비게이션 컴포넌트를 사용하지 않았다면 별도 처리를 하지 않았기 때문에 앱이 그냥 종료됐을 것이다.

이것 외에도 매우 편한 기능들이 많이 있어서 더 공부하고 써보면 좋을 것 같다.

반응형
Comments