본문 바로가기

안드로이드/개념정리

[AOS] Navigation Component

반응형

Navigation Component는 하나의 Activity에 다수 Fragment를 뒀을때 화면 이동을 도와줍니다.

기존에 Fragment Manager을 사용해서 Fragment들의 스택들과 Fragment별 전환 및 전환 애니메이션을 관리하던 방법은 다소 복잡해서 사용이 꺼려졌었는데 'Navigation Component'를 통해 관리하면 보다 직관적이고 간편하게 관리할 수 있습니다.

참고 자료

※AndroidStudio 버전은 3.3 이상이어야 합니다. 

 

| build.gradle

    def nav_version = "2.3.5"

    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

| Activity 및 Fragment 추가

단일 Activity에 표시할 Fragment를 3개 생성합니다. (ListFragment, AddFragment, AlarmFragment)

| Navigation graph 추가

Navigation graph는 화면이동에 대한 모든 정보가 정의된 xml파일로 res폴더에 navigaion 폴더를 생성 후 'Navigation Resource File'을 생성합니다.  

생성된 파일에 'Click to add a destination' 을 클릭하면 프로젝트에 생성되어 있는 destination들중 선택할 수 있는 창이 아래 사진 처럼 뜹니다.

destination이란 Navigation을 이용해 이동할 수 있는 activity나 fragment와 같은 앱의 화면을 뜻합니다. 

아래 코드는 위에서 생성한 nav_main.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_main"
    app:startDestination="@id/listFragment">

    <fragment
        android:id="@+id/listFragment"
        android:name="com.wony.remind.main.list.ListFragment"
        tools:layout="@layout/fragment_list"
        android:label="fragment_list" />
</navigation>

| activity_main.xml

생성한 graph파일을 activity와 연결합니다.  

  • android:name에 NavHost를 참조한 NavHostFragment를 작성합니다.
  • app:navGraph엔 연결할 그래프를 작성합니다.
  • app:defaultNavHost를 true로 해주면 해당 엑티비티가 시스템 백 버튼을 가로채 fragment 스텍들을 이동하게 합니다. navHost는 하나만 지정해야 합니다.
<?xml version="1.0" encoding="utf-8"?>
<layout>

    <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: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_main" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

| 화면 이동

앞서 생성한 navGraph의 action을 이용해 화면간 전환이 가능하며, 애니메이션 효과도 간단하게 추가할 수 있습니다. 

화면에 마우스를 가져다 댔을때 생기는 원을 끌어다가 다른 화면에 놓는 방법이나 화면에 우클릭 후 'AddAction' 을 선택하는 방법 2가지중 원하는걸로 추가합니다. 

nav_graph.xml 

Listfragment -> Addfragment로 이동하기 위해 정의된 action입니다. 

    <fragment
        android:id="@+id/listFragment"
        android:name="com.wony.remind.main.list.ListFragment"
        android:label="fragment_list"
        tools:layout="@layout/fragment_list">
        <action
            android:id="@+id/action_listFragment_to_addFragment"
            app:destination="@id/addFragment"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim"/>
    </fragment>

navController에 설정한 action id 값을 인자로 넘겨주거나 id로 생성된 액션명을 사용하는 방법 2가지중 하나를 선택하면 됩니다.

        binding.btAdd.setOnClickListener {
            findNavController().navigate(R.id.action_listFragment_to_addFragment)
            
            //or
            
            findNavController().navigate(ListFragmentDirections.actionListFragmentToAddFragment())
        }

navController는 아래 방법으로 접근 가능하며 navigate 함수 파라미터론 액션의 아이디 명이나 액션을 넣을 수 있습니다. 

- fragment 

    fun moveNavi(action: NavDirections){
        findNavController().navigate(action)
    }

- activity

nav_host_fragment는 위에서 작성한 activity_main.xml을 참고하시면 됩니다.

            var navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
            navHostFragment.navController.navigate(action)

