지금까지 예제에서는 앱에 인텐트를 추가하여 액티비티 사이를 이동했다. 하지만 모든 화면마다 별도의 액티비티가 필요한 건 아니다. 여러 일반적인 UI 패턴(예: 탭)이 Fragment라는 섹션을 사용해 단일 액티비티 내에 존재한다.
하단 탭을 눌러 메뉴를 이동하면 인텐트가 트리거되지 않고(=다른 액티비티가 실행되지 않고) 이전 프래그먼트가 다른 프래그먼트로 교체된다.
위와 같이 단일 액티비티에 여러 프래그먼트가 동시에 존재 할 수도 있다.
프래그먼트 수명 주기에는 Lifecycle.State 열거형으로 표현되는 다섯 가지 상태가 있다.
액티비티와 마찬가지로 상태전환을 할 수 있는 여러 함수가 있다.
중간 부분을 뭉뚱그리고 생성/삭제 부분만 보면 프래그먼트 만들기- 뷰만들기/ 뷰 삭제-프래그먼트 삭제.. 이런 느낌?
※액티비티의 수명 주기와의 차이점
onCreate()
메서드에 차이가 있다.
액티비티에서는 onCreate()
를 사용하여 레이아웃을 확장하고 뷰를 바인딩한다.
하지만 프레그먼트 수명 주기에서 onCreate()
는 뷰가 만들어지기 전에 호출되므로 여기서 레이아웃을 확장할 수 없다.(대신 대신 onCreateView()
에서 확장하고, onViewCreated()
에서 특정 뷰에 바인딩할 수 있다.)
안드로이드 Jetpack 라이브러리는 Navigation component를 제공한다.
NavHost
: 액티비티 내에서 Navigation Graph의 대상을 표시하는데 사용된다. 프래그먼트 간 이동이 이루어지면 NavHost에 표시되는 대상이 업데이트된다.NavController
: NavController
객체를 사용하면 NavHost
에 표시되는 Navigation들을 제어할 수 있다. 표시되는 프래그먼트를 교체하거나 이전 프래그먼트로 이동하는등..NavHost
역할을 할 FragmentContainerView
를 activity_main.xml에 작성해 보자! 이렇게 하면 앱의 모든 Navigation은 FragmentContainerView
내에서 실행되게 된다.
<FrameLayout
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">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
app:navGraph="@navigation/nav_graph"
에 생긴 오류에 대한 제안 사항을 눌러 nav_graph.xml을 만들자.
XML 파일을 만들면 새 편집기가 표시된다.
상단의 초록색+가 붙은 버튼을 눌러 프래그먼트들을 추가하고, 화살표로 연결해준다.
letterListFragment에서 선택한 단어가 wordListFragment로 전달되어야 한다. 단어가 매개변수로 전달될 수 있도록 arguments에서 +를 눌러 추가해준다.
그리고 집모양 버튼을 눌러 letterListFragment를 시작 화면으로 설정해 준다.
holder.button.setOnClickListener {
val action = LetterListFragmentDirections.actionLetterListFragmentToWordListFragment(letter = holder.button.text.toString())
holder.view.findNavController().navigate(action)
}
LetterAdapter의 리스너도 적절히 작성해 준다.
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
setupActionBarWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
MainActivity에서 navController
를 설정하는 코드를 추가한다.
private lateinit var letterId: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//let()은? arguments가 null이면 안쪽의 코드가 실행되지 않고 null이 아니면 실행됨.
arguments?.let {
//it:bundle
letterId = it.getString(LETTER).toString()
}
}
마지막으로 WordListFragment에서 매개변수를 받아오는 코드를 onCreate
에 작성한다.