Android multiple back stacks導航的幾種實現

来源:https://www.cnblogs.com/mengdd/archive/2022/06/25/solutions-of-android-multi-stack-navigation.html
-Advertisement-
Play Games

Android multiple back stacks導航 談談android中多棧導航的幾種實現. 什麼是multiple stacks 當用戶在app里切換頁面時, 會需要向後回退到上一個頁面, 頁面歷史被保存在一個棧里. 在Android里我們經常說"back stack". 有時候在app ...


Android multiple back stacks導航

談談android中多棧導航的幾種實現.

什麼是multiple stacks

當用戶在app里切換頁面時, 會需要向後回退到上一個頁面, 頁面歷史被保存在一個棧里.
在Android里我們經常說"back stack".

有時候在app里我們需要維護多個back stack, 比較典型的場景是bottom navigation bar或者側邊的drawer.

如果需求要求在切換tab的時候保存每個tab上的歷史, 這樣當用戶返回的時候還是返回到上次離開的地方, 這種就叫multiple stacks.

(與之對應的single stack行為是返回之後回到了tab首頁.)

本文之後的內容都以bottom bar的多棧導航為例.

multi-stack的需求

首先還是討論一下需求.

當bottom bar不支持多棧時, 當點擊切換底部tab, 再返回原來的tab, 所有在之上打開的頁面都會消失, 只有第一層(根)頁面會顯示.

這也是可以接受的, 甚至在material design裡面作為Android平臺的預設行為被提及: material design

但它同時也說了, 如果需要的話, 這個行為是可以被改的.

如果你想保留用戶在上個tab看過的內容狀態, 很可能就需要做multi-stack, 每個tab上的棧是獨立退出, 分別保留的.

通常, 這還不是僅有的需求.

如果用戶點擊已選中的tab, 需要重置這個stack嗎?

需要定製轉場動畫嗎?

需要保留tab歷史嗎? 比如從tab A -> B -> C, 在C的根頁面back, 是想回到B還是回到home tab?

在bottom navigation的預設實現中(用Android Studio創建一個Bottom Navigation的新項目), 在非home tab的根節點, 點擊back, 總是先回到home tab, 再次back才會退出app.
因為這樣是符合固定start destination的原則的. 用戶在打開後和關閉前, 看到的是同一個頁面.

但是如果你有保存tab歷史的需求, 也可以考慮如何定製它.

當你更進一步地涉及到實現層面, 你會遇到更多實際操作的問題, 比如怎麼把一個詳情頁push到一個指定的棧, 如何pop destination.

讓我們列一下幾個需求點:

  • 維護多個棧.
  • 切換tab: 手動點擊tab或者其他tab內的交互. 比如dashboard跳轉到某個內容tab.
  • Push/pop destinations.
  • 重選(reselect)tab會重置該棧. (clear history.)
  • 轉場動畫
  • tab歷史.

技術背景

要進行導航的選型, 首先確定一下你的"destination"是什麼.

是composable還是fragment, 或者乾脆是View, 解決方案可能有很大的不同.

以這篇文章的scope來說, 我們就關註一個傳統的android app, 用Activity和Fragment實現.
所以bottom tab上的tab內容, 是不同Fragment.

Fragment lifecycle

為什麼這裡要提一下Fragment的生命周期呢?

因為fragment的生命周期和它的ViewModel緊密關聯, 進一步關係到了在導航過程中我們是否需要關註fragment的狀態恢復和刷新.

首先複習一下Fragment生命周期的回調: 什麼時候onDestroy會被調用?

  • replacetransaction沒有addToBackStack().
  • 當fragment被removed或者被popBackStack().

replacetransaction加上addToBackStack(), 舊的fragment會被壓入棧, 但它的生命周期只調用到onDestroyView().
當在它之上的其他fragment pop出來以後, 舊的這個fragment實例依然是同一個, 它重新顯示, 重新從onCreateView()開始走.

這是我們在single back stack下預期的行為.

ViewModel的生命周期和Fragment是對齊的, 也即Fragment的onDestroy()調用時, ViewModel的onCleared()被調用.

在導航切換目的地時, 如果fragment被destroy了, 我們可以保存一些關註的變數在saved instance bundle或者SavedStateHandle里, 用於之後的狀態恢復.
但是如果fragment沒有被destroy, 我們可以剩下不少力氣做這些狀態恢復.

所以理想的狀態是, 壓棧後的fragment實例不會被銷毀重建.

