本篇是有關(guān) Navigation 的第二篇,如有對(duì) Navigation 不了解的朋友請(qǐng)先閱讀來學(xué)一波 Navigation婚夫。
多次執(zhí)行 onCreateView
在上一篇中营密,我們利用 Navigation 與 BottomNavigationView 做出了一個(gè)有三個(gè) Tab 的頁面划址,分別是 Feed劲妙、Timer呢岗、Mine冕香,這三個(gè) Fragment 都是只在當(dāng)前頁面顯示各自的名稱蛹尝。
現(xiàn)在我們來給 TimerFragment 加點(diǎn)內(nèi)容,我們?cè)?TimerFragment 的 onCreateView 方法中啟動(dòng)一個(gè)倒計(jì)時(shí)悉尾。
private void startTimer() {
new CountDownTimer(10 * 1000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
tvLabel.setText(String.valueOf((millisUntilFinished / 1000) + 1));
}
@Override
public void onFinish() {
tvLabel.setText("Finished");
}
}.start();
}
<img src="https://monster-image-backup.oss-cn-shanghai.aliyuncs.com/picgo/blog/blog_nav_tab.gif" style="zoom:33%;" />
仔細(xì)看上面的效果可以看到突那,每次切換到 TimerFragment 時(shí),倒計(jì)時(shí)總會(huì)重新開始构眯,不是我們想要的僅開始一次愕难。這是什么問題導(dǎo)致的呢?答案是 TimerFragment 執(zhí)行了多次的 onCreateView惫霸,為什么是會(huì)執(zhí)行多次猫缭,F(xiàn)ragment 為什么會(huì)加載多次?我們沒有什么特殊的操作呀壹店。是不是因?yàn)?Navigation猜丹?
現(xiàn)在讓我們深入到 Navigation 的源碼看一看這到底是怎么一回事,以及我們?cè)撊绾谓鉀Q這一問題硅卢。
首先射窒,我們需要明確我們的方向,就是 Navigation 到底是怎么做 Fragment 切換的将塑,為什么會(huì)導(dǎo)致 Fragment 的 onCreateView 被多次執(zhí)行轮洋。
從哪里作為入口呢?了解過 Navigation 的朋友對(duì)下面這行代碼應(yīng)該不會(huì)陌生抬旺,就是通過一個(gè) View 獲取到 NavController弊予,然后通過執(zhí)行 NavController 的 navigate 這個(gè)方法,我們就從這個(gè)方法開始开财。
Navigation.findNavController(view)
.navigate(id);
這個(gè) navigate 有多個(gè)重載方法汉柒,我們開始的 navigate 方法最終也是執(zhí)行到下面這個(gè)重載方法。
navigate(NavDestination node, Bundle args, NavOptions navOptions, Navigator.Extras navigatorExtras)
方法的具體內(nèi)容如下圖:
其中在第 9 行责鳍,我們可以看到通過 mNavigatorProvider 獲取到了一個(gè)泛型類型為 NavDestination 的 Navigator 對(duì)象碾褂,并且在第 12 行時(shí),通過調(diào)用剛獲取到的 navigator 的 navigate 方法历葛,得到了 NavDestination 這個(gè)對(duì)象正塌。
這兩行是關(guān)鍵代碼,一個(gè)是獲取到執(zhí)行 navigator 的對(duì)象恤溶,一個(gè)是實(shí)際執(zhí)行 navigate 的方法乓诽。看到這咒程,我們就只需要找到 Navigator 的 navigate 方法即可鸠天。不過,Navigator 這個(gè)只是一個(gè)抽象類帐姻,我們還需要繼續(xù)尋找它的實(shí)現(xiàn)類稠集。
快捷鍵:Implementation(s) Mac: option(?) + command(?)+B
Navigator 抽象類的關(guān)鍵代碼:
public abstract class Navigator<D extends NavDestination> {
@Retention(RUNTIME)
@Target({TYPE})
@SuppressWarnings("UnknownNullness")
public @interface Name {
String value();
}
@NonNull
public abstract D createDestination();
@Nullable
public abstract NavDestination navigate(@NonNull D destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Extras navigatorExtras);
public abstract boolean popBackStack();
@Nullable
public Bundle onSaveState() {
return null;
}
public void onRestoreState(@NonNull Bundle savedState) {
}
public interface Extras {
}
}
通過快捷鍵我們能找到多個(gè)實(shí)現(xiàn)類奶段,有 ActivityNavigator、DialogFragmentNavigator 還有 FragmentNavigator 等剥纷,這里我們只關(guān)注 FragmentNavigator 這個(gè)類中的 navigate 這個(gè)方法痹籍。
別看這么多代碼,別害怕晦鞋,其實(shí)關(guān)鍵部分的代碼就是第 32 行 ft.replace(mContainerId, frag)
這里使用的是 FragmentTransaction 的 replace 方法词裤,這個(gè)方法不用說了吧。 replace 是移除了相同 id 的 fragment 然后再進(jìn)行 add 的鳖宾。
所以吼砂,看到這,我們也就知道了鼎文,為什么 TimerFragment 的 onCreateView 方法會(huì)被執(zhí)行多次了渔肩,原因就是在這。
規(guī)避 replace
找到原因了拇惋,那我們有什么方法去規(guī)避周偎,或者說去繞過這個(gè) replace 嗎?答案是有的撑帖。
還記得剛才我們找的下面這行代碼吧(忘記的蓉坎,請(qǐng)看第一張代碼圖的第 9 行),剛才我說胡嘿,通過 mNavigatorProvider 找到一個(gè)泛型類型為 NavDestination 的 Navigator 對(duì)象蛉艾,那它實(shí)際上是怎么找到的呢?是通過 node.getNavigatorName() 然后找的衷敌,這個(gè) node 是什么東西勿侯?以及 mNavigatorProvider.getNavigator 內(nèi)部究竟發(fā)生了什么?
Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
node.getNavigatorName());
實(shí)際上這里的 node 就是一個(gè) NavDestination 對(duì)象缴罗,而一個(gè) NavDestination 對(duì)象就是對(duì)應(yīng)著 navigation graph 中的節(jié)點(diǎn)信息助琐。我用來演示的 Demo 的 navigation graph 文件如下:
<?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/tab_navigation"
app:startDestination="@id/feedFragment">
<fragment
android:id="@+id/feedFragment"
android:name="me.monster.blogtest.tab.FeedFragment"
android:label="fragment_feed"
tools:layout="@layout/fragment_feed" />
<fragment
android:id="@+id/timerFragment"
android:name="me.monster.blogtest.tab.TimerFragment"
android:label="fragment_timer"
tools:layout="@layout/fragment_timer" />
<fragment
android:id="@+id/mineFragment"
android:name="me.monster.blogtest.tab.MineFragment"
android:label="fragment_mine"
tools:layout="@layout/fragment_mine" />
</navigation>
node.getNavigatorName 返回的就是 fragment 節(jié)點(diǎn)的節(jié)點(diǎn)名稱 fragment
,而 getNavigator 其實(shí)內(nèi)部就是維護(hù)了一個(gè)類型為 HashMap 的 mNavigators面氓,這個(gè) HashMap 存的 key 就是節(jié)點(diǎn)名稱兵钮,value 就是抽象類 Navigator 的實(shí)現(xiàn)類。而與 fragment 對(duì)應(yīng)的 FragmentNavigator 也存儲(chǔ)在其中舌界。
既然是存在一個(gè) map掘譬,并從中取出相對(duì)于的 Navigator 實(shí)現(xiàn)類,那我們能不能創(chuàng)建一個(gè)類并實(shí)現(xiàn) Navigator禀横,然后將 key屁药、value 添加到那個(gè) HashMap 中粥血。答案是可行的柏锄。在NavigatorProvider 這個(gè)類中有兩個(gè)公共方法:
- addNavigator(Navigator navigator)
- addNavigator(String name, Navigator navigator)
其中酿箭,一個(gè)參數(shù)的 addNavigator 也是調(diào)用了 兩個(gè)參數(shù)的 addNavigator 方法,那個(gè) name 也就是 navigation graph 中 fragment 節(jié)點(diǎn)的節(jié)點(diǎn)名稱趾娃,同時(shí)也是 Navigator 這個(gè)抽象類中注解 Name
定義的值缭嫡。而且在 NavController 這個(gè)類(最初我們找到的 navigate 所在的類)中有一個(gè) getNavigatorProvider() 方法。
看到這抬闷,關(guān)系應(yīng)該就比較清楚了妇蛀。所以,我們需要自己創(chuàng)建一個(gè)類笤成,實(shí)現(xiàn) Navigator 并為 Name 注解添加一個(gè)值评架,然后在使用 Navigation 這個(gè)模塊的 Activity 獲取到 NavController 并調(diào)用其 getNavigatorProvider 方法后再調(diào)用 addNavigator 即可。
自定義 Navigator
Github 上已經(jīng)有一個(gè)演示自定義實(shí)現(xiàn) Navigator 的項(xiàng)目了炕泳。這個(gè)項(xiàng)目是以 Kotlin 語言編寫的纵诞。
項(xiàng)目地址: https://github.com/STAR-ZERO/navigation-keep-fragment-sample。
說起來這個(gè)項(xiàng)目還是 Drakeet 在他的知識(shí)星球中分享的培遵。感謝 Drakeet 的分享浙芙。
我根據(jù)按照他的代碼寫了一份 Java 版本的,并且在其中改了兩行代碼(注釋部分)籽腕。注釋的內(nèi)容其實(shí)就是使用 FragmentTranslation 對(duì) Fragment 進(jìn)行控制嗡呼。原作者寫的是 detach 與 attach 方法,我改成了使用 hide 和 show 方法皇耗。
@Navigator.Name("keep_state_fragment")
public class KeepStateNavigator extends FragmentNavigator {
private Context context;
private FragmentManager manager;
private int containerId;
public KeepStateNavigator(@NonNull Context context, @NonNull FragmentManager manager, int containerId) {
super(context, manager, containerId);
this.context = context;
this.manager = manager;
this.containerId = containerId;
}
@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
String tag = String.valueOf(destination.getId());
FragmentTransaction transaction = manager.beginTransaction();
boolean initialNavigate = false;
Fragment currentFragment = manager.getPrimaryNavigationFragment();
if (currentFragment != null) {
// transaction.detach(currentFragment);
transaction.hide(currentFragment);
} else {
initialNavigate = true;
}
Fragment fragment = manager.findFragmentByTag(tag);
if (fragment == null) {
String className = destination.getClassName();
fragment = manager.getFragmentFactory().instantiate(context.getClassLoader(), className);
transaction.add(containerId, fragment, tag);
} else {
// transaction.attach(fragment);
transaction.show(fragment);
}
transaction.setPrimaryNavigationFragment(fragment);
transaction.setReorderingAllowed(true);
transaction.commitNow();
return initialNavigate ? destination : null;
}
}
這里的代碼沒有傳遞 Bundle 類型的 args 同時(shí)也破壞了在 navigation graph 中切換動(dòng)畫的設(shè)置南窗,如需要,自行加上即可郎楼》可參考 FragmentNavigator 類中實(shí)現(xiàn)。
感謝 Qwer 提出問題
注意箭启,使用自定義 Navigator 的時(shí)候 navigation graph 需要把 fragment 節(jié)點(diǎn)名稱改為 keep_state_fragment壕翩,并且在承載的 Activity 中進(jìn)行設(shè)置并且還需要把 Activity 布局文件中 fragment 的 navGraph 屬性移除。
NavController navController = Navigation.findNavController(this, R.id.fragment3);
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment3);
KeepStateNavigator navigator = new KeepStateNavigator(this, navHostFragment.getChildFragmentManager(), R.id.fragment3);
navController.getNavigatorProvider().addNavigator(navigator);
navController.setGraph(R.navigation.tab_navigation);
最后來看一下使用自定義 Navigator 時(shí)的 TabActivity傅寡。
<img src="https://monster-image-backup.oss-cn-shanghai.aliyuncs.com/picgo/blog/blog_nav_tab_state.gif" style="zoom: 33%;" />
這樣好像看起來結(jié)束了放妈?其實(shí)并沒有,我們只是剛剛開始荐操。
首先芜抒,我先更正一下,在第一篇關(guān)于 Navigation 的博客中從 SettingFragment 返回到 RootFragment 那一段代碼有些問題托启。
沒看過那篇文章的不要著急宅倒,其實(shí)就是 A 調(diào)到 B,然后在 B 中觸發(fā)一個(gè)點(diǎn)擊事件屯耸,再從 B 返回到 A拐迁。返回的代碼如下蹭劈。
原代碼為:
btnToRoot.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.findNavController(btnToRoot)
.popBackStack();
}
});
這里在點(diǎn)擊事件中,最后執(zhí)行的是 popBackStack
线召,其實(shí)不應(yīng)該調(diào)用這個(gè)方法應(yīng)該用 navigateUp
這個(gè)方法铺韧。
在第一篇博客中,Navigation Graph 中所有的節(jié)點(diǎn)名稱都是 Fragment缓淹,如果我用上面這種 keep_state_fragment 的方式哈打,會(huì)發(fā)生什么呢?
<img src="https://i.loli.net/2019/10/21/BAC6leVN1WhdaKJ.gif" style="zoom: 50%;" />
可以看到讯壶,在把 Navigation Graph 節(jié)點(diǎn)名替換為 keep_state_fragment 后料仗,在 SettingFragment 點(diǎn)擊返回并沒有進(jìn)行返回。這是為什么呢伏蚊?我沒干啥呀罢维,怎么不好使了。
不行丙挽,我要看看 Navigation 源碼里面到底怎么做的肺孵。于是我開始了 debug 之旅。后來颜阐,我發(fā)現(xiàn)在 Navigation.findNavController(btnToRoot).navigateUp();
內(nèi)部判斷了當(dāng)前的返回棧個(gè)數(shù)是否為 1平窘,結(jié)果讓我很震驚,返回的竟然真的是 1凳怨。所以瑰艘,navigateUp 就理所當(dāng)然的返回 false,也就沒能從 SettingFragment 回到 RootFragment 了肤舞。
下面的兩段代碼分別是:NavController#navigateUp 和 NavController#getDestinationCountOnBackStack
private int getDestinationCountOnBackStack() {
int count = 0;
for (NavBackStackEntry entry : mBackStack) {
if (!(entry.getDestination() instanceof NavGraph)) {
count++;
}
}
return count;
}
我查了一下 mBackStack 這個(gè)數(shù)據(jù)類型紫新,發(fā)現(xiàn)它是一個(gè)棧,緊接著找到 mBackStack 入棧的方法李剖。
mBackStack#add 相關(guān)的方法一共有 4 個(gè)芒率,第一個(gè)方法是在 NavController#NavController 方法中進(jìn)行調(diào)用的,其余 3 個(gè) add 相關(guān)方法均是在 NavController#navigate 內(nèi)調(diào)用篙顺,而調(diào)用 add 方法之外有一個(gè)判空偶芍。判空的對(duì)象就是來自 Navigator#navigate 這個(gè)方法。
NavController 的 navigate 方法德玫,有刪減匪蟀。
private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
//......
Navigator<NavDestination> navigator = mNavigatorProvider
.getNavigator(node.getNavigatorName());
Bundle finalArgs = node.addInDefaultArgs(args);
NavDestination newDest = navigator.navigate(node, finalArgs,
navOptions, navigatorExtras);
if (newDest != null) {
// The mGraph should always be on the back stack after you navigate()
if (mBackStack.isEmpty()) {
mBackStack.add(new NavBackStackEntry(mGraph, finalArgs));
}
// Now ensure all intermediate NavGraphs are put on the back stack
// to ensure that global actions work.
ArrayDeque<NavBackStackEntry> hierarchy = new ArrayDeque<>();
NavDestination destination = newDest;
while (destination != null && findDestination(destination.getId()) == null) {
NavGraph parent = destination.getParent();
if (parent != null) {
hierarchy.addFirst(new NavBackStackEntry(parent, finalArgs));
}
destination = parent;
}
mBackStack.addAll(hierarchy);
// And finally, add the new destination with its default args
NavBackStackEntry newBackStackEntry = new NavBackStackEntry(newDest,
newDest.addInDefaultArgs(finalArgs));
mBackStack.add(newBackStackEntry);
}
//......
}
根據(jù)我們之前的經(jīng)驗(yàn),可以得出這里的 Navigator 就是我們自定義的 KeepStateNavigator 這個(gè)對(duì)象宰僧,那 navgate 這個(gè)方法的返回值也就是我們自己控制的材彪,也就是我們自己給自己挖了個(gè)坑。2333~
來吧,來看一下剛才寫的代碼段化。
public NavDestination navigate(Destination destination, Bundle args, NavOptions navOptions, Navigator.Extras navigatorExtras) {
String tag = String.valueOf(destination.getId());
FragmentTransaction transaction = manager.beginTransaction();
boolean initialNavigate = false;
Fragment currentFragment = manager.getPrimaryNavigationFragment();
if (currentFragment != null) {
transaction.hide(currentFragment);
} else {
initialNavigate = true;
}
Fragment fragment = manager.findFragmentByTag(tag);
if (fragment == null) {
String className = destination.getClassName();
fragment = manager.getFragmentFactory().instantiate(context.getClassLoader(), className);
transaction.add(containerId, fragment, tag);
} else {
transaction.show(fragment);
}
transaction.setPrimaryNavigationFragment(fragment);
transaction.setReorderingAllowed(true);
transaction.commitNow();
return initialNavigate ? destination : null;
}
在最后一行中嘁捷,我們通過對(duì) initialNavigate
進(jìn)行判斷然后返回 null 或是 destination 對(duì)象。而把 initialNavigate 賦值為 true 則是只有在 currentFragment 為空時(shí)才會(huì)進(jìn)行穗泵,什么時(shí)候 currentFragment 才會(huì)為空普气?只有當(dāng)打開一個(gè) Activity 并為其填充第一個(gè) Fragment 時(shí)才會(huì)為 true谜疤,在我們當(dāng)前這個(gè)場景里佃延,就是當(dāng)應(yīng)用啟動(dòng),打開 RootFragment 時(shí) initialNavigate 為 true夷磕,從 RootFragment 跳轉(zhuǎn)到 SettingFragment 時(shí) initialNavigate 為 false履肃。
這顯然是有問題的,那么我們需要改為坐桩,當(dāng)這個(gè) fragmen 為空時(shí)尺棋,在 transaction.add(containerId, fragment, tag);
之后把 initialNavigate 賦值為 true。這樣一來绵跷,NavController#getDestinationCountOnBackStack 就能獲取到實(shí)際的 fragment 大小了膘螟,也就不會(huì)直接 return fase 了。
運(yùn)行一下看看結(jié)果碾局?別著急啊荆残,再檢查檢查。剛才我說在 Navigation.findNavController(btnToRoot).navigateUp();
內(nèi)部判斷了當(dāng)前的返回棧個(gè)數(shù)是否為 1净当,現(xiàn)在我們把為 1 的情況解決了内斯,那么當(dāng)返回棧的個(gè)數(shù)不為 1 時(shí)它怎么做的?在判斷返回棧個(gè)數(shù)不是 1 的后經(jīng)過內(nèi)部調(diào)用像啼,最終來到了 NavController#popBackStackInternal
這個(gè)方法內(nèi)俘闯。
NavController 的 popBackStackInternal 方法,有刪減
boolean popBackStackInternal(@IdRes int destinationId, boolean inclusive) {
if (mBackStack.isEmpty()) {
// Nothing to pop if the back stack is empty
return false;
}
ArrayList<Navigator> popOperations = new ArrayList<>();
Iterator<NavBackStackEntry> iterator = mBackStack.descendingIterator();
boolean foundDestination = false;
while (iterator.hasNext()) {
NavDestination destination = iterator.next().getDestination();
Navigator navigator = mNavigatorProvider.getNavigator(
destination.getNavigatorName());
if (inclusive || destination.getId() != destinationId) {
popOperations.add(navigator);
}
//......
boolean popped = false;
for (Navigator navigator : popOperations) {
if (navigator.popBackStack()) {
NavBackStackEntry entry = mBackStack.removeLast();
popped = true;
// ......
return popped;
}
在這個(gè)方法內(nèi)忽冻,我又看到了那個(gè)熟悉的面孔 navigator真朗,在這里 navigator 執(zhí)行了一個(gè)叫 popBackStack 的方法,這個(gè)方法看起來好像就是做返回事件的僧诚∶刍可是,我們的 KeepStateNavigator 并沒有這個(gè)方法啊振诬,那是因?yàn)槲覀冞x擇了繼承自 FragmentNavigator蹭睡,在 FragmentNavigator 有一套 popBackStack 邏輯,不過我們用不了赶么。所以我們需要在 FragmentNavigator 進(jìn)行重寫這個(gè)方法肩豁。
由于我們需要返回到上一個(gè)頁面,所以我們也得有個(gè)管理?xiàng)#缓笤?KeepStateNavigator#navigate 方法中的 transaction.add(containerId, fragment, tag);
之后把當(dāng)前 Fragment 添加到返回棧中清钥,在 popBackStack 中根據(jù)一些條件再進(jìn)行 remove 即可琼锋。
這樣一來就可以了。
<img src="https://i.loli.net/2019/10/17/qn84IfTF7ybrViS.gif" style="zoom:50%;" />
不知道為什么祟昭,錄制的 gif 畫面一直在閃……
隨意切換
好了缕坎,這樣就可以了,終于可以愉快的使用 Navigation 了篡悟。直到有一天谜叹,老大找到我,跟我說了一個(gè)需求搬葬。
從 A 頁面進(jìn)入 B 頁面荷腊,再從 B 頁面進(jìn)入 C 頁面,在 C 頁面產(chǎn)生一個(gè)事件急凰,然后用戶返回時(shí)女仰,需要跳過 B,也就是從 C 直接回到 A抡锈。
問我這個(gè)能不能在 Navigation 上實(shí)現(xiàn)疾忍,我想了一下,說可以床三。下面就分享一下實(shí)現(xiàn)這種效果的思路一罩,我個(gè)人覺得可以有兩個(gè)解決方法,下面我依次來說一下勿璃。
假設(shè)頁面打開順序?yàn)椋篈擒抛、B、C补疑。
-
第一種:優(yōu)先關(guān)閉
當(dāng)從 C 返回到 A 時(shí)歧沪,其實(shí)并不一定是返回的時(shí)候進(jìn)行操作,可能是在某個(gè)事件產(chǎn)生之后莲组,這時(shí)就把 B 給關(guān)閉诊胞,此時(shí)回退棧里面也就只剩下 A 和 B。這時(shí)候只需要正常走頁面返回邏輯即可锹杈。
-
第二種:優(yōu)先返回
當(dāng)從 C 返回到 A 時(shí)撵孤,也可以直接跳過 B,具體方法為:當(dāng)從 C 點(diǎn)擊返回時(shí)竭望,觸發(fā)返回棧的操作邪码,當(dāng)完成返回操作后發(fā)現(xiàn)當(dāng)前頁面需要跳過時(shí),則繼續(xù)返回咬清,此時(shí)也就回到了 A闭专。
落實(shí)到 Navigation 中奴潘,就是在自定義 Navigator 中添加一些方法,然后在需要執(zhí)行此類操作的地方獲取到 Navigator 對(duì)象影钉,并進(jìn)行相關(guān)操作画髓。
獲取 Navigator 的相關(guān)代碼如下:
NavController navController = Navigation.findNavController(btnToRoot);
NavigatorProvider navigatorProvider = navController.getNavigatorProvider();
Navigator<?> navigator = navigatorProvider.getNavigator("keep_state_fragment");
if (navigator instanceof KeepStateNavigator) {
((KeepStateNavigator) navigator).closeMiddle(R.id.settingsFragment);
}
思考
我第一次學(xué)習(xí) Navigation 的時(shí)候就瞄了一眼,覺得這不就是個(gè) Fragment 的管理框架嗎平委?有什么的呀奈虾,比其他Fragment 的管理框架好嗎?看起來一般般啊廉赔。哇哦肉微,好復(fù)雜啊,算了昂勉,不看了浪册,知道大致怎么用就行扫腺「谡眨看著大家討論的越來越多,沒忍住笆环,又去仔細(xì)看了一下 Navigation 的使用攒至,以及稍微閱讀了源碼。不就是個(gè) Fragment 管理框架嗎躁劣?怎么搞這么復(fù)雜迫吐,什么 NavigatorProvider、Navigator账忘、Destination 這些都是啥啊志膀。
隨著我看的內(nèi)容越來越多,實(shí)踐的也變多了鳖擒,原生的 Navigation 越來不不能滿足需求了溉浙,才發(fā)現(xiàn),原來 Google 早就想到了蒋荚,只是沒有給我們提供具體的解決方法戳稽,只是把一些東西開放出來供開發(fā)者在不同場景下進(jìn)行自定義使用。
想想自己項(xiàng)目里的代碼期升,好像如果要擴(kuò)展的話惊奇,就得改動(dòng)較多原來的代碼,不能像 Navigation 這樣播赁,在需要改動(dòng)的時(shí)候颂郎,盡量不觸動(dòng)原有代碼,而通過接口容为、Provider乓序、泛型等更多是編碼技巧或是設(shè)計(jì)模式上的技巧來完成業(yè)務(wù)需求诞吱。
也不應(yīng)該把過多的心思花在各式各樣的第三方庫上,而是把更多的精力花在基礎(chǔ)技能上竭缝,雖然可能一時(shí)半會(huì)兒看不出什么結(jié)果房维,但這可能是笑到最后的方法。
本文首發(fā)于個(gè)人博客抬纸,文中全部源代碼已上傳至 GitHub咙俩,代碼分支為 closeBefore。喜歡本文的麻煩點(diǎn)個(gè)??湿故。
本文封面圖:Photo by Jo?o Silas on Unsplash