RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Navigation源碼解析

電子工程師 ? 來源:Android開發(fā)之旅 ? 作者:四爺 ? 2021-06-15 16:38 ? 次閱讀

Navigation源碼解析

谷歌推出Navigation主要是為了統(tǒng)一應(yīng)用內(nèi)頁面跳轉(zhuǎn)行為。本文主要是根據(jù)Navigation版本為2.1.0 的源碼進(jìn)行講解。

androidx.navigation2.1.0’ ‘a(chǎn)ndroidx.navigation2.1.0’ ‘a(chǎn)ndroidx.navigation2.1.0’ ‘a(chǎn)ndroidx.navigation2.1.0’

Navigation的使用很簡單,在創(chuàng)建新項(xiàng)目的時候可以直接選擇 Bottom Navigation Activity 項(xiàng)目,這樣默認(rèn)就已經(jīng)幫我們實(shí)現(xiàn)了相關(guān)頁面邏輯。

Navigation的源碼也很簡單,但是卻涉及到很多的類,主要有以下幾個:

Navigation提供查找NavController方法

NavHostFragment用于承載導(dǎo)航的內(nèi)容的容器

NavController通過navigate實(shí)現(xiàn)頁面的跳轉(zhuǎn)

Navigator是一個abstract,有四個主要實(shí)現(xiàn)類

NavDestination導(dǎo)航節(jié)點(diǎn)

NavGraph導(dǎo)航節(jié)點(diǎn)頁面集合

我們首先從NavHostFragment入手查看,因?yàn)樗侵苯佣x在我們的XML文件中的,我們直接查看器生命周期方法 onCreate :

@CallSuper @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Context context = requireContext();

mNavController = new NavHostController(context); //1 mNavController.setLifecycleOwner(this);

。。.。

onCreateNavController(mNavController);//2

。。.。 }

注釋1處 直接創(chuàng)建了NavHostController 并通過 findNavController 方法暴露給外部調(diào)用者。NavHostController是繼承自NavController的。注釋2處代碼如下:

@CallSuper protected void onCreateNavController(@NonNull NavController navController) { navController.getNavigatorProvider().addNavigator( new DialogFragmentNavigator(requireContext(), getChildFragmentManager())); navController.getNavigatorProvider().addNavigator(createFragmentNavigator()); }

通過navController獲取NavigatorProvider并向其中添加了兩個Navigator,分別為DialogFragmentNavigator和FragmentNavigator。另外在NavController的構(gòu)造方法中還添加了另外兩個Navigator,如下:

public NavController(@NonNull Context context) { 。。.。 mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider)); mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));}

他們都是Navigator的實(shí)現(xiàn)類。分別對應(yīng)于DialogFragment、Fragment和Activity的頁面跳轉(zhuǎn)。大家可能對于NavGraphNavigator一些好奇,它是用在什么地方的呢?其實(shí)我們在XML中配置的navGraph對應(yīng)的navigation跟節(jié)點(diǎn)文件中的 startDestination 就是通過NavGraphNavigator來實(shí)現(xiàn)跳轉(zhuǎn)的。這也是它目前唯一的用途。

各個Navigator通過復(fù)寫 navigate 方法來實(shí)現(xiàn)各自的跳轉(zhuǎn)邏輯。這里重點(diǎn)強(qiáng)調(diào)下 FragmentNavigator 的實(shí)現(xiàn)邏輯:

public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {

。。.。

final Fragment frag = instantiateFragment(mContext, mFragmentManager, className, args); frag.setArguments(args); final FragmentTransaction ft = mFragmentManager.beginTransaction();

。。.。

ft.replace(mContainerId, frag); //1

。。.。}

最關(guān)鍵的一行代碼就是注釋1 處。他是通過 replace 來加載 Fragment 的 ,這不符合我們實(shí)際的開發(fā)邏輯。文章后續(xù)會講解如何自定義 FragmentNavigator 來避免 Fragment 在切換的時候 生命周期的執(zhí)行。

回到上文中的 navController 獲取的 NavigatorProvider 其內(nèi)部是維護(hù)了一個HashMap來存儲相關(guān)的Navigator信息。通過獲取到Navigator的注解 Name 為key 和 Navigator 的 getClass為 value 進(jìn)行存儲。

