單Activity+多Fragment模式
自從知道這一招之後我基本不太願意使用activity了,fragment可以快速創建和管理,可以合理設計頁面跳轉,設計炫酷的跳轉動畫,一些操作可以統一進行管理。
- 用Fragment替代Activity
以前大部分時候都是將Activity作為頁面,Fragment作為頁面中的子頁面(當時稱之為碎片),基本上大部分功能由activity實現,比如老版本的淘寶app就是有上百個activity,當時卡頓的不要不要的。隨着技術迭代,我們發現activtiy創建、切換、銷燬所消耗的性能遠比fragment要大,fragment如今也能替代activity實現大部分功能。
- 將Activity作為容器
我理解的單Activity+多Fragment模式並不是指一個App一定只有一個activity,對於一些業務相關的場景,可以整合成一個單Activity+多Fragment模塊,將activity作為fragment的容器,讓fragment去做UI繪製工作。
- 管理Fragment棧
我們可以使用navigation管理fragment,fragment之間的跳轉、棧管理都輕而易舉,navigation還可以設置切換動畫、頁面間的數據傳遞。
Navigation組件
Navigation是Jetpack組件之一,很早之前iOS就是採用的這種跳轉方式,當時就在想Android為啥沒有,沒多久Navigation就面世了。
Navigation可以理解為以一個管理fragment的容器,在容器中各個fragment可以實現任意跳轉,
基礎使用:
- 我們需要在佈局中創建Fragment容器:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_main"/>
- 創建navigation.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/navigation_main"
app:startDestination="@+id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.example.mvvm_develop.MainFragment"
android:label="MainFragment" />
</navigation>
- 使用NavController
val navController = (childFragmentManager.findFragmentById(R.id.module_fragment_container) as NavHostFragment).navController
//跳轉
navController.navigate(R.id.mainFragment)
一些具體參數和用法:
navGraph
這個值指向xml文件,在xml文件中我們可以定義fragment,跳轉行為,目的地等。
創建、新增Fragment:
創建跳轉行為:
動畫、目的地、返回棧配置:
NavController
字面意思就是導航控制器,NavController可以控制跳轉、返回、動畫、監聽等操作。我們可以使用它進行靈活的跳轉,Google還出了一些Navigation Demo演示如何配合Toolbar和底部導航欄進行使用。
關於具體的用法這裏不講解了,很多文章都有,也可以參考官網。
Navigation存在的問題:
重走生命週期
Navigation目前有個問題:Fragment回退重走生命週期,這個問題可能是Google想讓Fragment和activity擁有同樣的工作模式,單重走生命週期真的很煩,我們可以自定義NavHostFragment去修復這個問題,具體參考項目代碼
修改之後使用如下:
android:name="androidx.navigation.fragment.NavHostFragment"
修改為我們自定義的NavHostFragment:
android:name="com.example.baselibrary.navigation.NavHostFragment"
<fragment
android:id="@+id/navigation_main"
android:name="com.example.baselibrary.navigation.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_main"/>
組件化中使用Navigation
我們通常使用底部導航欄將app劃分出不同的功能,這些都是單獨的module,但是在navigation中怎麼進行module間的跳轉呢?
比如:
其佈局文件就是一個FragmentContainerView+BottomNavigationView,切換下面按鈕的時候需要切換到不同的moduel頁面。首先我們將不同的moduel視為一個“單activity+多fragment”的模塊,或者也可以省略activity。
方式一:
google的demo中是在MainActivity中創建一個main\_navGraph,其中包含了不同子moduel的navGraph ,如下:
<?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_module"
app:startDestination="@+id/navi_home">
<include app:graph="@navigation/navi_home"/>
<include app:graph="@navigation/navi_collection"/>
<include app:graph="@navigation/navi_center"/>
</navigation>
navi_home 、navi_collection 、navi_center是子moduel中的navGraph文件,這種做法要求其指定startDestination,而且只能跳轉到startDestination
配合BottomNavigationView使用:
val navController = (childFragmentManager.findFragmentById(R.id.module_fragment_container) as NavHostFragment).navController
setupWithNavController(binding.bottomNav,navController)
這樣子確實可以實現moduel間的切換,但是我發現這種方法每次切換naviagtion都會重新初始化,導致性能消耗很大。
也許是我使用姿勢不對?
方式二:
因為app module本身是需要依賴各個子moduel的,我們可以在navGraph直接使用子moduel中的Fragment,主頁面只需加入各個module的主Fragment就行了:
<?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_module"
app:startDestination="@+id/navi_home">
<fragment
android:id="@+id/navi_home"
android:name="com.xlu.module_tab1.HomeFragment"
android:label="HomeFragment" />
<fragment
android:id="@+id/navi_collection"
android:name="com.xlu.module_collection.FragmentCollection"
android:label="CenterFragment" />
<fragment
android:id="@+id/navi_center"
android:name="com.xlu.module_center.CenterFragment"
android:label="FragmentCollection" />
</navigation>
在底部狀態切換的時候直接切換Fragment就行了:
val navController = (childFragmentManager.findFragmentById(R.id.module_fragment_container) as NavHostFragment).navController
binding.bottomNav.setOnItemSelectedListener(object :NavigationBarView.OnItemSelectedListener{
override fun onNavigationItemSelected(item: MenuItem): Boolean {
navController.navigate(item.itemId)
return true
}
})
偷懶的話可以將BottomNavigationView使用的menu中的id與navGraph中設置成一樣的啊哈哈哈哈
基本上ARouter+Navigation可以滿足大部分的頁面跳轉需求,但還是有一些難點,就是不同moduel之間的navGraph怎麼相互控制,暫時沒有想到太好的解決辦法(可以通過之前提到的想外提供接口服務實現),畢竟Navigation就沒打算為組件化準備。
相關視頻:
價值100w+Android項目實戰大全:MVVM詳解
Android(安卓)開發零基礎從入門到精通:MVVM實戰
原文鏈接: https://juejin.cn/post/6997422487654891533