观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

版权声明

本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/12344572.html

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

前言

  这篇博客只讲解NavController控制器的一些API的使用详解与应用环境的说明。不在讲解一些Navigation的基础知识,如果你还不了解Navigation建议你去另外一篇博客了解 https://www.cnblogs.com/guanxinjing/p/11555217.html

为什么要了解NavController

  NavController字面意思就是导航控制器,它负责操作Navigation框架下的Fragment的跳转与退出、动画、监听当前Fragment信息,当然这些是基本操作。但是更重要的是知道它可以使用的范围,一般情况下我们以为它只能在Fragment里调用,实际情况下它在Activity里也可以调用。灵活的使用它,可以帮你实现所有形式的页面跳转。除此之外你甚至还能使用TabLayout配合Navigation进行主页的分页设计。极端点你甚至还能在某个分页里再次添加TabLayout配合Navigation进行嵌套设计。

如何获取NavController实例

在Activity中获取

NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment); //这个R.id.fragment就是Activity布局里fragment控件的id

在Fragment中获取

NavController  navController = Navigation.findNavController(getView());

API讲解与使用场景

请注意一下代码有些是在Activity里操作NavController,但是实际上下面的代码也可以在Fragment里执行,这2者是没有区别的。所以这里并不重复贴Fragment里的代码了

在Activity根据业务需要使用setGraph切换不同的Navigation

xml

可以把   app:navGraph= 这个属性去除

    <fragment
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"/>

java

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_demo);  
        initNav(2);
    }

    private void initNav(int type){
        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment); //在Activity里获取NavController实例
        if (type == 1){
            controller.setGraph(R.navigation.demo_one_nav);  //设置xml文件
            return;
        }
        if (type == 2){
            Bundle bundle = new Bundle();
            bundle.putString("name", "demo");
            controller.setGraph(R.navigation.demo_two_nav, bundle); //设置xml文件的并传入数据,这个数据可以在启动的Fragment里获取到
            return;
        }
        DemoActivity.this.finish();
    }

上面的第二种方式,我们在设置xml文件的同时还传入了数据,这个数据可以在Fragment里建议用下面的方式获取

  @Override
    public void onAttach(@NonNull Context context) { //在Fragment里重写onAttach,在这里获取可以保证数据不会为null
        super.onAttach(context);
        Log.e("触发", "onAttach: 传入数据=" + getArguments().getString("name"));
    }

在Activity使用navigate跳转Fragment

使用Action的id跳转

请注意!下面的navigate的值是action的id,而使用action的id跳转Fragment是不能凭空在没有上一个Fragment的情况下跳转下一个Fragment的,这是会报错的。

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
        controller.setGraph(R.navigation.demo_one_nav);
        controller.navigate(R.id.action_oneFragment_to_twoFragment); //从默认启动的oneFragment跳转到TwoFragment

    }

使用fragment的id直接跳转

<fragment
        android:id="@+id/threeFragment"
        android:name="com.zh.fragmentdemo.ThreeFragment"
        android:label="fragment_three"
        tools:layout="@layout/fragment_three" />

在activity里

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
        controller.setGraph(R.navigation.demo_one_nav);
        controller.navigate(R.id.threeFragment); //直接跳转到threeFragment

    }

使用在navigation xml文件的fragment的id可以直接跳转到某一个Fragment里,这种跳转方式可以不需要是否有上一个Fragment。例如我们需要直接跳转到threeFragment。请注意!下面使用navigate直接指定id目标Fragment上是会创建一个新的Fragment到堆栈里的。这个时候就会看到每次navigate都会创建一个新的实例从而导致内存泄漏. 实际项目在一些tabLayout功能下最好配合popBackStack来使用。如果popBackStack跳转到指定目标为false,在使用navigate来跳转到指定Fragment。如下

if (!mNavController.popBackStack(R.id.threeFragment, false)) { 
  mNavController.navigate(R.id.threeFragment); 
}

 

使用navigate带动画跳转或者弹出Fragment

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
        controller.setGraph(R.navigation.demo_one_nav);
        NavOptions navOptions = new NavOptions.Builder()
                .setEnterAnim(R.anim.from_right) //进入动画
                .setExitAnim(R.anim.to_left)    //退出动画
                .setPopEnterAnim(R.anim.to_left)    //弹出进入动画
                .setPopExitAnim(R.anim.from_right)  //弹出退出动画
                .build();
        controller.navigate(R.id.action_oneFragment_to_twoFragment, null , navOptions);
    }

 

使用popBackStack弹出Fragment

方式一 弹出当前的Fragment

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
        controller.setGraph(R.navigation.demo_one_nav);
        controller.navigate(R.id.action_oneFragment_to_twoFragment); //从oneFragment进入到twoFragment
        controller.navigate(R.id.action_twoFragment_to_threeFragment);  //从twoFragment进入到threeFragment
        controller.popBackStack(); //弹出threeFragment,回到twoFragment
        
    }

方式二 弹出到指定的Fragment,并且设置布尔值是否连这个指定Fragment一起弹出