我們在回到上文中的 onCreate 方法:

@CallSuper@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Context context = requireContext();

。。.。

if (mGraphId != 0) { mNavController.setGraph(mGraphId); } else {

。。.。

if (graphId != 0) { mNavController.setGraph(graphId, startDestinationArgs); } }}

這里通過 mNavController 調(diào)用了 setGraph 。這里主要是為了解析我們的 XML 中配置的mobile_navigation節(jié)點(diǎn)信息文件。會根據(jù)不同的節(jié)點(diǎn)來各自解析。

@NonNullprivate NavDestination inflate(@NonNull Resources res, @NonNull XmlResourceParser parser, @NonNull AttributeSet attrs, int graphResId) throws XmlPullParserException, IOException {

Navigator navigator = mNavigatorProvider.getNavigator(parser.getName()); final NavDestination dest = navigator.createDestination();

dest.onInflate(mContext, attrs);

。。.。

final String name = parser.getName(); if (TAG_ARGUMENT.equals(name)) { // argument 節(jié)點(diǎn) inflateArgumentForDestination(res, dest, attrs, graphResId); } else if (TAG_DEEP_LINK.equals(name)) { // deeplink 節(jié)點(diǎn) inflateDeepLink(res, dest, attrs); } else if (TAG_ACTION.equals(name)) { // action 節(jié)點(diǎn) inflateAction(res, dest, attrs, parser, graphResId); } else if (TAG_INCLUDE.equals(name) && dest instanceof NavGraph) { // include 節(jié)點(diǎn) final TypedArray a = res.obtainAttributes(attrs, R.styleable.NavInclude); final int id = a.getResourceId(R.styleable.NavInclude_graph, 0); ((NavGraph) dest).addDestination(inflate(id)); a.recycle(); } else if (dest instanceof NavGraph) { // NavGraph 節(jié)點(diǎn) ((NavGraph) dest).addDestination(inflate(res, parser, attrs, graphResId)); } }

return dest;}

通過獲取 NavInflater 來對其進(jìn)行解析。解析后返回 NavGraph ,NavGraph是繼承自 NavDestination的。里面主要是保存了所有解析出來的節(jié)點(diǎn)信息。

最后簡單的總結(jié)下就是通過 NavHostFragment 獲取到NavContorl并存儲了相關(guān)的Navigator信息。通過各自的navigate方法進(jìn)行頁面的跳轉(zhuǎn)。通過setGraph來解析配置的頁面節(jié)點(diǎn)信息,并封裝為NavGraph對象。里面通過SparseArray來存儲 Destination 信息。

自定義Navigator上文中我們說了需要自定義自己的 Navigator 用于承載 Fragment 。主要的實(shí)現(xiàn)思路就是繼承現(xiàn)有的 FragmentNavigator 并復(fù)寫其 navigate 方法,將其中的 replace 方法 替換為 show 和 hide 方法 來完成 Fragment 的切換。

那么我們自定義的 Navigator 如何才能讓系統(tǒng)識別呢?這也簡單,只要給我們的 類加上注解 @Navigator.Name(value) 那么他就是一個 Navigator 了。最后通過上文中分析的思路 在將其加入到NavigatorProvider 中 即可。

具體的自定義Navigator 已經(jīng)在項(xiàng)目 Android Jetpack架構(gòu)開發(fā)組件化應(yīng)用實(shí)戰(zhàn)(https://github.com/winlee28/Jetpack-WanAndroid) 中了,類名:FixFragmentNavigator。大家可以自行去看下。這里就將核心的代碼貼出來看下:

@Navigator.Name(“fixFragment”) //新的 Navigator 名稱class FixFragmentNavigator(context: Context, manager: FragmentManager, containerId: Int) : FragmentNavigator(context, manager, containerId) {

override fun navigate( destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras? ): NavDestination? {

。。.。

//ft.replace(mContainerId, frag)

/** * 1、先查詢當(dāng)前顯示的fragment 不為空則將其hide * 2、根據(jù)tag查詢當(dāng)前添加的fragment是否不為null,不為null則將其直接show * 3、為null則通過instantiateFragment方法創(chuàng)建fragment實(shí)例 * 4、將創(chuàng)建的實(shí)例添加在事務(wù)中 */ val fragment = mManager.primaryNavigationFragment //當(dāng)前顯示的fragment if (fragment != null) { ft.hide(fragment) }

var frag: Fragment? val tag = destination.id.toString() frag = mManager.findFragmentByTag(tag) if (frag != null) { ft.show(frag) } else { frag = instantiateFragment(mContext, mManager, className, args) frag.arguments = args ft.add(mContainerId, frag, tag) }

。。.。 }}

自定義完成好,還需要將 mobile_navigation 的節(jié)點(diǎn)中遠(yuǎn) fragment 替換為 fixFragment 節(jié)點(diǎn)。并刪除布局文件中NavHostFragment 節(jié)點(diǎn)的

app:navGraph=“@navigation/mobile_navigation”

信息,因?yàn)槲覀冃枰謩訉?FixFragmentNavigator 和 NavControl 進(jìn)行關(guān)聯(lián)。