|fragment간 데이터 전달

기존처럼 Navigationd에서도 Bundle을 이용해 데이터를 전달할 수 있지만 type-safe한 SafeArgs를 제공한다.

build.gradle ( project.level ) 

buildscript 
    ext.nav_version = '2.3.5'
	....
    dependencies {
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

build.gradle ( app.level )

plugins {
    id 'androidx.navigation.safeargs.kotlin'
}

nav_graph.xml

argument태그로 전달하거나 받을 데이터를 명시합니다. 데이터를 전달받는 fragment태그안에 명시해야 액션을 생성할때와 받을때 모두 편안하게 사용할 수 있습니다. 

데이터의 타입은 공식문서를 참고해주시고 long타입시 defaultValue에는 꼭 'L'을 붙여서 사용해야 합니다. 

<?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_main"
    app:startDestination="@id/listFragment">

    <fragment
        android:id="@+id/listFragment"
        android:name="com.wony.remind.main.list.ListFragment"
        android:label="fragment_list"
        tools:layout="@layout/fragment_list">
        <action
            android:id="@+id/action_listFragment_to_addFragment"
            app:destination="@id/addFragment"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim"/>

        <action
            android:id="@+id/action_listFragment_to_alarmFragment"
            app:destination="@id/alarmFragment"/>
    </fragment>
    <fragment
        android:id="@+id/addFragment"
        android:name="com.wony.remind.main.add.AddFragment"
        android:label="AddFragment">
        <argument
            android:name="id"
            android:defaultValue="0L"
            app:argType="long" />
    </fragment>
    <fragment
        android:id="@+id/alarmFragment"
        android:name="com.wony.remind.main.alarm.AlarmFragment"
        android:label="AlarmFragment" >
        <argument
            android:name="id"
            android:defaultValue="0L"
            app:argType="long" />
    </fragment>
</navigation>

 

| 데이터 전송

위에 작성된 argument가 액션 생성시 전달인자로 나타납니다.

ListFragment에서 AddFragment로 데이터의 아이디 값을 전달하여 추가되어 있는 데이터의 정보를 수정하기 위한 코드 입니다.

    fun moveAdd(id: Long){
        var action = ListFragmentDirections.actionListFragmentToAddFragment(id)
        findNavController().navigate(action)
    }

| 데이터 전달받기 

부가적인 코드를 제외하고 데이터를 전달받는 코드만 남겼습니다.

bundle을 이용하면 타입이 맞지 않을 경우 널체크도 해야하고, bundle Key값을 변수로 선언해서 사용해야 합니다. 하지만 safeArgs를 이용하면 아래와 같이 보다 깔끔하고 안전하게 사용할 수 있습니다. 

class AddFragment : BaseFragment<FragmentAddBinding, AddVM>() {
	.....
    private val args: AddFragmentArgs by navArgs()

    override fun init() {
        viewModel.setSelectId(args.id)
		....
    }
}

| 번들을 이용한 데이터 전송

val bundle = bundleOf("id" to id)
view.findNavController().navigate(R.id.action_listFragment_to_addFragment, bundle)

| 번들을 이용한 데이터 받기

id = arguments?.getLong("id") ?: 0L

 

방법을 다양하지만 어떤것들이 있고 장단점은 무엇인지 알고난 후 상황에 맞는것을 사용하세요 ! 

평소에 사용해보지 않았던 부분을 이직을 준비하면서 공부한 내용을 정리해 다소 부족할 수 있습니다. 그점 양해해주셔서 봐주시면 감사하겠습니다.

궁금하신 내용은 여느때처럼 남겨주세요 ! 

위 코드는 github에 올려놨습니다.

 

GitHub - NamGungWon/remind

Contribute to NamGungWon/remind development by creating an account on GitHub.

github.com

 

 

 

 

 

 

반응형

'안드로이드 > 개념정리' 카테고리의 다른 글

[AOS] Kotlin 확장 함수  (0) 2021.10.08