请注意! 请重视 public boolean popBackStack(@IdRes int destinationId, boolean inclusive) 这个方法。因为此方法可以实现清空中间导航栈堆的需求,举例假如 现在有 A -> B -> C -> D 这四个导航Fragment,如果我们现在当前导航到D并且想回到A,如果使用navigate()方法回到A,你就会发现用A Fragment里按返回键,不是直接退出而是直接到D  Fragment。 如果是使用popBackStack(@IdRes int destinationId, boolean inclusive)方法,就不会回到D Fragment

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
        controller.setGraph(R.navigation.demo_one_nav);
        controller.navigate(R.id.action_oneFragment_to_twoFragment); //从oneFragment进入到twoFragment
        controller.navigate(R.id.action_twoFragment_to_threeFragment); //从twoFragment进入到threeFragment
        controller.popBackStack(R.id.twoFragment, true); //弹出到twoFragment,第二个参数的布尔值如果为true则表示参数一的Fragment一起弹出,这个时候我们就会回到oneFragment

    }

另外,如果你发现目标Fragment不存在跳转失败后,你可以参考以下这种实现,直接创建指定目标的Fragment

                    boolean jumpStatus = Navigation.findNavController(getView()).popBackStack(R.id.scenesTimingFragment, false);
                    if (!jumpStatus){
                        Navigation.findNavController(getView()).navigate(R.id.scenesTimingFragment);
                    }

navigateUp() 向上导航

 Navigation.findNavController(getView()).navigateUp();

navigateUp也是执行返回上一级Fragment的功能。 有小朋友会问了popBackStack和navigateUp()的区别是什么呢?

navigateUp向上返回的功能其实也是调用popBackStack的。 但是,navigateUp的源码里多了一层判断,源码如下:就是判断这个Navigation是否是最后一个Fragment,并且这个Navigation与里面的Fragment是不是有可能是其他Navigation跳转过来的。如果是其他Navigation跳转过来的就会回到之前的Navigation上。并且销毁当前Navigation的Activity。

public boolean navigateUp() {
        if (getDestinationCountOnBackStack() == 1) {
            // If there's only one entry, then we've deep linked into a specific destination
            // on another task so we need to find the parent and start our task from there
            NavDestination currentDestination = getCurrentDestination();
            int destId = currentDestination.getId();
            NavGraph parent = currentDestination.getParent();
            while (parent != null) {
                if (parent.getStartDestination() != destId) {
                    TaskStackBuilder parentIntents = new NavDeepLinkBuilder(this)
                            .setDestination(parent.getId())
                            .createTaskStackBuilder();
                    parentIntents.startActivities();
                    if (mActivity != null) {
                        mActivity.finish();
                    }
                    return true;
                }
                destId = parent.getId();
                parent = parent.getParent();
            }
            // We're already at the startDestination of the graph so there's no 'Up' to go to
            return false;
        } else {
            return popBackStack();
        }
    }

使用getCurrentDestination获取当前导航目的地

        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
        NavDestination navDestination = controller.getCurrentDestination(); //获取当前目的地的信息
        Log.e(TAG, "onCreate: NavigatorName = " + navDestination.getNavigatorName());
        Log.e(TAG, "onCreate: id = " + navDestination.getId());
        Log.e(TAG, "onCreate: Parent = " + navDestination.getParent());

 添加或者移除导航目的地监听

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
        NavController.OnDestinationChangedListener listener = new NavController.OnDestinationChangedListener() {
            @Override
            public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
                Log.e(TAG, "onDestinationChanged: id = " + destination.getId());
            }
        };
        controller.addOnDestinationChangedListener(listener); //添加监听
        controller.setGraph(R.navigation.demo_one_nav);
        controller.navigate(R.id.action_oneFragment_to_twoFragment);
        controller.navigate(R.id.action_twoFragment_to_threeFragment);
        controller.removeOnDestinationChangedListener(listener); //移除监听
    }

getNavInflater  Navigation创建

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        NavController controller = Navigation.findNavController(DemoActivity.this, R.id.fragment);
        NavGraph navGraph = controller.getNavInflater().inflate(R.navigation.demo_one_nav); //获得NavGraph 就是navigation,可以操作各种Fragment的增加或移除功能,一个是代码操作一个是xml操作
        controller.setGraph(navGraph); //其实用这种方式setGraph(navGraph)与直接setGraph(R.navigation.demo_one_nav);一样
    }

Fragment里嵌套Navigation

在Fragment里

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTabLayout = (TabLayout) view.findViewById(R.id.tab_layout);
        mAddBtn = (ImageView) view.findViewById(R.id.add_btn);
        mNavController = Navigation.findNavController(getActivity(), R.id.scenes_fragment);//请注意,这个 R.id.scenes_fragment是Fragment布局里的fragment

    }

判断当前页面显示的Fragment是不是目标Fragment

    public fun <F : Fragment> isActiveFragment(fragmentClass: Class<F>): Boolean {
        val navHostFragment = this.supportFragmentManager.fragments.first() as NavHostFragment
        navHostFragment.childFragmentManager.fragments.forEach {
            if (fragmentClass.isAssignableFrom(it.javaClass)) {
                return true
            }
        }
        return false
    }

 

 

 

End

posted on 2020-02-22 11:07  观心静  阅读(31439)  评论(4编辑  收藏  举报