반응형

fragment, activity간 이동을 위하여 복잡한 함수들 사용없이 jetpack 라이브러리 중 하나인 navigation을  이용하여 훨씬 쉽게 프레그먼트간의 이동을 구현할 수 있습니다!

의존성 추가

가장 먼저 build.gradle(Module)에 있는 dependencies에 아래와 같이 의존성을 추가해줍니다.

최신 버전은 여기서 확인할 수 있습니다.

dependencies {
  def nav_version = "2.4.2"

  // Java language implementation
  implementation("androidx.navigation:navigation-fragment:$nav_version")
  implementation("androidx.navigation:navigation-ui:$nav_version")

  // Kotlin
  implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
  implementation("androidx.navigation:navigation-ui-ktx:$nav_version")

  // Feature module Support
  implementation("androidx.navigation:navigation-dynamic-features-fragment:$nav_version")

  // Testing Navigation
  androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")

  // Jetpack Compose Integration
  implementation("androidx.navigation:navigation-compose:$nav_version")
}

NavigationGraph 생성

navigation의 destination, action등을 관리할 수 있는 NavigationGraph를 생성해보겠습니다.

app -> res -> New -> Android Resource File 경로에서 새 파일을 생성합니다.

Resource type은 Navigation으로 설정해줍니다.

 

res - navigation 경로에 nav_graph.xml이 생성된 것을 확인할 수 있습니다.

NavHost 생성

Navigation component의 핵심 부분 중 하나가 navigation host(NavHost)입니다. navigation host는 destination을 swap 해주는 빈 container입니다. navigation host 는 반드시 NavHost에서 파생되어야 합니다. 

  • Navigation Component는 하나의 엑티비티에 여러 개의 프레그먼트를 destination으로 사용하도록 디자인 되었다고 합니다.
    • 메인 엑티비티는 navigation grpha와 연관되어야 하고 필요에 따라 destination을 swap 하는 NavHostFragment를 포함해야 합니다.
  • 하나의 앱이 여러 개의 activity destination을 가지면 각 엑티비티는 각자의 navigation graph가 있어야 합니다.

메인 엑티비티에 NavHostFragment를 만들어주겠습니다.

activity_main.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=".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_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph"/>

</androidx.constraintlayout.widget.ConstraintLayout>
  • android:name 속성은 NavHost가 구현된 클래스의 이름을 포함합니다.
  • app:navGraph 속성은 위에서 만들어 두었던 navigationGraph와 NavHostFragment를 연관시킵니다. 
  • app:defaultNavHost="true"속성은 이 NavHostFragment를 default로 설정합니다. default로 설정된 NavHostFragment는 시스템 Back Button을 사용합니다. default NavHost는 하나만 지정할 수 있습니다.

프레그먼트 생성

간단한 프레그먼트 2개를 만들어보겠습니다.

FirstFragment.kt

package com.example.supersimplenav

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.supersimplenav.databinding.FragmentFirstBinding

class FirstFragment : Fragment() {

    val binding by lazy { FragmentFirstBinding.inflate(layoutInflater)}

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        
        return binding.root
    }

}

fragment_first.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=".FirstFragment">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30dp"
        android:textColor="@color/black"
        android:text="첫 번째 프레그먼트"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnToSec"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="두 번째 프레그먼트로 이동"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title"
        android:layout_marginTop="20dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

SecondFragment.kt

package com.example.supersimplenav

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.supersimplenav.databinding.FragmentSecondBinding

class SecondFragment : Fragment() {

    val binding by lazy { FragmentSecondBinding.inflate(layoutInflater)}

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        
        return binding.root
    }

}

fragment_second.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=".SecondFragment">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30dp"
        android:textColor="@color/black"
        android:text="두 번째 프레그먼트"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnToFir"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="첫 번째 프레그먼트로 이동"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title"
        android:layout_marginTop="20dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Destination 설정

프레그먼트간 이동을 위하여 아까 만들었던 nav_graph.xml로 가서 destination을 설정해주겠습니다.

 

