기본적인 Jetpack Navigation + Bottom Navigation View의 경우에는 BottomNavigationView를 클릭할 때마다 Fragment가 재생성된다.
최초에만 Fragment가 생성되고 Fragment들을 Show/Hide 하는 방향으로 구현하려 한다.
Custom Navigation
FragmentNavigator Class 생성
Annotation인 @Navigator.Name("keep_state_fragment") 는 해당 내비게이터의 이름이고 graph.xml에서 <keep_state_fragment>와 같은 형태로 쓰일 TAG의 이름을 설정한다.
해당 FragmentNavigator의 navigate를 override 하여 재정의함으로써 기존의 프래그먼트 재생성 문제를 해결
@Navigator.Name("keep_state_fragment")
class KeepStateNavigator(private val context: Context, private val manager: FragmentManager, private val containerId: Int)
: FragmentNavigator(context, manager, containerId) {
override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras?): NavDestination? {
val tag = destination.id.toString()
val transaction = manager.beginTransaction()
var initialNavigate = false
val currentFragment = manager.primaryNavigationFragment
// primaryNavigationFragment 가 존재하면 기존 primaryFragment hide 처리 (재생성 방지)
if (currentFragment != null) {
transaction.hide(currentFragment)
} else {
initialNavigate = true
}
var fragment = manager.findFragmentByTag(tag) // 최초로 생성되는 fragment
if (fragment == null) {
val className = destination.className
fragment = manager.fragmentFactory.instantiate(context.classLoader, className)
transaction.add(containerId, fragment, tag) // add로 fragment 최초 생성 (add)
} else {
transaction.show(fragment) // 이미 생성되어 있던 fragment 라면 show
}
transaction.setPrimaryNavigationFragment(fragment) // destination fragment 를 primary 로 설정
// transaction 관련 fragment 상태 변경 최적화
transaction.setReorderingAllowed(true)
transaction.commitNow()
return if (initialNavigate) {
destination
} else {
null
}
}
}
존재하는 기존 Primary Fragment가 있을시 Hide 처리를 하여 Fragment가 Replace(재생성)되지 않게 한다.
Destination Fragment가 FragmentManager에 존재한다면 기존에 Hide 처리되어 있던 Fragment를 Show 하여 Replace(재생성)이 아닌 상태를 유지하게 하고,
존재하지 않는다면 최초 생성이므로 FragmentFactory를 통해 instance 생성 후 add.
만들어준 Custom Navigator를 Activity 단에서 navController에 추가하고, bottom Navigation과 navController를 연결
직접 Custom 한 Navigator를 추가해야 한다면 아래와 같이 하드코딩으로 Navigator를 추가한 후
setGraph로 그래프를 추가해주어야 Navigator가 정상적으로 그래프에 반영.
val navHostFragment = supportFragmentManager.findFragmentById(binding.navMainFragment.id) as NavHostFragment
val navController = navHostFragment.navController
//Custom Navigator 추가
val navigator = KeepStateNavigator(this, navHostFragment.childFragmentManager, binding.navMainFragment.id)
navController.navigatorProvider += navigator
navController.setGraph(R.navigation.nav_main)
//BottomNavigation 연결
binding.bottomNavigationView.setupWithNavController(navController)
기존 Navigation을 사용 할 때에는 xml에서 FragmentContainerView에 바로 graph를 설정하였으나, 이렇게 설정하면 navigator를 찾을 수 없다는 오류를 발생, xml에서 설정해준 그래프 설정은 지워주자
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_main_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintTop_toTopOf="parent" />
<!--
app:navGraph="@navigation/nav_main"
Custom Navigator 사용을 위한 navGraph 제거
-->
이후 Navigation 그래프에 Custom 한 Navigator를 지정 <keep_state_fragment> 사용
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_main"
app:startDestination="@id/homeFragment">
<keep_state_fragment
android:id="@+id/homeFragment"
android:name="com.project.kovid.function.home.HomeFragment"
android:label="HomeFragment" />
<keep_state_fragment
android:id="@+id/worldFragment"
android:name="com.project.kovid.function.world.WorldFragment"
android:label="WorldFragment" />
<keep_state_fragment
android:id="@+id/mapFragment"
android:name="com.project.kovid.function.map.MapFragment"
android:label="MapFragment" />
<keep_state_fragment
android:id="@+id/newsFragment"
android:name="com.project.kovid.function.news.NewsFragment"
android:label="NewsContainerFragment" />
</navigation>
Singleton Navigation Host
https://github.com/STAR-ZERO/navigation-keep-fragment-sample
https://hungseong.tistory.com/35?category=518367
'Android > Reference' 카테고리의 다른 글
권한 체크 Permission Check (0) | 2022.02.23 |
---|---|
Fragment ViewModel 공유 (0) | 2022.02.22 |
LiveData setValue(), postValue() 차이 (0) | 2022.02.16 |
AAC (MVVM + RoomDB + RecyclerView) 실습 (0) | 2022.02.09 |
AAC (Android Architecture Component) (1) | 2022.02.07 |