欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android中導(dǎo)航組件Navigation的實現(xiàn)原理

 更新時間:2022年02月08日 09:55:54   作者:tangedegushi  
大家好,本篇文章主要講的是Android中導(dǎo)航組件Navigation的實現(xiàn)原理,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下

        對于導(dǎo)航組件的使用方式不是本文的重點,具體使用可以參考官方文檔,導(dǎo)航組件框架是通過fragment來實現(xiàn)的,其核心類主要可以分為三個NavGraph、NavHostController、NavHostFragment,這三個類的作用分別是:

NavGraph:

解析導(dǎo)航圖xml獲取到的對象,其內(nèi)部主要維護了一個集合用來存儲目的地,當(dāng)導(dǎo)航到目的地時,會傳遞進來一個id,這個id可能導(dǎo)航圖xml中fragment的id,也有可能是fragment節(jié)點下action節(jié)點的id,如果是action節(jié)點的id,內(nèi)部會轉(zhuǎn)換成fragment的id(這也就是說,action節(jié)點不加也是可以的),這樣就可以尋找到對應(yīng)的fragment。

NavHostController:

導(dǎo)航控制的核心類,內(nèi)部持有解析導(dǎo)航圖xml的對象,還維護了導(dǎo)航回退棧,管理著導(dǎo)航中的邏輯處理。

NavHostFragment:

導(dǎo)航組件的入口,主要是初始化一些相關(guān)類,最主要的是持有NavHostController,可以控制整個導(dǎo)航圖。

