Navigation을 이용한 Fragment 설계

지프치프·2022년 6월 28일
0

Android

목록 보기
51/89
post-thumbnail

“Android 로봇은 Google에서 제작하여 공유한 저작물을 복제하거나 수정한 것으로 Creative Commons 3.0 저작자 표시 라이선스의 약관에 따라 사용되었습니다.”


개요

Jetpack의 구성요소 중에는 Navigation이라는 컴포넌트가 있다.
Fragment들의 전환 및 이동을 좀 더 쉽게 도와주는, 설계 해주는 컴포넌트로 FragmentTransaction을 통해서 replace 되던 기존의 방식을 대체한다.

구성요소

Navigation의 주요 구성요소는 3가지가 있다.

  • Navigation Graph
    XML 리소스 파일로 모든 Fragment들의 흐름을 화살표(경로)로 표시한다.

  • NavHost
    Navigation Graph에서 Fragment를 표시하는 빈 컨테이너로써
    기본 NavHost를 구현하는 NavHostFragment가 포함된다.

  • NavController
    NavHost에서 Navigation을 관리하는 객체이다.

구현

구현해볼 예제는 로그인을 하여 성공하면 SuccessFragment를 띄우고
실패하면 FailFragment를 띄우는 예제를 만들어본다.

먼저 app 수준의 build.gradle에서 의존성을 추가해주자

implementation 'androidx.navigation:navigation-fragment-ktx:2.4.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.2'

완성된 예제의 Graph는 아래와 같다.

Navigation에서는 각 Fragment를 Destination이라고 부르고
이 Destination들의 관계를 나타내는 화살표를 action이라고 부른다.
코드를 직접 작성해줘도 되겠지만
UI로 작성하는 것이 좀 더 편하니 UI 작성을 기준으로 설명하겠다.

Destination


위 버튼을 누르면 Fragment 리스트가 드롭다운에 표시되는데
이 중에 선택해서 누르면 Graph에 추가가 된다.

Action


Destination에 커서를 올리면 위 사진과 같이 가장자리에 점이 생기는데
이 점을 끌어다 다른 Destination에 올려놓으면 action이 연결된다.

이렇게 해서 완성된 예제의 Grpah 코드는 아래와 같다.

<?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"
    android:id="@+id/nav_graph"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.jeepchief.navigationsample.fragment.MainFragment"
        android:label="MainFragment" >
        <action
            android:id="@+id/action_mainFragment_to_loginFragment"
            app:destination="@id/loginFragment" />
    </fragment>
    <fragment
        android:id="@+id/failFragment"
        android:name="com.jeepchief.navigationsample.fragment.FailFragment"
        android:label="FailFragment" >
        <action
            android:id="@+id/action_failFragment_to_mainFragment2"
            app:destination="@id/mainFragment" />
    </fragment>
    <fragment
        android:id="@+id/successFragment"
        android:name="com.jeepchief.navigationsample.fragment.SuccessFragment"
        android:label="SuccessFragment" />
    <fragment
        android:id="@+id/loginFragment"
        android:name="com.jeepchief.navigationsample.fragment.LoginFragment"
        android:label="LoginFragment" >
        <action
            android:id="@+id/action_loginFragment_to_successFragment"
            app:destination="@id/successFragment" />
        <action
            android:id="@+id/action_loginFragment_to_failFragment"
            app:destination="@id/failFragment" />
    </fragment>
</navigation>

자동 셍성되는 action의 이름은 일반적을
action_현재Fragment_to이동할Fragment라고 생각하면 되겠다.

MainActivity

XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id = "@+id/nav_host_fragment"
        android:name = "androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost = "true"
        app:navGraph = "@navigation/nav_graph" />

</RelativeLayout>

Activity에는 Fragment를 보여줄 container 역할을 하게된다.
app:defaultNavHost 설정을 통해 Navigation의 Host임을 지정해주고
app:navGraph 설정을 통해 적용할 graph를 지정한다.

Activity

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)


        binding.apply {
            navController = findNavController(R.id.nav_host_fragment)
        }
    }
}

navController릂 lateinit으로 선언한 뒤
findNavController()를 통해 Navigaion Host를 찾아주면 된다.

여기까지 진행했다면 MainActivity의 Fragmnet를 통해서

LoginFragment

class LoginFragment : Fragment() {
    companion object {
        const val ID = "jeepchief"
        const val PW = "123"
    }
    private var _binding: FragmentLoginBinding? = null
    private val binding get() = _binding!!
    private lateinit var navController: NavController

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        navController = Navigation.findNavController(view)
        binding.apply {
            btnLogin.setOnClickListener {
                if(
                    edtId.text.toString() == ID &&
                            edtPw.text.toString() == PW
                ) {
                    navController.navigate(R.id.action_loginFragment_to_successFragment)
                }
                else {
                    navController.navigate(R.id.action_loginFragment_to_failFragment)
                }
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}

MainActivity에서 했던 것처럼 먼저 navController를 선언해야 한다.
Navigation.findNavController(view)를 통해 navController를 가져 올 수 있다.

그 다음 navigate() 메소드를 통해 다음 action을 지정해주면
대상 fragmnet로 이동한다.

다른 Fragmnet들도 버튼 클릭을 통해 다음 action을 실행하는 코드는 똑같은 관계로 따로 예제 코드를 올리진 않겠다.

예제 전체 코드는 레포지토리에서 확인이 가능하다.

개인적으로 공부했던 것을 바탕으로 작성하다보니
잘못된 정보가 있을수도 있습니다.
인지하게 되면 추후 수정하겠습니다.
피드백은 언제나 환영합니다.
읽어주셔서 감사합니다.

profile
지프처럼 거침없는 개발을 하고싶은 개발자

0개의 댓글