Navigator

developer.android.google.cn/jetpack

navigation

在目的地之間傳遞數(shù)據(jù)

對(duì)navigation中action參數(shù)的destination,popUpTo,popUpToInclusive的一點(diǎn)筆記

 destination顧名思義是導(dǎo)航的目的地,popUpTo(tag) 跳轉(zhuǎn)到tag,并彈出tag之上的fragment,popUpToInclusive=true會(huì)彈出tag,false則不會(huì)彈出。

【Android進(jìn)階必學(xué)】JetPack指路明燈—Navigation

Jetpack Navigation工作原理

Fragment不用再每次切換是都重新創(chuàng)建了
jetpack系列之自定義FragmentNavigator


Android Navigation 如何動(dòng)態(tài)的更換StartDestination &&保存Fragment狀態(tài)


Google控件Navigation進(jìn)階使用

2.統(tǒng)一配置Animation
fun getObjectNacAction(id: Int, singleTop: Boolean = true, popid: Int? = null, clearPop: Boolean = false) =
       NavActionBuilder().apply {
           destinationId = id
           navOptions {
               if (popid != null) {
                   popUpTo(popid) {
                       inclusive = clearPop
                   }
               }
               launchSingleTop = singleTop
               anim {
                   popEnter = R.anim.slide_left_in
                   popExit = R.anim.slide_left_out
                   enter = R.anim.push_left_in
                   exit = R.anim.push_right_out
               }
           }
       }


開(kāi)始使用 Navigation



 要避免這種重復(fù)的話(huà),可以在從C 到A 的action中指定 app:popUpTo 還有 app:popUpToInclusive 屬性,如下:

<fragment
    android:id="@+id/c"
    android:name="com.example.myapplication.C"
    android:label="fragment_c"
    tools:layout="@layout/fragment_c">

    <action
        android:id="@+id/action_c_to_a"
        app:destination="@id/a"
        app:popUpTo="@+id/a"
        app:popUpToInclusive="true"/>
</fragment>


在到達(dá)了目的地 C 之后,回退棧中包含了目的地 A,B,C 的實(shí)
例.當(dāng)導(dǎo)航回目的地 A 的時(shí)候,我們同時(shí) popUpTo A ,這意味著
我們?cè)趯?dǎo)航的時(shí)候從回退棧中刪除了 B,C. 伴隨著 
app:popUpToInclusive="true" 我們同時(shí)彈出了回退棧中的第
一個(gè) A.要注意如果你沒(méi)有使用 app:popUpToInclusive="true" 
這個(gè)屬性的話(huà),你的回退棧中將會(huì)包含兩個(gè)A 的實(shí)例.


谷歌官方組件Navigation你會(huì)用了嗎?

兩種跳轉(zhuǎn)分別是傳入nav_graph.xml當(dāng)中的action id和resource id。
兩種方法都可以實(shí)現(xiàn)跳轉(zhuǎn),但是我更建議用第一種,因?yàn)榈谝环N可以配合著過(guò)渡的動(dòng)畫(huà)使用。

可以看到當(dāng)我們倒C之后,后臺(tái)堆棧當(dāng)中包括A、B、C單個(gè)實(shí)例。當(dāng)我們通過(guò)popUpTo A回到A的時(shí)候,意味著我們從堆棧當(dāng)中把B和C刪除了。當(dāng)我們使用app:popUpToInclusive =“true”的時(shí)候,我們還會(huì)把A彈出堆棧并有效的清除它。如果我們沒(méi)有使用app:popUpToInclusive =“true”那么也意味著我們的堆棧當(dāng)中包含兩個(gè)A的實(shí)例。


<action
        android:id="@+id/action_c_to_a"
        app:destination="@id/a"
        app:popUpTo="@+id/a"
        app:popUpToInclusive="true"/>



Navigation.findNavController(it).navigate(R.id.twoFragment)

對(duì)于Button控件來(lái)說(shuō),還有另一種實(shí)現(xiàn)跳轉(zhuǎn)的方法

view?.let { Navigation.findNavController(it).navigate(R.id.twoFragment)

問(wèn)題即使以前的片段名為popUpTo的根,Android導(dǎo)航框架仍會(huì)顯示后退箭頭


使用Android導(dǎo)航框架時(shí),我將IntroFragment作為根,將MainFragment作為IntroFragment的目標(biāo) . 在IntroFragment中我打電話(huà):

view.findNavController().navigate(IntroFragmentDirections.actionIntroFragmentToMainFragment())
該操作的xml是:

<action android:id="@+id/action_introFragment_to_mainFragment" app:destination="@id/mainFragment"
            app:exitAnim="@anim/slide_out_right" app:popUpTo="@+id/main" app:popUpToInclusive="true"/>
盡管有popUpToInclusive,但MainFragment工具欄中仍然會(huì)出現(xiàn)一個(gè)后退箭頭 . 我一直無(wú)法找到擺脫它的方法 . 我已經(jīng)確認(rèn)按后面的軟鍵確實(shí)會(huì)退出活動(dòng) .





Navigation問(wèn)題詳解——Fragment創(chuàng)建新的實(shí)例

···

這樣就會(huì)出現(xiàn)一個(gè)問(wèn)題,可能我們之前的OneFragment保存一些狀態(tài)或者數(shù)據(jù),當(dāng)我們跳轉(zhuǎn)OneFragment的時(shí)候,因?yàn)閯?chuàng)建新的實(shí)例導(dǎo)致我們之前保存狀態(tài)或者數(shù)據(jù)全部消失。