Host에 activity_main이 정상적으로 등록된 것을 확인할 수 있습니다! 마우스가 올려져있는 곳인 New Destination을 눌러 FirstFragment, SecondFragment를 모두 가져옵니다.

해당 프레그먼트 부분에 마우스를 올리면 위의 사진과 같이 동그란 점이 나오는데 이 점이 시작하는 지점이 출발 프레그먼트, 이 점이 끝나는 곳이 도착 프레그먼트입니다. 첫 번째 -> 두 번째 이동과 두 번째 -> 첫 번째 이동을 모두 구현하고 싶기 때문에 firstFragment에서 secondFragment로, 그 반대로도 점을 이어줍니다.

두 개 사이의 거리에 따라 화살표의 모양이 달라지지만 두 프레그먼트가 정상적으로 생긴 것을 확인할 수 있습니다.

우측 상대에 있는 code 혹은 split버튼으로 현재 xml파일의 코드를 확인할 수 있습니다.

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

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.example.supersimplenav.FirstFragment"
        android:label="fragment_first"
        tools:layout="@layout/fragment_first" >
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.example.supersimplenav.SecondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" >
        <action
            android:id="@+id/action_secondFragment_to_firstFragment"
            app:destination="@id/firstFragment" />
    </fragment>
</navigation>

 

위와 같이 구성이 되어있습니다.

<fragment
    android:id="@+id/firstFragment"
    android:name="com.example.supersimplenav.FirstFragment"
    android:label="fragment_first"
    tools:layout="@layout/fragment_first" >
    <action
        android:id="@+id/action_firstFragment_to_secondFragment"
        app:destination="@id/secondFragment" />
</fragment>

<action>을 제외하고 속성들을 살펴보겠습니다.

  • <fragment 부분은 "Type field" 입니다.
    • Type field는 destination이 fragment로 구현될 것인지, activity로 구현될 것인지 혹은 custom class로 구현될 것인지를 가리킵니다.
  • android:label 부분은 "Label field" 입니다.
    • Label field는 유저가 읽을 수 있는 destination의 이름을 포함합니다. 이것은 UI로 표현될 수 있습니다. 
      • 예를 들면 setupWithNavController()를 사용하여 NavGraph를 Toolbar에 연결하는 경우.
      • 따라서 string resource사용이 권장됩니다.(위에서는 string resource를 사용하지 않음.)
  • android:id 부분은 "ID field" 입니다.
    • ID field는 코드에서 참조하는데 필요한 destination의 ID를 포함합니다

<action>부분은 위에서 작업한 프레그먼트를 연결하여 생긴 부분입니다. 프레그먼트 사이의 화살표가 action속성입니다.

<action> 안에서는 목적지(destination)를 설정하고 이것을 호출하기 위하여 id(action_firstFragment_to_secondFragment)를 구성합니다.

버튼 클릭 판정 구현(프레그먼트 이동)

action설정은 해놓았으므로 이제 각 프레그먼트에 있는 버튼에 클릭 리스너를 설정하여 1 -> 2, 2 -> 1로 이동을 구현해보겠습니다.

FirstFragment.kt

...
override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        binding.btnToSec.setOnClickListener {
            findNavController().navigate(R.id.action_firstFragment_to_secondFragment)
        }
        return binding.root
    }
    
...

SecondFragment.kt

...
override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        binding.btnToFir.setOnClickListener {
            findNavController().navigate(R.id.action_secondFragment_to_firstFragment)
        }
        return binding.root
    }
...

FirstFragment와 SecondFragment안에 있는 onCreateView에서 각 버튼에 setOnClickListener을 설정하였고

버튼이 클릭된다면 findNavController()로 NavController를 찾아온 후 navigate(R.id.action...)을 통하여 해당 네비게이션 동작을 하도록 만들었습니다.

동작 모습

잘 이동하는 것을 확인할 수 있습니다!

 

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기