為了比較不同的解決方案, 我把一些sample放在了一起: https://github.com/mengdd/bottom-navigation-samples

Jetpack navigation component

官網: https://developer.android.com/guide/navigation

即便在FragmentManager的文檔 里, 也建議開發者使用jetpack的navigation library來處理app的navigation.

multiple back stack的支持是Navigation 2.4.0-alpha01Fragment 1.4.0-alpha01才加的.

試了下這個 demo,
代碼非常簡單, 我們基本什麼都不用做.

關於這裡面的思想可以看這篇文章: https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134

優點:

  • 最知名, 畢竟是官方的庫.
  • 支持類型安全的參數.
  • NavigationController支持pop到一個指定的destination.
  • 可以和Compose navigation庫一起使用.

缺點:

  • Multi-stack的支持: 當切換tab時, 前一個tab上的所有fragment都會被destroy, 當返回tab時棧內fragment會重建. 所以狀態會丟, 頁面可能會刷新.
  • 每個tab都需要是一個內嵌的navigation graph, 如果有一些common的destination, 需要include到每個graph中去. xml的navigation文件感覺很像一個大塊的樣板代碼.

FragmentManager

如果我們想做更多的定製, 我們可以考慮用FragmentManager的新APIs自己手動實現.

在文檔中doc 介紹的:

FragmentManager allows you to support multiple back stacks with the saveBackStack()
and restoreBackStack() methods. These methods allow you to swap between back stacks by saving one back stack and restoring a different one.

這是navigation component實現中實現多棧導航使用的方法.
所以也可以解釋為什麼切tab的時候fragment都被銷毀了.

saveBackStack() works similarly to calling popBackStack() with the optional name
parameter: the specified transaction and all transactions after it on the stack are popped.
The difference is that saveBackStack() saves the state of all fragments in the popped transactions.

優點:

  • 精細控制, 開發者獲得更多控制, 也更明白到底是怎麼回事.
  • 如果我們當前項目沒有採用任何navigation library, 都是手動跳轉, 採用這種方法我們就不用考慮遷移navigation.

缺點:

  • 要寫很多fragment transaction的樣板代碼.
  • 和navigation components一樣: 多棧實現中在切換棧時, 在舊的tab上的Fragments會被銷毀, 返回時全部重建.

Enro

https://github.com/isaac-udy/Enro

對於多module的大型項目來說, 我很推薦這個庫, 它可以幫助我們解耦module間的依賴.

multi-stack的demo

優點:

  • 基於註解, 所以要寫的代碼很少, 導航使用很方便.
  • 多module項目解耦.
  • 傳類型安全的參數和返回結果都很容易.
  • 可以在ViewModel中獲取navigation handle, 獲取參數.
  • 支持Compose做節點.
  • 對Unit Test也有一個輔助測試的依賴.
  • multi-stack support: 保持了切換tab的時候fragment實例.

缺點:

  • 可能目前還不是很知名. 需要說服別人學和採用這個.
  • Fragment的multi-stack: 不能rest stack到根節點. (嘗試了一下定製這個行為, 有點難).

Simple-stack

https://github.com/Zhuinden/simple-stack

這裡推薦一下這個庫作者的文章Creating a BottomNavigation Multi-Stack using child Fragments with Simple-Stack.
關於如何用simple-stack來做multi-stack.

最開始作者展示了一個不用任何庫, 僅用child fragments來實現的版本.

這是手動實現的另一種思想了.

後來才引入了用simple-stack做的demo
這是採用了原作者提供的sample, 比較簡單, 試了一下以後我發現可能還需要添加更多的代碼, 來做實際的應用.
比如詳情頁需要獲得某個tab的local stack的實例, 從而把自己push上去.

優點:

  • 作者在社區十分活躍, 有很多視頻和文章介紹simple-stack這個庫. 所以社區支持挺好.
  • multi-stack support: 保持了切換tab的時候fragment實例.
  • 支持控制和清空棧的歷史.
  • 有compose的擴展.

缺點:

  • 如果你的bottom bar當前是在activity的佈局里, 你需要把bottom bar和相關的東西都挪進一個RootFragment, 作為總的節點.
  • 作者提供的multi-stack sample還非常簡單, 需要寫更多的代碼來或者當前正確的棧來做push和pop操作. 不瞭解這個庫可能會寫得很醜.

其他庫

還有一些庫, 不是通用的navigation解決方案, 而只是為多棧導航設計的小庫.
比如:

這些庫都自帶sample.

