还没有用过navigation的小伙伴赶紧来用它,实在是太方便了,·再也不用写supportFragmentManager.beginTransaction().add()和supportFragmentManager.beginTransaction().replace()来切换布局了,只要将所有的需要跳转的Frament全部放到布局里面就好了,navigation和flutter的路由差不多,它只不过是将frament装到了作为一个个的跳转点而已。我觉着它最大的优势是可以通过android studio清晰的看到每个界面的跳转点。如图所示:

不用运行就可以看见每个模块的界面跳转,是不是很炫。大家可以参考一下官方文档android导航。

用起来也比较简单首先在build.gradle中加入

 implementation "androidx.navigation:navigation-fragment-ktx:2.1.0"    implementation "androidx.navigation:navigation-ui-ktx:2.1.0"

引入支持库之后,第二步在你的模块主布局中加入需要动态变化的Frament

    

对,就是上面这个东西,注意一点最终实例化的Fragmentandroidx.navigation.fragment.NavHostFragment类,实例化的Frament是扩展库中的类,注意 app:navGraph="@navigation/mobile_navigation" 这个属性是真正的填充Fragment的类。第三步,在res下新建navigation文件夹,xml内容如下:

                                                                                                                                                                                                                                                

其中app:startDestination="@+id/home_dest"属性用来声明第一个显示的Fragment,argument标签用来声明传递的属性,action标签是用来跳转的,这和activity的action有异曲同工之妙,跳转到新的Fragment的时候调用Navigation.findNavController(view).navigate(),回到上一个Fragment的时候调Navigation.findNavController(view).navigateUp()

方法。好了,这就是Navigation的基本使用了,大家可以在官网下载demo看一下,接下来来探究一下navigation的原理。

前面介绍了navigation使用的入口就是

androidx.navigation.fragment.NavHostFragment这个类

public class NavHostFragment extends Fragment implements NavHost

NavHostFragment 就是一个Fragment并实现了NavHost接口,这个接口只有一个方法

public interface NavHost {    /**     * Returns the {@link NavController navigation controller} for this navigation host.     *     * @return this host's navigation controller     */    @NonNull    NavController getNavController();}

在它的onCreateView中,它只是填充了简单的帧布局FrameLayout,它的用处就是最终用来替换布局的

  public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,                             @Nullable Bundle savedInstanceState) {        FrameLayout frameLayout = new FrameLayout(inflater.getContext());        // When added via XML, this has no effect (since this FrameLayout is given the ID        // automatically), but this ensures that the View exists as part of this Fragment's View        // hierarchy in cases where the NavHostFragment is added programmatically as is required        // for child fragment transactions        frameLayout.setId(getId());        return frameLayout;    }

onCreateView执行完以后会执行onViewCreated,看一下

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        if (!(view instanceof ViewGroup)) {            throw new IllegalStateException("created host view " + view + " is not a ViewGroup");        }        Navigation.setViewNavController(view, mNavController);        // When added programmatically, we need to set the NavController on the parent - i.e.,        // the View that has the ID matching this NavHostFragment.        if (view.getParent() != null) {            View rootView = (View) view.getParent();            if (rootView.getId() == getId()) {                Navigation.setViewNavController(rootView, mNavController);            }        }    }

很简单就是为当前Fragment设置当前的根view设置一个tag,这个tag的值是NavHostController,这个类实现了对Fragment的控制的封装。而onInflate是在fragment标签被解析的时候调用的,它早于fragment的任何生命周期方法

  public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs,            @Nullable Bundle savedInstanceState) {        super.onInflate(context, attrs, savedInstanceState);        final TypedArray navHost = context.obtainStyledAttributes(attrs, R.styleable.NavHost);        final int graphId = navHost.getResourceId(R.styleable.NavHost_navGraph, 0);        if (graphId != 0) {            mGraphId = graphId;        }        navHost.recycle();        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment);        final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);        if (defaultHost) {            mDefaultNavHost = true;        }        a.recycle();    }

这里根据属性解析出app:navGraph引用的xml的id

然后在onCreate方法通过  mNavController.setGraph(mGraphId)方法解析出来第一个需要显示的Fragment

 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;                requireFragmentManager().beginTransaction()                        .setPrimaryNavigationFragment(this)                        .commit();            }        }        if (navState != null) {            // Navigation controller state overrides arguments            mNavController.restoreState(navState);        }        if (mGraphId != 0) {            // Set from 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);            }        }    }

继续看

  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;        onGraphCreated(startDestinationArgs);    }

