Navigation (二) 概念原理
來看看 Navigation
是怎麼管理 Fragment 的跳轉和返回的管理, Navigation
主要有三個部分:
Navigation Graph : xml 檔,包含所有被管理的 Fragment,起始目標,換頁目標,返回目標。
NavHost : 用來展示 destanation 的容器, Navigation
提供了 NavHostFragment
這個類別用來展示 Fragment,通常會加上 app:defaultNaHost="true"
以攔截系統的返回事件。
<fragment
android:id="@+id/navHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNaHost="true"
app:navGraph="@navigation/nav_graph_main" />
NavHost
中的導航動作,通常是寫在點擊事件內完成 Fragment 的切換。 textView.setOnClickListener {
findNavController().navigate(R.id.action_Fragment1_to_Fragment2)
}
在 NavHostFragment
裡面加上 app:defaultNaHost="true"
攔截系統的返回事件後,在這個 Activity 的返回鍵事件就統一交給 NavHostFragment
做管理。
回到 nav_graph_main
<navigation
android:id="@+id/navigation"
app:startDestination="@id/blankFragment">
<fragment
android:id="@+id/Fragment1"
android:name="com.guanhong.mvvmpractice.view.navigation.Fragment1"
tools:layout="@layout/fragment_first" >
<action
android:id="@+id/action_Fragment1_to_Fragment2"
app:destination="@id/Fragment2" />
</fragment>
<fragment
android:id="@+id/Fragment2"
android:name="com.guanhong.mvvmpractice.view.navigation.Fragment2"
tools:layout="@layout/fragment_second" />
</navigation>
當呼叫到 action_Fragment1_to_Fragment2
這個 action,會從 Fragment1 -> Fragment2
這時候按下返回會默認回到這個 action 所在的 Fragment,以這個例子而言就是 Fragment1,
如果不想要使用默認的話,可以加上 popUpTo
來決定按下返回鍵要回到哪個 Fragment。
<action
android:id="@+id/action_Fragment1_to_Fragment2"
app:destination="@id/Fragment2"
app:popUpTo="@id/Fragment3"/>
原生的 FragmentTransaction 在切換 Fragment 有兩種方式,分別是
replace
每次切換 Fragment 時會重跑一次 onCreate
show
, hide
只顯示當前要顯示的 Fragment,並把其他的隱藏起來,不調用任何生命週期
但是 Navigation
默認都是用 replace
切換 Fragment,如果想要用 show
, hide
來切換,必須要自定義 Navigator
@Navigator.Name("custom_fragment") // Use as custom tag at navigation.xml
class CustomNavigator(
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
if (currentFragment != null) {
transaction.detach(currentFragment)
} else {
initialNavigate = true
}
var fragment = manager.findFragmentByTag(tag)
if (fragment == null) {
val className = destination.className
fragment = manager.fragmentFactory.instantiate(context.classLoader, className)
transaction.add(containerId, fragment, tag)
} else {
transaction.attach(fragment)
}
transaction.setPrimaryNavigationFragment(fragment)
transaction.setReorderingAllowed(true)
transaction.commitNow()
return if (initialNavigate) {
destination
} else {
null
}
}
}
把 fragment 標籤換成 custom_fragment
custom_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/custom_graph"
app:startDestination="@id/blankFragment1">
<custom_fragment
android:id="@+id/blankFragment1"
android:name="com.guanhong.mvvmpractice.view.navigation.BlankFragment"
android:label="fragment_blank"
tools:layout="@layout/fragment_blank">
<action
android:id="@+id/action_blankFragment1_to_blankFragment2"
app:destination="@id/blankFragment2" />
</custom_fragment>
<custom_fragment
android:id="@+id/blankFragment2"
android:name="com.guanhong.mvvmpractice.view.navigation.BlankFragment2"
android:label="BlankFragment2">
<action
android:id="@+id/action_blankFragment2_to_blankFragment1"
app:destination="@id/blankFragment1" />
</custom_fragment>
</navigation>
MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_na)
val navController = findNavController(R.id.customFragment)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.customFragment)!!
val navigator =
CustomNavigator(this, navHostFragment.childFragmentManager, R.id.customFragment)
navController.navigatorProvider.addNavigator(navigator)
navController.setGraph(R.navigation.custom_graph)
}
這裡只貼出範例,更詳細的可參考 官網
OK 今天了解了 Navigation
的切換概念、返回事件、並完成了自定義的 NavController
有任何問題或講得不清楚的地方歡迎留言和我討論。
更歡迎留言糾正我任何說錯的地方!