優點:

  • 實現簡單, 只用幾個類. 如果我們想定製我們可以用這個代碼.
  • 要改動的範圍可以限制在bottom navigation的部分, 而不是整體改變navigation方案.

缺點:

  • 這些庫都不是很出名, 有不再維護的風險.
  • 可能和其他的navigation方案不能相容, 比如Navigation Components. 需要考慮整體.

總結

android (fragment實現) multi-stack navigation的可能解決方案:

方案 流行 整體方案 活躍 支持清空棧 Fragment被保存, 不被銷毀 支持Multi-modules Compose擴展
Jetpack Navigation Components 官方, 最出名 Yes Yes Yes No Yes Yes
Fragment Manager Android SDK - Yes Yes No No -
Enro Star: 188 Yes Yes No Yes Yes Yes
Simple Stack Star: 1.2k Yes Yes Yes Yes Yes Yes
Child Fragments Android SDK - Yes Yes Yes No -
JetradarMobile/android-multibackstack Star: 224 No No Yes No No -
DimaKron/Android-MultiStacks Star: 32 No Not sure Yes Yes No -

註意:

  • 整體方案: 表示該方案可以用於app整體的navigation解決方案, 而不僅僅是解決multi-stack的問題.
  • Fragment被保存, 不被銷毀: 當跳轉或者切tab時, 被壓入棧中的fragments不會被destroyed. 多棧支持的情況下, 儘管fragment被返回時都會被重建, 但是如果它不被銷毀, 我們就不需要做額外的工作來緩存狀態.

References:

作者: 聖騎士Wind
出處: 博客園: 聖騎士Wind
Github: https://github.com/mengdd
微信公眾號: 聖騎士Wind
微信公眾號: 聖騎士Wind
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、簡介 vmware為我們提供了三種網路工作模式,它們分別是:Bridged(橋接模式)、NAT(網路地址轉換模式)、Host-Only(僅主機模式)。 查看網路連接 打開vmware虛擬機,我們可以在選項欄的“編輯”->“虛擬網路編輯器”中看到VMnet0(橋接模式)、VMnet1(僅主機模式) ...
  • 安裝docker後,提示需要啟用hyper-v,在控制面板中勾選Hyper-v,然後重啟,更新快完成就提示無法完成功能配置,正在撤銷更改 解決方法 方法1 控制面板一個一個選 方法2 百度了n多內容,命令行什麼的都試了下,但是都沒有起作用.有說禁用了服務,看了下,確實是禁用了,但是開起來後還是不行. ...
  • 這玩意搞了我今天,直接裂開!系統更新根本解決不了 好在查了相關資料才知道,原來微軟在 Win10 的更新中,將搜索功能和語音助手 Cortana 進行了拆分,搜索成了一個獨立的功能,還好有外媒發現問題原因是 Bing 和 Cortana 集成造成,而且修複很簡單。 方法1 使用電腦管家修複; 方法2 ...
  • 四、Nginx 4.1、概述 4.1.1、介紹 Nginx是一款輕量級的Web伺服器/反向代理伺服器/電子右鍵(IMAP/POP3)代理伺服器。其特點是占有記憶體少,併發能力強,事實上Nginx的併發能力在同類型的網頁伺服器中表現較好,中國大陸使用Nginx的網站有:百度、就京東、新浪、網易、騰訊、淘 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 CentOS 1.備份原來的源 在控制台輸入mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup備份原本的源 2、下載新的 CentOS-Bas ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 一、環境要求 系統版本:CentOS7.x版本 硬體配置:記憶體2GB以上 cpu2核以上 硬碟大於30G 集群網路配置:集群中所有伺服器內網必須互通,並且需要訪問外網來拉取鏡像 禁用swap分區 二、k8s基礎環境操作: 1、關閉防火牆: [ro ...
  • 程式包編譯安裝的步驟: 源代碼-->預處理-->編譯-->彙編-->鏈接-->執行 多文件:文件中的代碼之間,很可能存在跨文件依賴關係 編譯源碼的項目工具 使用相關的項目管理工具可以大大減少編譯過程的複雜度 根據源碼類型來對這些工具進行分類: C、C++的源碼編譯:使用 make 項目管理器 con ...
  • 1、簡述 binlog 二進位日誌文件,這個文件記錄了MySQL所有的DML操作。通過binlog日誌我們可以做數據恢復,增量備份,主主複製和主從複製等等。 2、Docker中無法使用vim問題解決 https://blog.csdn.net/Tomwildboar/article/details/ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...