-
Android Architecture Components #4 - NavigationAndroid/AAC 2019. 8. 5. 18:04
Navigation을 사용하려면 Fragment에 대해서 잠시 짚고 넘어가야 한다.
Fragment는 하나의 액티비티가 여러 개의 화면을 가지도록 만들기위해 고안된 개념으로 옛날에 태블릿의 화면 구성을 위해서 1개의 액티비티의 3개의 화면을 출력하고 싶으나 그 당시 View로서는 불가능하거나 너무 어려운 처지였다. 그래서 Fragment라는 것을 만들었다고 한다.
이러한 Fragment를 좀 더 쉽게 다루고 화면 전환시의 코드나 전체적인 앱의 흐름을 파악하기 좋게 하기 위해
IOS의 스토리보드가 있듯이 안드로이드에도 Navigation이라는 것을 만들어서 사용을 하게 되었다.
단, Activity는 지원하지 않으며 오직 Fragment만 지원을 한다. 그래서 액티비티 1개에 여러개의 프래그먼트가 들어가는 개발방법을 사용하여 소개를 해보도록 하겠다.
그전에 Navigation을 만들때 또는 앱을 만들때 권고사항 들이니 한번 읽어서 권고사항을 지켜가면
좀 더 나은 앱이 나올것 같다.
The app should have a fixed starting destination
앱은 하나의 고정된 시작지점을 가져야 한다.
시작화면은 앱을 실행 시켰을때와 back button에 의해 가장 마지막에 보이는 화면이 일치하여야 한다.
셋업이나 로그인 같은 조건부 화면을 시작 화면으로 사용하면 안된다.
A stack is used to represent the "navigation state" of an app
앱의 네이게이션 상태는 stack 구조를 가져야 한다.
The Up button never exits your app
업버튼으로 앱이 절때 종료되면 안된다.
Up and Back are equivalent within your app's task
업과 백버튼의 동작은 동일 해야 한다.대상에 깊이 연결하거나 동일한 대상에 탐색하면 동일한 스택이 생성되어야 한다.
Deep linking to a destination or navigating to the same destination should yield the same stack
Gradle Dependency
implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.0.0'
1. Navigation 생성하기
Android Project에서 Resource Directory를 생성해주는데 name과 type을 navigation으로 작성하고 OK 눌러줍니다
이번엔 Resource File을 생성해줍니다. File name은 Main으로 입력하였습니다.
만약 Gradle Dependency가 적용되어 있지 않다면 적용 시킬 것이냐고 창이 뜹니다. "OK"
기본적인 Navigation 화면이다
좌측 : 패널 목록 : 현재 그래프 편집기에 있는 모든 대상을 나열
중앙 : 그래프 편집기 : Fragment를 시각적으로 출력해주고 해당 Fragment의 XML 창으로 바로 이동 가능합니다.
우측 : 속성 : 선택된 Fragment의 속성을 표시합니다.
2. 화면 구성하기
화면을 추가하는 아이콘처럼 생긴것을 눌러 Create new Destination을 눌러줍니다.
첫번째 프레그먼트를 추가시켜 줍니다.
좌측에는 추가시킨 Fragment 목록이 보이고 중간엔 추가시킨 Fragment 화면이 보입니다.
계속해서 총 2개의 Fragment를 더 추가시켜 주겠습니다.
총 3개의 화면을 구성 하였고 다음과 같이 구성해보도록 하겠습니다.
OneFragment -> TwoFragment
TwoFragment -> ThreeFragment ( 입력한 이름이 넘어가도록 설정 )
위의 화면을 더블클릭하면 에디터로 넘어갑니다.
Layout을 구성해주도록 합시다.
레이아웃을 구성 후 One -> Two -> Three 순서이니 Fragment에 커서를 올리면 점이 나옵니다! 그걸 다음에 출력될
Fragment랑 연결시켜줍니다.좌측에 보시면 Actions라고 어떠한 행동이 보입니다. 한번 Text로 가서 코드를 보도록 합시다.
action 태그에 보시면 destination ( 목적지 ) 가 twoFragment로 설정되어 있는 걸 볼 수 있습니다.
보시다시피 간단하게 이미지로 해당 Fragment는 어디로 연결되어 있고 어디로 갈 수 있는지를 볼 수 있습니다.
또한 Two -> Three 로 넘어갈때 이름을 받아 출력을 해야 함으로 좌측에서 Arguments를 추가해줍니다.
3. MainActivity랑 연결시켜주기
생성한 것들은 Fragment입니다. 실제로 Activity를 통해 Fragment를 볼 수 있으니 MainActivity를 수정해보도록 합시다.
<?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=".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/main"/> </androidx.constraintlayout.widget.ConstraintLayout>
MainActivity에 Fragment를 추가하고 NavHost를 true 원하는 Navigation Graph를 아까 생성한 Navigation으로 잡아줍니다.
4. 클래스 코드 작성하기
4-1. 화면 이동하기
package com.kkomi.myapplication import android.content.Context import android.net.Uri import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup // 이름과 매치되게 변수명과 값을 변경해줍니다. private const val ARG_PARAM1 = "param1" private const val ARG_PARAM2 = "param2" /** * 간단한 Fragment 클래스 입니다. * 해당 Fragment를 상속받은 클래스는 다음을 구현해야 합니다. * OnFragmentInteractionListenr : 상호작용 이벤트 인터페이스 * newInstance : 해당 Fragment 인스턴스 생성을 위한 Factory Method */ class OneFrament : Fragment() { // 매개 변수 이름 변경 private var param1: String? = null private var param2: String? = null private var listener: OnFragmentInteractionListener? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { param1 = it.getString(ARG_PARAM1) param2 = it.getString(ARG_PARAM2) } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_one_frament, container, false) } fun onButtonPressed(uri: Uri) { listener?.onFragmentInteraction(uri) } override fun onAttach(context: Context) { super.onAttach(context) if (context is OnFragmentInteractionListener) { listener = context } else { throw RuntimeException(context.toString() + " must implement OnFragmentInteractionListener") } } override fun onDetach() { super.onDetach() listener = null } /** * 현재 Fragment와 다른 Fragments와 상호작용을 하는 인터페이스 입니다. */ interface OnFragmentInteractionListener { fun onFragmentInteraction(uri: Uri) } companion object { /** * 매개변수를 이용한 새 인스턴스를 사용하려면 Factory Method를 사용하세요 */ @JvmStatic fun newInstance(param1: String, param2: String) = OneFrament().apply { arguments = Bundle().apply { putString(ARG_PARAM1, param1) putString(ARG_PARAM2, param2) } } } }
무지 막지하게 있는 코드들 중 필요한 부분만 간추려 냈습니다.
package com.kkomi.myapplication import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.findNavController import kotlinx.android.synthetic.main.fragment_one_frament.* class OneFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_one_frament, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { one_button_next.setOnClickListener { view.findNavController().navigate(R.id.action_oneFrament_to_twoFragment) } super.onViewCreated(view, savedInstanceState) } }
아까 위에서 생성된 action을 실행 해주시면 됩니다.
4-2. 화면 이동시 데이터 넘기기
package com.kkomi.myapplication import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.findNavController import kotlinx.android.synthetic.main.fragment_two.* class TwoFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_two, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { two_button_next.setOnClickListener { val bundle = Bundle() bundle.putString("name", two_edit_name.text.toString()) view.findNavController().navigate(R.id.action_twoFragment_to_threeFragment, bundle) } super.onViewCreated(view, savedInstanceState) } }
기존 방법과 동일하게 Bundle로 넘겨주면 됩니다.
package com.kkomi.myapplication import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import kotlinx.android.synthetic.main.fragment_three.* class ThreeFragment : Fragment() { lateinit var name: String override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { arguments?.let { name = it.getString("name") } return inflater.inflate(R.layout.fragment_three, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { three_view_name.text = name super.onViewCreated(view, savedInstanceState) } }
기존 방식처럼 데이터도 받아주시면 됩니다
이렇게 하면 번거롭다고 생각이 들겠지만 아까 작업했던 nav로 다시 가보시면
지금은 적은 양의 Fragment지만 만약 화면이 많아 질 수록 관리가 쉬워진다는 장점이 있습니다.
개발자의 취향으로 사용하실 분들은 사용하시면 될 것 같습니다.
'Android > AAC' 카테고리의 다른 글
Android Architecture Components #6 - Paging (0) 2019.08.06 Android Architecture Components #5 - Room (0) 2019.08.05 Android Architecture Components #3 - LiveData (0) 2019.07.31 Android Architecture Components #2 - ViewModel (0) 2019.07.31 Android Architecture Components #1 - DataBinding (0) 2019.07.24