網站首頁 編程語言 正文
? ? ? ? 對于導航組件的使用方式不是本文的重點,具體使用可以參考官方文檔,導航組件框架是通過fragment來實現的,其核心類主要可以分為三個NavGraph、NavHostController、NavHostFragment,這三個類的作用分別是:
NavGraph:
解析導航圖xml獲取到的對象,其內部主要維護了一個集合用來存儲目的地,當導航到目的地時,會傳遞進來一個id,這個id可能導航圖xml中fragment的id,也有可能是fragment節點下action節點的id,如果是action節點的id,內部會轉換成fragment的id(這也就是說,action節點不加也是可以的),這樣就可以尋找到對應的fragment。
NavHostController:
導航控制的核心類,內部持有解析導航圖xml的對象,還維護了導航回退棧,管理著導航中的邏輯處理。
NavHostFragment:
導航組件的入口,主要是初始化一些相關類,最主要的是持有NavHostController,可以控制整個導航圖。
這里先看下在布局文件xml中的簡單使用:
<fragment android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@id/nav_view" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/mobile_navigation" />
這里的name屬性指定了androidx.navigation.fragment.NavHostFragment,熟悉fragment的應該知道,這里會去加載NavHostFragment,
public class NavHostFragment extends Fragment implements NavHost { private static final String KEY_GRAPH_ID = "android-support-nav:fragment:graphId"; private static final String KEY_START_DESTINATION_ARGS = "android-support-nav:fragment:startDestinationArgs"; private static final String KEY_NAV_CONTROLLER_STATE = "android-support-nav:fragment:navControllerState"; private static final String KEY_DEFAULT_NAV_HOST = "android-support-nav:fragment:defaultHost"; private NavHostController mNavController; private Boolean mIsPrimaryBeforeOnCreate = null; private View mViewParent; // State that will be saved and restored private int mGraphId; private boolean mDefaultNavHost; @CallSuper @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Context context = requireContext(); mNavController = new NavHostController(context); mNavController.setLifecycleOwner(this); mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher()); // Set the default state - this will be updated whenever // onPrimaryNavigationFragmentChanged() is called mNavController.enableOnBackPressed( mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate); mIsPrimaryBeforeOnCreate = null; mNavController.setViewModelStore(getViewModelStore()); onCreateNavController(mNavController); Bundle navState = null; if (savedInstanceState != null) { navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE); if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) { mDefaultNavHost = true; getParentFragmentManager().beginTransaction() .setPrimaryNavigationFragment(this) .commit(); } mGraphId = savedInstanceState.getInt(KEY_GRAPH_ID); } if (navState != null) { // Navigation controller state overrides arguments mNavController.restoreState(navState); } if (mGraphId != 0) { // 會去解析xml導航圖,mGraphId是從onInflate()設置進來的 mNavController.setGraph(mGraphId); } else { // See if it was set by NavHostFragment.create() final Bundle args = getArguments(); final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0; final Bundle startDestinationArgs = args != null ? args.getBundle(KEY_START_DESTINATION_ARGS) : null; if (graphId != 0) { mNavController.setGraph(graphId, startDestinationArgs); } } } /** * 創建導航控制器,在導航圖中,導航到的目的地可以是fragment、activity、dialog、子導航圖, * 導航到不同的目的地使用不同的控制器,此處提供的是dialog和fragment */ @SuppressWarnings({"WeakerAccess", "deprecation"}) @CallSuper protected void onCreateNavController(@NonNull NavController navController) { navController.getNavigatorProvider().addNavigator( new DialogFragmentNavigator(requireContext(), getChildFragmentManager())); navController.getNavigatorProvider().addNavigator(createFragmentNavigator()); } /** * 創建fragment的控制器 */ @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated @NonNull protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() { return new FragmentNavigator(requireContext(), getChildFragmentManager(), getContainerId()); } @CallSuper @Override public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs, @Nullable Bundle savedInstanceState) { super.onInflate(context, attrs, savedInstanceState); final TypedArray navHost = context.obtainStyledAttributes(attrs, androidx.navigation.R.styleable.NavHost); final int graphId = navHost.getResourceId( androidx.navigation.R.styleable.NavHost_navGraph, 0); if (graphId != 0) { mGraphId = graphId; } ... ... } }
NavHostFragment這個類代碼行數不多,這里在精簡了下,保留了幾個在初始化流程上的方法,布局中遇到fragment標簽,會先進行創建view,執行到NavHostFragment就會先執行這里的onInflate(),可以看到這里獲取到了導航圖的id,并賦值給了變量mGraphId。接著就會調用到fragment的生命周期方法,也就是這里的onCreate()方法,在這里會先初始化NavHostController對象,然后調用了onCreateNavController()方法,這個方法和NavHostController的構造函數都創建了導航控制器并添加NavigatorProvider對象中,導航到指定頁面時用到的就是這里的控制器,之后調用mNavController.setGraph(mGraphId):
public void setGraph(@NavigationRes int graphResId) { setGraph(graphResId, null); } @CallSuper public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) { setGraph(getNavInflater().inflate(graphResId), startDestinationArgs); } @CallSuper public void setGraph(@NonNull NavGraph graph, @Nullable Bundle startDestinationArgs) { if (mGraph != null) { // Pop everything from the old graph off the back stack popBackStackInternal(mGraph.getId(), true); } mGraph = graph; // 在導航圖中配置的startDestination默認顯示頁面就是在這個方法中處理的 onGraphCreated(startDestinationArgs); }
可以看到,這里對導航圖xml進行了解析,最終結果存儲在NavGraph中,這里對xml的解析類似于布局xml的解析,這里就不進去看了,感興趣的可以自己看看,在導航圖的根標簽下通常會配置startDestination屬性指定啟動的默認fragment,對這個屬性的處理就在onGraphCreate()方法中:
private void onGraphCreated(@Nullable Bundle startDestinationArgs) { ... ... if (mGraph != null && mBackStack.isEmpty()) { boolean deepLinked = !mDeepLinkHandled && mActivity != null && handleDeepLink(mActivity.getIntent()); if (!deepLinked) { // Navigate to the first destination in the graph // if we haven't deep linked to a destination navigate(mGraph, startDestinationArgs, null, null); } } else { dispatchOnDestinationChanged(); } }
這里會調用到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); ... ... }
這里先獲取到導航控制器,然后導航到對應的界面,關于導航控制器的添加,前面有說到,這里再來看下具體的添加:
public NavController(@NonNull Context context) { ... ... mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider)); mNavigatorProvider.addNavigator(new ActivityNavigator(mContext)); }
調用的是NavigatorProvider的addNavigator()方法:
private final HashMap<String, Navigator<? extends NavDestination>> mNavigators = new HashMap<>(); public final Navigator<? extends NavDestination> addNavigator( @NonNull Navigator<? extends NavDestination> navigator) { String name = getNameForNavigator(navigator.getClass()); return addNavigator(name, navigator); } @CallSuper @Nullable public Navigator<? extends NavDestination> addNavigator(@NonNull String name, @NonNull Navigator<? extends NavDestination> navigator) { if (!validateName(name)) { throw new IllegalArgumentException("navigator name cannot be an empty string"); } return mNavigators.put(name, navigator); }
這里拿到的name是導航控制器類上的注解,比如:
@Navigator.Name("navigation") public class NavGraphNavigator extends Navigator<NavGraph> { ... ... }
這里獲取到的name就是這個navigation,并以這個name為key保存對應的導航控制器,這里回到上面的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); ... ... }
?傳入的node是導航圖的根對象,node.getNavigatorName()獲取到的值是navigation,故這里獲取到的導航控制器是NavGraphNavigator,接著調用它的navigate()方法:
public NavDestination navigate(@NonNull NavGraph destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Extras navigatorExtras) { int startId = destination.getStartDestination(); ... ... NavDestination startDestination = destination.findNode(startId, false); ... ... Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator( startDestination.getNavigatorName()); return navigator.navigate(startDestination, startDestination.addInDefaultArgs(args), navOptions, navigatorExtras); }
先獲取到導航圖中配置的默認顯示視圖id,然后根據id找到對應的導航目的地,根據導航目的地獲取對應導航控制器,以如下導航圖xml為例:
<?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/mobile_navigation" app:startDestination="@id/navigation_home"> <fragment android:id="@+id/navigation_home" android:name="com.tangedegushi.jetpack_navigation.ui.home.HomeFragment" android:label="@string/title_home" tools:layout="@layout/fragment_home" /> </navigation>
startDestination.getNavigatorName()獲取到就是fragment,那對應的導航控制器是FragmentNavigator,接著調用它的navigate()方法:
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { if (mFragmentManager.isStateSaved()) { Log.i(TAG, "Ignoring navigate() call: FragmentManager has already" + " saved its state"); return null; } String className = destination.getClassName(); if (className.charAt(0) == '.') { className = mContext.getPackageName() + className; } final Fragment frag = instantiateFragment(mContext, mFragmentManager, className, args); frag.setArguments(args); final FragmentTransaction ft = mFragmentManager.beginTransaction(); int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1; int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1; int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1; int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1; if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) { enterAnim = enterAnim != -1 ? enterAnim : 0; exitAnim = exitAnim != -1 ? exitAnim : 0; popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0; popExitAnim = popExitAnim != -1 ? popExitAnim : 0; ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim); } ft.replace(mContainerId, frag); ft.setPrimaryNavigationFragment(frag); final @IdRes int destId = destination.getId(); final boolean initialNavigation = mBackStack.isEmpty(); // TODO Build first class singleTop behavior for fragments final boolean isSingleTopReplacement = navOptions != null && !initialNavigation && navOptions.shouldLaunchSingleTop() && mBackStack.peekLast() == destId; boolean isAdded; if (initialNavigation) { isAdded = true; } else if (isSingleTopReplacement) { // Single Top means we only want one instance on the back stack if (mBackStack.size() > 1) { // If the Fragment to be replaced is on the FragmentManager's // back stack, a simple replace() isn't enough so we // remove it from the back stack and put our replacement // on the back stack in its place mFragmentManager.popBackStack( generateBackStackName(mBackStack.size(), mBackStack.peekLast()), FragmentManager.POP_BACK_STACK_INCLUSIVE); ft.addToBackStack(generateBackStackName(mBackStack.size(), destId)); } isAdded = false; } else { ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId)); isAdded = true; } if (navigatorExtras instanceof Extras) { Extras extras = (Extras) navigatorExtras; for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) { ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue()); } } ft.setReorderingAllowed(true); ft.commit(); // The commit succeeded, update our view of the world if (isAdded) { mBackStack.add(destId); return destination; } else { return null; } }
這里就是對fragment的操作了,執行完成后對應的視圖也就顯示出來了,關于點擊導航的也類似,這里就不在贅述了。
原文鏈接:https://blog.csdn.net/tangedegushi/article/details/122606552
相關推薦
- 2022-06-06 PyTorch?device與cuda.device用法介紹_python
- 2022-11-08 Python?Panda中索引和選擇?series?的數據_python
- 2022-02-03 ionic 富文本編輯樣式后,前臺不能回顯樣式
- 2022-11-22 Python實例方法與類方法和靜態方法介紹與區別分析_python
- 2022-08-11 如何利用python繪制等高線圖_python
- 2024-02-25 前端顯示的日期時間與數據庫日期時間不一致
- 2023-06-04 Pandas.DataFrame時間序列數據處理的實現_python
- 2022-06-23 C#使用DLLImport調用外部DLL的方法_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支