這里先看下在布局文件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的應(yīng)該知道,這里會去加載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導(dǎo)航圖,mGraphId是從onInflate()設(shè)置進來的
            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);
            }
        }
    }
 
    /**
     * 創(chuàng)建導(dǎo)航控制器,在導(dǎo)航圖中,導(dǎo)航到的目的地可以是fragment、activity、dialog、子導(dǎo)航圖,
     * 導(dǎo)航到不同的目的地使用不同的控制器,此處提供的是dialog和fragment
     */
    @SuppressWarnings({"WeakerAccess", "deprecation"})
    @CallSuper
    protected void onCreateNavController(@NonNull NavController navController) {
        navController.getNavigatorProvider().addNavigator(
                new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
        navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
    }
 
    /**
     * 創(chuàng)建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這個類代碼行數(shù)不多,這里在精簡了下,保留了幾個在初始化流程上的方法,布局中遇到fragment標(biāo)簽,會先進行創(chuàng)建view,執(zhí)行到NavHostFragment就會先執(zhí)行這里的onInflate(),可以看到這里獲取到了導(dǎo)航圖的id,并賦值給了變量mGraphId。接著就會調(diào)用到fragment的生命周期方法,也就是這里的onCreate()方法,在這里會先初始化NavHostController對象,然后調(diào)用了onCreateNavController()方法,這個方法和NavHostController的構(gòu)造函數(shù)都創(chuàng)建了導(dǎo)航控制器并添加NavigatorProvider對象中,導(dǎo)航到指定頁面時用到的就是這里的控制器,之后調(diào)用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;
        // 在導(dǎo)航圖中配置的startDestination默認顯示頁面就是在這個方法中處理的
        onGraphCreated(startDestinationArgs);
    }

可以看到,這里對導(dǎo)航圖xml進行了解析,最終結(jié)果存儲在NavGraph中,這里對xml的解析類似于布局xml的解析,這里就不進去看了,感興趣的可以自己看看,在導(dǎo)航圖的根標(biāo)簽下通常會配置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();
        }
    }

這里會調(diào)用到navigate()這個方法,傳遞的是導(dǎo)航圖中的根對象:

    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);
        ... ...
    }

這里先獲取到導(dǎo)航控制器,然后導(dǎo)航到對應(yīng)的界面,關(guān)于導(dǎo)航控制器的添加,前面有說到,這里再來看下具體的添加:

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

調(diào)用的是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是導(dǎo)航控制器類上的注解,比如:

@Navigator.Name("navigation")
public class NavGraphNavigator extends Navigator<NavGraph> {
    ... ...
}

這里獲取到的name就是這個navigation,并以這個name為key保存對應(yīng)的導(dǎo)航控制器,這里回到上面的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是導(dǎo)航圖的根對象,node.getNavigatorName()獲取到的值是navigation,故這里獲取到的導(dǎo)航控制器是NavGraphNavigator,接著調(diào)用它的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);
    }

先獲取到導(dǎo)航圖中配置的默認顯示視圖id,然后根據(jù)id找到對應(yīng)的導(dǎo)航目的地,根據(jù)導(dǎo)航目的地獲取對應(yīng)導(dǎo)航控制器,以如下導(dǎo)航圖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,那對應(yīng)的導(dǎo)航控制器是FragmentNavigator,接著調(diào)用它的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的操作了,執(zhí)行完成后對應(yīng)的視圖也就顯示出來了,關(guān)于點擊導(dǎo)航的也類似,這里就不在贅述了。

到此這篇關(guān)于Android中導(dǎo)航組件Navigation的實現(xiàn)原理的文章就介紹到這了,更多相關(guān)Android導(dǎo)航組件Navigation內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Gradle的安裝和環(huán)境變量的配置詳解

    Gradle的安裝和環(huán)境變量的配置詳解

    這篇文章主要介紹了Gradle的安裝和環(huán)境變量的配置詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Flutter實現(xiàn)圖文并茂的列表

    Flutter實現(xiàn)圖文并茂的列表

    列表在 App 中是最常見的形式了,在 Flutter 中提供了 ListView 這個組件來實現(xiàn)列表,本篇將通過 ListView 實現(xiàn)一個圖文并茂的列表。
    2021-05-05
  • Android實現(xiàn)截圖分享qq 微信功能

    Android實現(xiàn)截圖分享qq 微信功能

    在日常生活中,經(jīng)常用到qq,微信截圖分享功能,今天小編通過本文給大家介紹Android實現(xiàn)截圖分享qq 微信功能,具體實現(xiàn)代碼大家參考下本文
    2017-12-12
  • Android評分控件RatingBar使用實例解析

    Android評分控件RatingBar使用實例解析

    這篇文章主要為大家詳細介紹了Android評分控件RatingBar使用實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Android控件系列之RadioButton與RadioGroup使用方法

    Android控件系列之RadioButton與RadioGroup使用方法

    本文介紹了Android中如何使用RadioGroup和RadioButton,對比了RadioButton和CheckBox的區(qū)別,并實現(xiàn)了自定義的RadioGroup中被選中RadioButton的變更監(jiān)聽事件
    2012-11-11
  • 淺談Android中視圖動畫的屬性與使用

    淺談Android中視圖動畫的屬性與使用

    這篇文章給大家簡單介紹了Android中視圖動畫的基本屬性以及使用示例,對大家的學(xué)習(xí)有一定的參考價值,有需要的朋友們下面來一起看看吧。
    2016-09-09
  • Android App中制作仿MIUI的Tab切換效果的實例分享

    Android App中制作仿MIUI的Tab切換效果的實例分享

    這篇文章主要介紹了Android App中制作仿MIUI的Tab切換效果的實例分享,實現(xiàn)具有跟隨手指滾動而滾動功能的ViewPagerIndicator,需要的朋友可以參考下
    2016-04-04
  • Android動畫學(xué)習(xí)筆記之補間動畫

    Android動畫學(xué)習(xí)筆記之補間動畫

    這篇文章主要為大家詳細介紹了Android動畫學(xué)習(xí)筆記之補間動畫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • Android?Room數(shù)據(jù)庫加密詳解

    Android?Room數(shù)據(jù)庫加密詳解

    這篇文章主要為大家詳細介紹了Android?Room數(shù)據(jù)庫加密,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Android開發(fā)中的9個常見錯誤和解決方法

    Android開發(fā)中的9個常見錯誤和解決方法

    這篇文章主要介紹了Android開發(fā)中的9個常見錯誤和解決方法,這是Android開發(fā)中最常見的9個錯誤,經(jīng)過各種各樣的整理,以及和熱心網(wǎng)友討論總結(jié)而來,需要的朋友可以參考下
    2015-01-01

最新評論