iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android中导航组件Navigation的实现原理
  • 288
分享到

Android中导航组件Navigation的实现原理

2024-04-02 19:04:59 288人浏览 泡泡鱼
摘要

        对于导航组件的使用方式不是本文的重点,具体使用可以参考官方文档,导航组件框架是通过fragment来实现的,其核心类主要可以分

        对于导航组件的使用方式不是本文的重点,具体使用可以参考官方文档,导航组件框架是通过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);
            }
        }
    }
 
    
    @SuppressWarnings({"WeakerAccess", "deprecation"})
    @CallSuper
    protected void onCreateNavController(@NonNull NavController navController) {
        navController.getNavigatorProvider().addNavigator(
                new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
        navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
    }
 
    
    @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的操作了,执行完成后对应的视图也就显示出来了,关于点击导航的也类似,这里就不在赘述了。

到此这篇关于Android中导航组件Navigation的实现原理的文章就介绍到这了,更多相关Android导航组件Navigation内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Android中导航组件Navigation的实现原理

本文链接: https://www.lsjlt.com/news/138363.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android中导航组件Navigation的实现原理
            对于导航组件的使用方式不是本文的重点,具体使用可以参考官方文档,导航组件框架是通过fragment来实现的,其核心类主要可以分...
    99+
    2024-04-02
  • Android Jetpack组件Navigation导航组件的基本使用
    目录1.Navigation 基本概念2.Navigation 使用入门2.1 添加Navigation依赖2.2 创建导航图2.3 导航图中添加目的地Fragment2.4 Act...
    99+
    2024-04-02
  • Android实现底部导航栏方法(Navigation篇)
    Navigation实现底部导航栏 前言导入和基本使用导入基础使用创建nav文件编辑Nav文件添加页面(代码版)添加页面(图解版) 创建导航动作 action创建action(代码版)...
    99+
    2023-10-10
    android
  • Android怎么实现字母导航控件
    这篇文章主要介绍“Android怎么实现字母导航控件”,在日常操作中,相信很多人在Android怎么实现字母导航控件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android怎么实现字母导航控件”的疑惑有所...
    99+
    2023-06-26
  • bootstrap侧边导航栏实现原理
    这篇文章主要介绍bootstrap侧边导航栏实现原理,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!bootstrap自带的响应式导航栏是向下滑动的,有时满足不了个性化的需求,需要做一个类似于android drawe...
    99+
    2023-06-14
  • Android 中ActionBar+fragment实现页面导航的实例
    Android 中ActionBar+fragment实现页面导航的实例为保证android2.0以上均能运行,使用support.v7库下的actionbar及fragment继承自AppCompatActivity(ActionBarA...
    99+
    2023-05-30
    android actionbar fragment
  • 一文详解 Compose Navigation 的实现原理
    目录前言1. 从 Jetpack Navigation 说起2. 定义导航3. 导航跳转4. 保存状态SaveableStateHolder & rememberSaveab...
    99+
    2024-04-02
  • Android实现字母导航控件的示例代码
    目录自定义属性Measure测量坐标计算绘制Touch事件处理数据组装显示效果今天分享一个以前实现的通讯录字母导航控件,下面自定义一个类似通讯录的字母导航 View,可以知道需要自定...
    99+
    2024-04-02
  • vue+element-ui实现头部导航栏组件
    本文实例为大家分享了vue+element-ui实现头部导航栏组件具体代码,供大家参考,具体内容如下 话不多说,先上一张效果图: 这是一个头部导航栏,网站最常见的一个功能,鼠标点击...
    99+
    2024-04-02
  • Vue3导航栏组件封装实现方法
    在Vue3中封装一个导航栏组件,并且实现,随着滚动条滚动实现一个吸顶效果,供大家参考 导航栏组件的效果图: 滚动条滚动以后的吸顶效果示意图: 具体代码展示: <temp...
    99+
    2024-04-02
  • Vue中怎么实现一个底部导航栏组件
    今天就跟大家聊聊有关Vue中怎么实现一个底部导航栏组件,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。<template> <di...
    99+
    2024-04-02
  • ANDROID BottomNavigationBar底部导航栏的实现示例
    第一种介绍的就是使用开源库,因为使用开源库最简单,也更加的符合我们的审美标准,同时BottomNavigationBar还是符合当前的Material Design标准的。效果展示依赖compile'com.ashokvarma.andro...
    99+
    2023-05-30
    android bottomnavigationbar ott
  • 一个侧边栏导航组件实现方法教程
    这篇文章主要讲解了“一个侧边栏导航组件实现方法教程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“一个侧边栏导航组件实现方法教程”吧!构建一个响应式导航系统是...
    99+
    2024-04-02
  • Android中TabLayout+ViewPager 简单实现app底部Tab导航栏
    前言在谷歌发布Android Design Support Library之前,app底部tab布局的实现方法就有很多种,其中有RadioGroup+FrameLayout、TabHost+Fragment、FragmentPagerAda...
    99+
    2023-05-31
    android tablayout age
  • Android自定义水波纹底部导航的实现
    今天给大家带来一个自定义的底部导航,我不会做动图,只能搞一张图片给大家看看,大家见谅 这个就是自定义的tablayout底部搞好的样式了 TabLayout提供了一个水平布局用于展...
    99+
    2022-11-13
    Android 水波纹底部导航 Android 水波纹
  • android : 底部导航栏的实现(使用ViewPager和BottomNavigationView)
      本案例中需要用的控件ViewPager和BottomNavigationView ViewPager:主要是页面的切换Fragment:碎片(也就是每个页面的内容)BottomNavigationView:底部导航栏 非常简单,主要就...
    99+
    2023-09-01
    android android studio ide
  • Android中使用ViewPagerIndicator实现一个导航栏指示器
    今天就跟大家聊聊有关Android中使用ViewPagerIndicator实现一个导航栏指示器,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。自定义一个ViewPagerIndica...
    99+
    2023-05-31
    android viewpagerindicator age
  • Android 中使用RadioGroup和Fragment实现底部导航栏的功能
    在一些购物商城中经常会遇到这类效果,效果图如下: 先看效果图 步骤一: 完成对主界面main.xml的创建: <?xml version="1.0" enco...
    99+
    2024-04-02
  • C/C++ Qt 选择夹TabWidget组件实现导航栏切换
    目录在Qt中通过使用选择夹组件可以实现在一个页面中集成多种功能,我们以TabWidget选择夹组件为例,实现在单个页面中集成多个功能,并给每一个子夹增加对应的Ico图标。 如果我们使...
    99+
    2024-04-02
  • Android应用中怎么实现一个导航键透明效果
    这期内容当中小编将会给大家带来有关Android应用中怎么实现一个导航键透明效果,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。MainActivity代码public class Ma...
    99+
    2023-05-31
    android roi
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作