//添加自定義的FixFragmentNavigatornavController = Navigation.findNavController(this, R.id.nav_host_fragment)val fragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragmentval fragmentNavigator = FixFragmentNavigator(this, supportFragmentManager, fragment??!.id)navController.navigatorProvider.addNavigator(fragmentNavigator)

navController.setGraph(R.navigation.mobile_navigation)

這樣就完成了自定義 Navigator 實(shí)現(xiàn)切換 Tab 的時候 Fragment 生命周期不會重新執(zhí)行了。

具體代碼邏輯詳見:Android Jetpack架構(gòu)開發(fā)組件化應(yīng)用實(shí)戰(zhàn)(https://github.com/winlee28/Jetpack-WanAndroid)

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Android
    +關(guān)注

    關(guān)注

    12

    文章

    3935

    瀏覽量

    127339
  • XML
    XML
    +關(guān)注

    關(guān)注

    0

    文章

    188

    瀏覽量

    33077
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    639

    瀏覽量

    29185

原文標(biāo)題:Navigation源碼解析及自定義FragmentNavigator詳解

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    SSM框架的源碼解析與理解

    的核心是控制反轉(zhuǎn)(IoC)和面向切面編程(AOP)。 源碼解析: Spring的源碼主要分為以下幾個部分: Bean容器: 負(fù)責(zé)實(shí)例化、配置和組裝對象。核心接口是 B
    的頭像 發(fā)表于 12-17 09:20 ?161次閱讀

    ESP-BOX 智能藥盒源碼解析(續(xù))

    藥盒基本概述請參考上一篇文章源碼解析主函數(shù)部分___main/main.c●初始化NVS:初始化非易失性存儲(NVS),如果需要擦除和重新初始化NVS,會進(jìn)行相應(yīng)處理?!駲z查百度API密鑰:調(diào)用
    的頭像 發(fā)表于 10-10 08:01 ?206次閱讀
    ESP-BOX 智能藥盒<b class='flag-5'>源碼</b><b class='flag-5'>解析</b>(續(xù))

    如何在NXP源碼基礎(chǔ)上適配ELF 1開發(fā)板的PWM功能

    本次源碼適配項(xiàng)目是在NXP i.MX6ULL EVK評估板所搭載的Linux內(nèi)核源碼(版本為Linux-imx_4.1.15)基礎(chǔ)上進(jìn)行的,主要目標(biāo)是通過調(diào)整功能接口引腳配置,使其適應(yīng)ELF 1開發(fā)板。為了深入闡述這一適配過程,我們將以PWM功能的適配作為具體示例,深入
    的頭像 發(fā)表于 09-10 10:00 ?918次閱讀
    如何在NXP<b class='flag-5'>源碼</b>基礎(chǔ)上適配ELF 1開發(fā)板的PWM功能

    ESP32 崩潰后調(diào)試信息定位到源碼方法

    arduino 通過調(diào)試信息定位出錯源碼
    的頭像 發(fā)表于 08-27 14:29 ?836次閱讀

    ElfBoard技術(shù)貼|在NXP源碼基礎(chǔ)上適配ELF 1開發(fā)板的按鍵功能

    ,將以按鍵功能的適配作為具體示例,深入解析整個適配的流程。一、準(zhǔn)備工作NXP源碼路徑:ELF1開發(fā)板資料包\07-NXP原廠資料\07-1NXP官方源碼\linux-
    的頭像 發(fā)表于 07-10 09:54 ?594次閱讀
    ElfBoard技術(shù)貼|在NXP<b class='flag-5'>源碼</b>基礎(chǔ)上適配ELF 1開發(fā)板的按鍵功能

    UCGUI單片機(jī)源碼

    UCGUI單片機(jī)源碼
    發(fā)表于 07-04 17:11 ?1次下載

    鴻蒙ArkTS聲明式組件:Navigation

    Navigation組件一般作為Page頁面的根容器,通過屬性設(shè)置來展示頁面的標(biāo)題欄、工具欄、導(dǎo)航欄等。
    的頭像 發(fā)表于 06-26 09:43 ?1470次閱讀
    鴻蒙ArkTS聲明式組件:<b class='flag-5'>Navigation</b>

    浙大博導(dǎo)開源飛控planner源碼

    浙大博導(dǎo)開源飛控planner源碼
    發(fā)表于 06-12 11:43 ?4次下載

    labview實(shí)例源碼之控壓取樣系統(tǒng)

    labview源碼,包含報表、曲線、通訊等
    發(fā)表于 06-06 11:23 ?1次下載

    什么是源碼?源碼有什么作用?源碼組件是什么?源碼可二次開發(fā)嗎?

    源碼,也稱為源程序,是指未編譯的按照一定的程序設(shè)計(jì)語言規(guī)范書寫的文本文件,是一系列人類可讀的計(jì)算機(jī)語言指令。
    的頭像 發(fā)表于 05-25 14:55 ?1.5w次閱讀
    什么是<b class='flag-5'>源碼</b>?<b class='flag-5'>源碼</b>有什么作用?<b class='flag-5'>源碼</b>組件是什么?<b class='flag-5'>源碼</b>可二次開發(fā)嗎?

    HarmonyOS實(shí)戰(zhàn)開發(fā)-如何在Navigation中完成路由攔截

    介紹 本示例介紹在Navigation中如何完成路由攔截:首次登錄時記錄登錄狀態(tài),再次登錄時可以直接訪問主頁無需重復(fù)登錄,當(dāng)退出登錄時,下次需重新登錄。 效果圖預(yù)覽 使用說明 點(diǎn)擊
    發(fā)表于 05-08 14:21

    HarmonyOS開發(fā):【基于命令行(獲取源碼)】

    在Ubuntu環(huán)境下通過以下步驟獲取OpenHarmony源碼。
    的頭像 發(fā)表于 04-25 22:08 ?399次閱讀
    HarmonyOS開發(fā):【基于命令行(獲取<b class='flag-5'>源碼</b>)】

    基于Android13的AOSP源碼下載及編譯指南

    AOSP(Android Open Source Project)是Android操作系統(tǒng)的開源項(xiàng)目,通過下載和編譯AOSP源碼,您可以獲得原始的Android系統(tǒng),并進(jìn)行定制和開發(fā)。本教程將向您介紹如何下載AOSP源碼并進(jìn)行編譯的步驟。
    的頭像 發(fā)表于 01-17 09:49 ?3925次閱讀
    基于Android13的AOSP<b class='flag-5'>源碼</b>下載及編譯指南

    Apache Doris聚合函數(shù)源碼解析

    筆者最近由于工作需要開始調(diào)研 Apache Doris,通過閱讀聚合函數(shù)代碼切入 Apache Doris 內(nèi)核,同時也秉承著開源的精神,開發(fā)了 array_agg 函數(shù)并貢獻(xiàn)給社區(qū)。筆者通過這篇文章記錄下對源碼的一些理解,同時也方便后面的新人更快速地上手源碼開發(fā)。
    的頭像 發(fā)表于 01-16 09:52 ?1006次閱讀
    Apache Doris聚合函數(shù)<b class='flag-5'>源碼</b><b class='flag-5'>解析</b>

    C#網(wǎng)絡(luò)串口調(diào)試助手源碼

    非常牛B網(wǎng)絡(luò)串口調(diào)試助手C#源碼,支持添加多條協(xié)議
    發(fā)表于 12-27 09:45 ?4次下載
    RM新时代网站-首页