3.resource id替換為action id
看過(guò)我之前代碼的同學(xué),可能會(huì)注意到我特意強(qiáng)調(diào)
findNavController().navigate(R.id.action_oneFragment_to_twoFragment)

這里的id最好用action id,這一種可以配合著action里的動(dòng)畫(huà)使用,當(dāng)然也可以配合著action里的屬性popUpTo popUpToInclusive來(lái)使用。
之前有位同學(xué)在公共號(hào)給我留言,問(wèn)下了Demo之后,為什么設(shè)置 popUpTo/popUpToInclusive不起作用,問(wèn)題就在這里應(yīng)該是action Id

問(wèn)題依舊....
看似,我們?cè)诨赝说臅r(shí)候,正常了。但是,我們的問(wèn)題依舊存在,每次點(diǎn)擊的時(shí)候依然會(huì)創(chuàng)建一個(gè)新的Fragment,一個(gè)新的實(shí)例,只是我們?cè)趧?chuàng)建新的實(shí)例的時(shí)候,把之前的給清除了而已。
我會(huì)在下一篇文章介紹如何解決這個(gè)問(wèn)題!

···


Jetpack源碼解析---Navigation為什么切換Fragment會(huì)重繪?

看到這里就很清楚了吧,F(xiàn)ragment的切換是通過(guò)replace方式
來(lái)切換的,并且加入回退棧,也就是說(shuō)每次切換Fragment,都
會(huì)銷(xiāo)毀視圖和重新創(chuàng)建視圖。至于為什么用這種方式我是真
的想不到,也沒(méi)搞清楚初衷是什么?按照我們目前的開(kāi)發(fā)來(lái)
說(shuō),F(xiàn)ragment的切換通常都會(huì)使用hide()、show(),而
replcae()的方式很少用,替換會(huì)把容器中的所有內(nèi)容全都替
換掉,有一些app會(huì)使用這樣的做法,保持只有一個(gè)fragment
在顯示,減少了界面的層級(jí)關(guān)系。


不僅僅是這樣,上篇文章有小伙伴問(wèn)切換了Fragment之后,
點(diǎn)擊返回按鈕,發(fā)現(xiàn)之前的Fragment重走了onCreateView流
程,這就意味著之前的狀態(tài)沒(méi)了。對(duì)于這個(gè)問(wèn)題其實(shí)根據(jù)上
面的分析,也能大概想到是因?yàn)槭裁?,但是返回按鈕的操作
我之前還真沒(méi)有看過(guò)源碼,所以這次順便了解一下:



到這里就基本結(jié)束了,我只分析了一個(gè)大概,可以了解到點(diǎn)擊返回按鈕,同樣也會(huì)重新創(chuàng)建視圖,也就是onCreateView會(huì)重新走一遍。



.1 建議

這里我的建議是:如果你的每個(gè)Fragment真的每次都需要重新繪制的話(huà),你可以考慮使用Navigation組件來(lái)實(shí)現(xiàn),畢竟通過(guò)Navgation組件真的很方便幫助我們切換導(dǎo)航,而且雖然布局會(huì)重新繪制,但是Google的官方Demo–SunFlower還是使用了這種方式,所以這里面我覺(jué)得:官方推薦我們使用Jetpack組件中的ViewModelLiveData…等,可以發(fā)現(xiàn)SunFlowerdemo中,即便是切換Fragmengt也不會(huì)有很明顯的卡頓現(xiàn)象,因?yàn)槊總€(gè)Fragment即便重新繪制,但是View所對(duì)應(yīng)的ViewModel還在,數(shù)據(jù)并不需要重新加載或者請(qǐng)求,當(dāng)然這僅僅是我自己的看法啊.

但是如果你沒(méi)有這種場(chǎng)景的話(huà),建議還是用普通的方式我們自己來(lái)控制切換吧,這樣無(wú)論是基于Drawerlayout還是BottomNaivgationView的話(huà),我們可以自己實(shí)現(xiàn)切換。這塊我也不是很確定哈,也希望聽(tīng)取大家的意見(jiàn)和建議。

因?yàn)槊總€(gè)Fragment即便重新繪制,但是View所對(duì)應(yīng)的ViewModel還在,數(shù)據(jù)并不需要重新加載或者請(qǐng)求,


Android-Jetpack筆記-Navigation之Fragment支持復(fù)用


Jetpack路由組件學(xué)習(xí):深入理解功能強(qiáng)大的Navigation架構(gòu)之接管系統(tǒng)的返回操作




