본문 바로가기
🖥 Programming/📱 Android (Kotlin)

[Android][kotlin] Android Jetpack Navigation

by MinChan-Youn 2021. 11. 9.

Jetpack은 Android 개발을 빠르게 도와주는 컴포넌트 라이브러리입니다. 

2018 Google I/O 행사에서 Jetpack Navigation이 소개되었다고 하네요.

Android Studio 3.3버전부터 Navigation Editor 기능이 생기면서 Xcode처럼 UI를 통한 Navigation 편집이 가능해졌습니다.

 

그렇다면 Jetpack Navigation 사용방법에 대해서 알아보겠습니다.

 

Step 1. gradle에 Navigation관련 추가

    //Jetpakc-Navigation : Kotlin
    def nav_version = "2.3.5"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

 

Step 2. Navigation 탐색 그래프 만들기

res폴더 우클릭 -> Android Resouce File -> File name, Resource type: Navigation으로 설정 후 생성 -> res 폴더안에 navigation 폴더가 생성된것을 확인

 

Step 3. fragment 생성

navigation생성한 파일을 Design보기로 변경하여 다음과 같이 버튼을 눌러 fragment를 생성합니다.

 

 

Step 4. MainActivity에 navigation을 보여줄 framgnet로 셋팅

<?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=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

<androidx.fragment.app.FragmentContainerView를 설정하여

app:navGraph설정 등을 해줍니다.

 

 

여기까지 왔으면 navigation과 fragment설정을 끝났으며 각 코드를 작성하여 프로그램을 만들어 줍니다.

 

 

전체코드

----------------------------------------------------------------------------------------------------------------------

//MainActivity.kt

package com.example.jetpack_navigation_kotlin

//MainActivity.kt
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- acitivity_main.xml -->
<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=".MainActivity">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

//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/fragment1">

    <fragment
        android:id="@+id/fragment1"
        android:name="com.example.jetpack_navigation_kotlin.fragment1"
        android:label="fragment1_fragment"
        tools:layout="@layout/fragment1_fragment" >
        <action
            android:id="@+id/action_fragment1_to_fragment2"
            app:destination="@id/fragment2" />
    </fragment>

    <fragment
        android:id="@+id/fragment2"
        android:name="com.example.jetpack_navigation_kotlin.fragment2"
        android:label="fragment_fragment2"
        tools:layout="@layout/fragment_fragment2" >
        <action
            android:id="@+id/action_fragment2_to_fragment1"
            app:destination="@id/fragment1" />
    </fragment>
</navigation>

 

//fragment1.kt

package com.example.jetpack_navigation_kotlin

import androidx.lifecycle.ViewModelProvider
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.example.jetpack_navigation_kotlin.databinding.Fragment1FragmentBinding

class fragment1 : Fragment() {

    private var _binding: Fragment1FragmentBinding? = null
    private val binding get() = _binding!!

//    companion object {
//        fun newInstance() = fragment1()
//    }

    private lateinit var viewModel: Fragment1ViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        _binding = Fragment1FragmentBinding.inflate(inflater, container, false)

        findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>("return")?.observe(viewLifecycleOwner){
            binding.tv1.text = "fragment1에서 받은 데이터 ${it}"
        }

        binding.tvMovefragment1.setOnClickListener {
            //fragment2로 보낼 데이터 set
            val bundle = Bundle()
            bundle.putString("data", binding.et1.text.toString())

            //fragment2로 이동
            this.findNavController().navigate(R.id.fragment2, bundle)
        }

        return binding.root
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        //(테스트) Fragment - ViewModel 생성
        viewModel = ViewModelProvider(this).get(Fragment1ViewModel::class.java)
    }
}

 

//fragment1ViewModel.kt

package com.example.jetpack_navigation_kotlin

import androidx.lifecycle.ViewModel

class Fragment1ViewModel : ViewModel() {
    // TODO: Implement the ViewModel
}

 

//fragment1_fragment.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=".fragment1">

    <EditText
        android:id="@+id/et_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#00F0F0"
        android:gravity="center"
        android:hint="fragment2로 전달할 데이터를 입력하세요"
        app:layout_constraintBottom_toTopOf="@+id/tv_movefragment1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

    <TextView
        android:id="@+id/tv_movefragment1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#F0F0F0"
        android:text="go to fragment2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#F0F0F0"
        android:text="fargment2에서 받은 데이터"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_movefragment1"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

//fragment2.kt

package com.example.jetpack_navigation_kotlin

import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.addCallback
import androidx.navigation.fragment.findNavController
import com.example.jetpack_navigation_kotlin.databinding.FragmentFragment2Binding


class fragment2 : Fragment() {
    private var _binding: FragmentFragment2Binding? = null
    private val binding get() = _binding!!

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        _binding = FragmentFragment2Binding.inflate(inflater, container, false)

        //fragment1에서 받은 데이터 출력
        binding.tv2.text = arguments?.getString("data")

        //뒤로가기 TextView 클릭 시
        binding.tvMovefragment2.setOnClickListener {
            goBack()
        }

        //뒤로가기 버튼 눌렀을때
        requireActivity().onBackPressedDispatcher.addCallback{
            goBack()
        }

        return binding.root
    }

    private fun goBack(){
        //fragment1으로 보낼 데이터 set
        val returnMsg = binding.et2.text.toString()
        findNavController().apply {
            previousBackStackEntry?.savedStateHandle?.set("return", returnMsg)
            popBackStack()
        }

//        findNavController().previousBackStackEntry?.savedStateHandle?.set("return", returnMsg)
//        findNavController().popBackStack()
    }
}

 

//fragment_fragment2.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=".fragment2">

    <EditText
        android:id="@+id/et_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#00F0F0"
        android:gravity="center"
        android:hint="fragment1으로 전달할 데이터를 입력하세요"
        app:layout_constraintBottom_toTopOf="@+id/tv_movefragment2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

    <TextView
        android:id="@+id/tv_movefragment2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#F0F0F0"
        android:text="go to fragment1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#F0F0F0"
        android:text="fargment2에서 받은 데이터"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_movefragment2"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

//build.gradle (:app)

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 30

    defaultConfig {
        applicationId "com.example.jetpack_navigation_kotlin"
        minSdk 21
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }

    //View, Data Binidng
    buildFeatures {
        viewBinding true
        dataBinding true
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    //Jetpakc-Navigation : Kotlin
    def nav_version = "2.3.5"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}