如果mGraph 不为null就证明栈中已经存放了当前的Fragment,直接让它出栈就好了,接着看onGraphCreated方法

 private void onGraphCreated(@Nullable Bundle startDestinationArgs) {        if (mNavigatorStateToRestore != null) {            ArrayList navigatorNames = mNavigatorStateToRestore.getStringArrayList(                    KEY_NAVIGATOR_STATE_NAMES);            if (navigatorNames != null) {                for (String name : navigatorNames) {                    Navigator navigator = mNavigatorProvider.getNavigator(name);                    Bundle bundle = mNavigatorStateToRestore.getBundle(name);                    if (bundle != null) {                        navigator.onRestoreState(bundle);                    }                }            }        }        if (mBackStackUUIDsToRestore != null) {            for (int index = 0; index < mBackStackUUIDsToRestore.length; index++) {                UUID uuid = UUID.fromString(mBackStackUUIDsToRestore[index]);                int destinationId = mBackStackIdsToRestore[index];                Bundle args = (Bundle) mBackStackArgsToRestore[index];                NavDestination node = findDestination(destinationId);                if (node == null) {                    throw new IllegalStateException("unknown destination during restore: "                            + mContext.getResources().getResourceName(destinationId));                }                if (args != null) {                    args.setClassLoader(mContext.getClassLoader());                }                mBackStack.add(new NavBackStackEntry(uuid, node, args, mViewModel));            }            updateOnBackPressedCallbackEnabled();            mBackStackUUIDsToRestore = null;            mBackStackIdsToRestore = null;            mBackStackArgsToRestore = null;        }        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);            }        }    }

和流程有关的方法是navigate,继续进入

    private void navigate(@NonNull NavDestination node, @Nullable Bundle args,            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {        boolean popped = false;        if (navOptions != null) {            if (navOptions.getPopUpTo() != -1) {                popped = popBackStackInternal(navOptions.getPopUpTo(),                        navOptions.isPopUpToInclusive());            }        }        Navigator navigator = mNavigatorProvider.getNavigator(                node.getNavigatorName());        Bundle finalArgs = node.addInDefaultArgs(args);        NavDestination newDest = navigator.navigate(node, finalArgs,                navOptions, navigatorExtras);        if (newDest != null) {            if (!(newDest instanceof FloatingWindow)) {                // We've successfully navigating to the new destination, which means                // we should pop any FloatingWindow destination off the back stack                // before updating the back stack with our new destination                //noinspection StatementWithEmptyBody                while (!mBackStack.isEmpty()                        && mBackStack.peekLast().getDestination() instanceof FloatingWindow                        && popBackStackInternal(                                mBackStack.peekLast().getDestination().getId(), true)) {                    // Keep popping                }            }            // The mGraph should always be on the back stack after you navigate()            if (mBackStack.isEmpty()) {                mBackStack.add(new NavBackStackEntry(mGraph, finalArgs, mViewModel));            }            // Now ensure all intermediate NavGraphs are put on the back stack            // to ensure that global actions work.            ArrayDeque 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, mViewModel));                }                destination = parent;            }            mBackStack.addAll(hierarchy);            // And finally, add the new destination with its default args            NavBackStackEntry newBackStackEntry = new NavBackStackEntry(newDest,                    newDest.addInDefaultArgs(finalArgs), mViewModel);            mBackStack.add(newBackStackEntry);        }        updateOnBackPressedCallbackEnabled();        if (popped || newDest != null) {            dispatchOnDestinationChanged();        }    }

这个方法有点长,大部分代码都是保存当前需要显示的Fragment类的封装类NavDestination ,保存到栈中。

navigator.navigate(node, finalArgs,                navOptions, navigatorExtras);

navigator.navigate是真正实现Fragment切换的,真正实现类是FragmentNavigator

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

这个方法最重要的就是这个代码   ft.replace(mContainerId, frag),ft是FragmentTransaction 类,而mContainerId就是NavHostFragment的布局View的id如下:

 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,                             @Nullable Bundle savedInstanceState) {        FrameLayout frameLayout = new FrameLayout(inflater.getContext());        // When added via XML, this has no effect (since this FrameLayout is given the ID        // automatically), but this ensures that the View exists as part of this Fragment's View        // hierarchy in cases where the NavHostFragment is added programmatically as is required        // for child fragment transactions        frameLayout.setId(getId());        return frameLayout;    }

所以Frament的切换都是通过NavHostFragment的FrameLayout 来切换的

 

 

更多相关文章

  1. 安卓 屏蔽子控件点击事件
  2. Android(安卓)数据存取到文件当中
  3. android 实现自定义卫星菜单
  4. Activity的创建
  5. android的莫名其妙的事
  6. Android图形动画概述
  7. 自定义android Rating bar
  8. android 巧用finish方法
  9. 自定义Dialog之信息提示

随机推荐

  1. 北京地铁赵炜:《网络安全审查办法》背景下
  2. 全球分布式,多模数据库Azure Cosmos DB
  3. 原创 | 从ZOOM个人信息安全事件浅谈视频
  4. 运营商常见大数据业务
  5. Omega系统简介
  6. 平台发展趋势
  7. 原创 | 初探施耐德PLC蠕虫技术
  8. 老罗能成功吗
  9. 原创 | ***ICS的新型神秘勒索软件
  10. Azure IoT解决方案