動態

詳情 返回 返回

寫一個MVVM快速開發框架:談一談“單Activity+多Fragment”模式 - 動態 詳情

單Activity+多Fragment模式

自從知道這一招之後我基本不太願意使用activity了,fragment可以快速創建和管理,可以合理設計頁面跳轉,設計炫酷的跳轉動畫,一些操作可以統一進行管理。
  1. 用Fragment替代Activity

以前大部分時候都是將Activity作為頁面,Fragment作為頁面中的子頁面(當時稱之為碎片),基本上大部分功能由activity實現,比如老版本的淘寶app就是有上百個activity,當時卡頓的不要不要的。隨着技術迭代,我們發現activtiy創建、切換、銷燬所消耗的性能遠比fragment要大,fragment如今也能替代activity實現大部分功能。

  1. 將Activity作為容器

我理解的單Activity+多Fragment模式並不是指一個App一定只有一個activity,對於一些業務相關的場景,可以整合成一個單Activity+多Fragment模塊,將activity作為fragment的容器,讓fragment去做UI繪製工作。

  1. 管理Fragment棧

我們可以使用navigation管理fragment,fragment之間的跳轉、棧管理都輕而易舉,navigation還可以設置切換動畫、頁面間的數據傳遞。

Navigation組件

Navigation是Jetpack組件之一,很早之前iOS就是採用的這種跳轉方式,當時就在想Android為啥沒有,沒多久Navigation就面世了。

Navigation可以理解為以一個管理fragment的容器,在容器中各個fragment可以實現任意跳轉,

基礎使用:

  1. 我們需要在佈局中創建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"/>
  1. 創建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>
  1. 使用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_homenavi_collectionnavi_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

Add a new 評論

Some HTML is okay.