Android Navigation的四大要點(diǎn)你都知道嗎?



deepLink


popUpTo屬性表示堆棧返回到某個(gè)界面,其后的棧數(shù)據(jù)清空
//popUpToInclusive屬性為true表示回到指定界面時(shí),界面棧中是否還包括當(dāng)前界面
//(如果棧中已經(jīng)包含了指定要跳轉(zhuǎn)的界面,那么只會(huì)保留一個(gè),不指定則棧中會(huì)出現(xiàn)兩個(gè)
//界面相同的Fragment數(shù)據(jù))

a->b->c ,從c到a時(shí),指定app:popUpTo="@id/listFragment" 
,app:popUpToInclusive="true"  發(fā)現(xiàn)是新開(kāi)了一個(gè)a,之前的a 
走了,onDestroyView 和  onDestroy 方法,

如何 做到 從c到a時(shí)復(fù)用 a,也就是不銷(xiāo)毀之前的那個(gè),也不開(kāi)啟新的,直接復(fù)用老的fragment. 答案是使用popBackStack 方法,見(jiàn)下下圖:情況2

情況1

a(ListFragment),b(DetailFragment),c(EditorFragment) 三個(gè)fragment分別在 onCreate,onCreateView,onViewCreated,onDestroyView,onDestroy方法中加入日志,其中 b 啟動(dòng)c的 action中加入了 app:popUpTo="@id/listFragment" ,app:popUpToInclusive="true"

使用修復(fù)過(guò)的NavHostFragment

image.png

使用系統(tǒng)自帶的NavHostFragment

image.png

【Android進(jìn)階必學(xué)】JetPack指路明燈—Navigation

navigateUp

navigateUp與物理返回鍵的功能類(lèi)似,即返回當(dāng)前頁(yè)面堆棧的棧頂頁(yè)面,代碼如下所示。

Navigation.findNavController(it).navigateUp()
當(dāng)我們從A路由到B,B路由到C后,通過(guò)上面的代碼,使用navigateUp返回,則路由返回路徑為C到B,B到A,如果在A繼續(xù)調(diào)用navigateUp,則不會(huì)響應(yīng),因?yàn)楫?dāng)前棧中只有唯一一個(gè)頁(yè)面,而且是startDestination,所以不會(huì)再響應(yīng)返回操作。

情況2

popBackStack

navigateUp只能響應(yīng)向上一級(jí)的路由控制,而不能跨級(jí)進(jìn)行路由返回,popBackStack則是對(duì)其的補(bǔ)充,可以指定路由返回的action,代碼如下所示。
Navigation.findNavController(it).popBackStack(R.id.loginFragment, true)

當(dāng)我們從A路由到B,B路由到C后,通過(guò)popBackStack返回,指定要返回到的Fragment的id,即可直接返回到指定位置,第二個(gè)參數(shù)inclusive,代表返回操作是否包含指定的Fragment id。

這里要注意的是,當(dāng)你指定返回到A,同時(shí)inclusive為true的
時(shí)候,A也是不會(huì)被移除的,因?yàn)锳是棧頂。  

但如下圖所示,界面并沒(méi)有執(zhí)行 a 的onDestroy 方法和
oncreate 方法 ,說(shuō)明 a 并沒(méi)有銷(xiāo)毀

使用系統(tǒng)自帶的NavHostFragment

inclusive為true時(shí)


image.png

inclusive為false時(shí)


image.png

使用修改過(guò)的NavHostFragment

inclusive為true時(shí)

image.png

inclusive為false時(shí)

image.png

從驗(yàn)證結(jié)果看不管inclusive為false還是true, a 頁(yè)面始終沒(méi)有 被銷(xiāo)毀。

使用修改過(guò)的NavHostFragment和系統(tǒng)自帶的NavHostFragment我區(qū)別?

修改過(guò)的NavHostFragment 用action開(kāi)頁(yè)面時(shí)不會(huì)調(diào)用onDestroyView和onDestroy方法,而系統(tǒng)自帶的會(huì)調(diào)用。
再考慮下面這樣一個(gè)場(chǎng)景,A—B,B路由到C的時(shí)候,設(shè)置popUpTo="@id/A",如果popUpToInclusive=false,則跳轉(zhuǎn)到C之后的路由棧為A—C,如果設(shè)置為true,則只剩下A在路由棧中,代碼如下所示。

如上文字經(jīng)過(guò)驗(yàn)證發(fā)現(xiàn)是錯(cuò)的,正確的結(jié)論是:

使用修改過(guò)的NavHostFragment,popUpToInclusive=true的情況
image.png
使用修改過(guò)的NavHostFragment, popUpToInclusive=false的情況
image.png

場(chǎng)景,A—B,B路由到C的時(shí)候,設(shè)置popUpTo="@id/A",如果popUpToInclusive=false,則跳轉(zhuǎn)到C之后的路由棧為A—C,如果設(shè)置為true,則只剩下c在路由棧中,





















最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容