Android Jetpack Navigation 深入体验报告
Android Jetpack 之 Navigation深入體驗報告
前言
當前Android開發中使用Fragment來開發頁面已經成為主流做法。Fragment輕量、可控性強等優點讓人感覺很香。
但是Fragment也有自己的硬傷,那就是回退棧與頁面參數傳遞。雖然當前有例如Fragmentation這樣的開源庫解決了這類問題,而且這些三方開源庫也經受住了時間與項目的檢驗。但是總讓Android開發者心中覺得少了點什么(尤其是學了iOS開發之后。。。)
Navigation的橫空出世,讓Android開發者終于看到了一線曙光。
上手接入
看到這么個好藥,能治Android開發者的心病,我就抱著試試看的態度買了兩盒,額,不對,是接入了一下。
其實接入方法非常簡單,就是一頓添加依賴。Navigation主要是有兩個庫和一個gradle插件。
// 需要添加依賴的兩個Navigation庫 implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0-beta02' implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-beta02'// 這段依賴添加在工程的build.gradle中,這個是一個gradle插件 classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-beta02'// 在app用到的module中啟用這個插件 apply plugin: 'androidx.navigation.safeargs' 復制代碼兩個依賴庫就是Navigation的主體,提供了Navigation的基礎能力。safeArgs這個插件是用來支持頁面間類型安全的參數傳遞,不是必須的,但是建議使用,因為真的很方便,這個后面會講到。
創建一個Navigation
依賴都添加好了,開搞!
Navigation還是延用了Android的XML管理思想,每一個Navigation導航邏輯對應一個xml文件(就是對應iOS的storyboard)。這個xml文件在資源目錄的navigation文件夾下(和layout、drawable是平級的)
創建方法就是右鍵,選擇new一個Android Resource File,之后的彈窗里,你會發現ResourceType的選項里,多了一個Navigation,如下圖。(如果沒有,老弟兒,你的依賴一定沒配置對,或者你還是AndroidStudio3.2吧。。。)
起個名字,創建!操作面板長成這樣(不要在意我風騷的名字)
我的需求很簡單,我有一個桌面頁(desktop),我現在想上youtube,我先要跳轉的Google,然后在通過Google上youtube去看看外面的世界,完事以后回到桌面。嗯,就這么簡單。
按照以前我們的做法,首先肯定要自己創建3個頁面的Fragment布局,然后再創建Fragment類,然后還要在Activity里面創建三個Fragment,然后通過SupportFragmentManager來添加頁面,管理頁面跳轉和回退,這個就是我們開篇時候說的痛點。
現在有了Navigation,怎么做呢?放下一前的老思想吧,不用寫代碼就能搞定。
首先,在剛才的面板中點擊添加一個destination,destination(目的地)是一個新概念,用于頁面跳轉。一個destination對應一個Fragment。例如從A跳轉B,我的destination就是B。
創建destination的過程非常人性化,填寫Fragment名字,自動創建對應的xml布局文件。我們就添加三個頁面,Desktop、Google、youtube。添加完如圖所示:
OK,我們用到的3個頁面都在這里了。我們的跳轉流程就是Desktop - > Google -> youtube -> Desktop。怎么設置呢?連線就行了!
連線完畢是這樣的,不要在意那飄逸的線條。Desktop頁面左上角有個小房子圖標,這說明這個頁面是Navigation的起始頁面。這個可以在右側編輯欄里的start destination中修改。
關聯Navigation到Activity
聯好線了,走到這一步,我們已經將頁面的跳轉關系聲明完畢。但是要提醒大家一下,我們編輯的這個Navigation是一個xml,而且是一個導航xml,并不是布局啊。怎么往頁面里添加呢?
別急,這個也很簡單。先在Activity的布局xml里添加一個fragment標簽。然后添加navi屬性,具體如下:
<fragmentandroid:id="@+id/nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:defaultNavHost="true"app:navGraph="@navigation/navi_youtube"/> 復制代碼關鍵屬性有兩個個,android:name app:navGraph
name中聲明這個Fragment是一個NavHostFragment。這個可以理解為所有導航頁面的容器。導航中聲明了各個頁面的跳轉關系,但是總要有一個容器來展示并控制它們吧?這個容器就是NavHostFragment,然后創建一個Activity,用這個Activity再包一下NavHostFragment就可以了。
navGraph屬性指定一個導航xml,就是我們剛才編輯的那個navi_pornhub。
配置完了就可以運行一下看看效果了,果然啟動后進入了Desktop頁面(這里懶得截圖了。。。)
頁面間的跳轉
看的這里可能各位會說,這玩意除了一個圖形化的編輯界面就完事了?這個我用原來的純代碼也能做啊。。。它的優勢在哪?難道只是長得好看嗎?
各位別急啊
以前頁面的跳轉怎么做?SupportFragmentManager用FragmentTransaction,自己管理,寫好多行代碼。
看看現在的新操作:
在Desktop頁面中點擊TextView跳轉Google,只需要在onClick里加入這樣一行代碼即可:
tv_desktop.setOnClickListener {findNavController().navigate(FragmentDesktopDirections.actionFragmentDesktopToFragmentGoogle()) } 復制代碼加完之后運行一下,發現真的可以跳轉了!
我們來仔細看看這行魔法代碼。首先它執行findNavController(),這個函數點進去發現它是Navigation庫給Fragment做的一個拓展。
這里有就是調用了NaviHostFragment的同名函數,這也印證了我們前文說到的NaviHostFragment的作用,它就是一個控制容器。找到NavController之后,調用了navigate(NavDirections directions)函數。這個函數從名字看出來就是做導航跳轉用的。但是新姿勢是它的參數比較特殊,是一個NavDirections實例。這個類是干什么的呢?
還記得前面介紹的Destination概念嗎?Destination表示一個跳轉目的地,一個Destination對應一個Fragment。NavDirections表示的是兩個頁面之間的跳轉關系,其實就是我們最開始畫導航頁面關系時候,連接頁面的那個箭頭。一個箭頭對應一個NavDirections實例。
那跳轉的時候如何獲取NavDirections實例呢?這里Navigation框架采用編譯器自動生成代碼的技術,對導航xml每一個Fragment,都生成了一個對應的類,名字就是類名+Directions,例如FragmentGoogleDirections。這些自動生成的類可以在工程的generatedJava路徑下看到。代碼如下:
其實就是一個Java靜態工具類。有一個action,名字就可以看出是由桌面跳轉Google,它返回一個NavDirections實例。所以在跳轉頁面時,我們直接調用這個函數獲取NavDirections實例即可。這個實例就包含了頁面跳轉信息,告訴Navigation我要從桌面跳轉的Google頁面。
這里只有一個跳轉action函數,因為桌面只有跳轉Google一個跳轉管理。如果頁面增加,桌面有多條跳轉關系,這里會生成多個action函數,函數名稱對應導航xml中每個箭頭的action名稱。
同理,把Google跳轉youtube,和youtube跳轉桌面也加上,都很簡單。
tv_google.setOnClickListener {findNavController().navigate(FragmentGoogleDirections.actionFragmentGoogleToFragmentYoutube()) }tv_youtube.setOnClickListener {findNavController().navigate(FragmentPornHubDirections.actionFragmentYoutubeToFragmentDesktop()) } 復制代碼這樣就實現了頁面間的跳轉邏輯。這種寫法首先很簡潔,思路清晰,看函數名一眼就能看出來是怎么跳轉的,而且自動生成的代碼也避免了人為犯錯。怎么樣,回想一下以前用SupportFragmentManager做跳轉的方法,是不是感覺相見恨晚?
頁面回退管理
了解了頁面跳轉,我們再來看看頁面回退。運行一下剛才的程序,發現可以無限循環跳轉,桌面到Google,Google到youtube,youtube再到桌面。。。
我們點一下返回鍵看看會發生什么。額。。。發現會一直回退,你剛才重復循環了跳轉多少次,就要回退多少次。這個說白了就是Activity的Standard啟動模式。
這個不是我們要的效果啊,我們要的是A到B,B到C,C返回A,之后頁面棧里就只有A了。不能再返回了。說白了就是singleTask啟動模式。
這個需要對跳轉的屬性進行設置,打開導航xml,選中youtube到桌面的連接箭頭。在右側的屬性編輯區里,找到紅框里的屬性,填寫好即可。
這里首先聲明了這是一個pop跳轉操作,要跳回到Desktop。然后inclusive打上對勾,這個表示調回之后,要把destination也彈出。不勾選這個,會導致返回后棧里有兩個桌面Fragment。不信可以試試哦。
之后再你的Activity里面,覆寫這個函數:
override fun onSupportNavigateUp() =findNavController(this, R.id.nav_host_fragment).navigateUp() 復制代碼覆寫這個函數就是把Activity的返回操作權利移交給Navigation框架來處理。從此不用再寫onKeyDown了。。。
頁面參數傳遞
跳轉和回退都搞定了,就要看看頁面參數傳遞了。這個體驗之后發現是真香度最高的部分。
先回顧一下老方法,老方法當然就是setArguments。因為Fragment不支持你在構造函數中加入參數(當然你要硬加也行,各種報警告,賊難受),所以只能通過setArguments來搞,不管你怎么封裝,都需要兩步:創建和設置參數。怎么都不快樂。
現在來Navigation吧,這個新操作絕對眼前一亮。
需求:我想在桌面跳轉Google時,給頁面傳遞一個搜索關鍵詞youtube,并且彈一個Toast。下面是做法:
首先在導航xml里,選中GoogleFragment,在右面的操作面板里,點擊添加arguments,添加一個字符串參數,叫keyword。不可空,沒有默認值。點擊確認添加。
之后可以看見GoogleFragment里面已經有一個叫keyword的參數了。
之后點擊桌面到Google的箭頭(還記得箭頭其實就是一個NavDirections吧),你會發現這個Direction的屬性里也有了一個參數,其實這個就是一個自動化的對應機制,因為GoogleFragment有一個參數,所以所有跳轉到這個頁面的Direction里都應該有對應的參數。
參數添加好以后,修改一下跳轉的那一行魔法代碼。
tv_desktop.setOnClickListener {findNavController().navigate(FragmentDesktopDirections.actionFragmentDesktopToFragmentGoogle("youtube")) } 復制代碼這里在調用action函數獲取NavDirections實例的時候,需要傳一個字符串,就是keywork的值。同理如果有多個參數這里就傳多個值。
OK,啟動方的參數傳遞已經完成了。我們來看看接收方。
在GoogleFragment中如何獲取到keyword的值呢?其實Navigation庫已經幫你把參數放到arguments里面了,你直接取就行了。例如這樣:
val keyword = arguments?.getString("keyword") ?: "" 復制代碼直接這么調用就能拿到傳遞過來的值了。但是感覺不是很香啊。那是因為你沒有使用safeArgs插件!來看一下這個新操作。
val safeArgs = FragmentGoogleArgs.fromBundle(arguments!!) val keyword = safeArgs.keyword 復制代碼這里又用到了自動生成代碼的技術,對于每一個有參數的Fragment,都生成一個類名+Args的類。調用第一行代碼,就能得到一個safeArgs實例。之后需要用參數的時候,直接safeArgs.參數名就能拿到參數的值。
各位老鐵,感覺出safeArgs的好處了嗎?首先它是類型安全的,可以直接點出來,不用打問號,舒服了很多。最重要的是,老方法是getString("變量名")這個變量名是手打的,safeArgs可以直接點出變量名來取值,方便還不容易出錯。
體驗總結
- 真香
- 目前還是beta版本,還不建議在實際項目中使用
- 會持續關注后續消息,期待穩定版本
-- 2019.03.18更新,已發布1.0.0正式版
轉載于:https://juejin.im/post/5c8f283f6fb9a0710466981a
總結
以上是生活随笔為你收集整理的Android Jetpack Navigation 深入体验报告的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode1011
- 下一篇: 高级编程学习笔记day01(知识点篇)