navigation_navigationbar - CSDN
精华内容
参与话题
  • Navigation框架介绍

    千次阅读 2019-03-08 18:14:46
    Navigation框架 概述 Navigation,导航, Google官方对它的描述: 今天,我们宣布推出Navigation组件,作为构建您的应用内界面的框架,重点是让单 Activity 应用成为首选架构。利用Navigation组件对 Fragment 的...

    Navigation框架

    概述

    Navigation,导航, Google官方对它的描述:

    今天,我们宣布推出Navigation组件,作为构建您的应用内界面的框架,重点是让单 Activity 应用成为首选架构。利用Navigation组件对 Fragment 的原生支持,您可以获得架构组件的所有好处(例如生命周期和 ViewModel),同时让此组件为您处理 FragmentTransaction 的复杂性。此外,Navigation组件还可以让您声明我们为您处理的转场。它可以自动构建正确的“向上”和“返回”行为,包含对深层链接的完整支持,并提供了帮助程序,用于将导航关联到合适的UI小部件,例如抽屉式导航栏和底部导航。## 一.相关文档

    1. 入门阅读:

    2.相关博客

    3. 提升阅读:

    二.使用方法(按照NavigationDemo进行讲解)

    Demo运行需要的虚拟机尺寸:

    generic_x86:/ $ wm density
    wm density
    Physical density: 240
    Override density: 200
    generic_x86:/ $ wm size
    wm size
    Physical size: 1920x720
    generic_x86:/ $
    

    step1 在Module下的build.gradle中添加以下依赖:

        // Navigation kotlin版本(依赖Kotlin插件)
        implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha08' 
        implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha08' 
         // Navigation Java版本
        implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha08' 
        implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha08'
    

    step2 分析Fragment层级,创建Fragment:

    层级关系分为两种类型:并列关系和上下级关系。

    • 并列关系的Fragment需要在转场切换中保持实例的唯一性,确保不会因为转场和切换致使实时数据丢失而影响用户体验。
    • 上下级关系的Fragment,下级Fragment的生命周期需要从推出开始然后到返回到上一级Fragment的时候结束。在本项目中各个Fragment的层级关系如图2-1所示:image注:
    • 实线表示构成,而黄色表示实体所含Fragment对象除初始destination的生命周期跟随父Fragment之外其他的Fragment只拥有一次生命周期。
    • 蓝色虚线表示并列关系.
    • 青色带箭头实线按照指向表示上下级关系.
    • 青色带箭头虚线线按照指向表示越级转场.
    • 青色实体表示只拥有一次生命周期的Fragment。

    step3 创建导航配置文件:如上图2-1所示,由同一实体发出由实线构成的一组组合对应一个graph文件,创建步骤如下:

    • 在项目module的res文件夹下创建navigation文件夹。
    • 在navigation文件夹下创建对应的 graph xml文件,如图3-1所示:*根据名称能反映出与之对应的Activity或Fragment实体
      image
    • graph文件xml内容格式如main_navigation.xml:
    <?xml version ="1.0" encoding ="utf-8"?>
     <!--startDestination 表示初始destination,在这里每一个Fragment就是一个destination节点未来可能支持Activity-->
     <!--keep_state_fragment 表示一个destination节点,节点标签名keep_state_fragment可自定义-->
    <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"
        app:startDestination="@+id/testCrossLevelFragment">
      
        <keep_state_fragment
            android:id="@+id/testCrossLevelFragment"
            android:name="com.oaksdance.navigationdemo.ui.fragment.test.TestCrossLevelContainerFragment"
            android:label="TestCrossLevelFragment"
            tools:layout="@layout/fragment_test_cross_level_container" />
        <keep_state_fragment
            android:id="@+id/blueToolFragment"
            android:name="com.oaksdance.navigationdemo.ui.fragment.BlueToolFragment"
            android:label="@string/blue_tool"
            tools:layout="@layout/fragment_blue_tool" />
        <keep_state_fragment
            android:id="@+id/screenFragment"
            android:name="com.oaksdance.navigationdemo.ui.fragment.ScreenFragment"
            android:label="@string/screen"
            tools:layout="@layout/fragment_screen" />
        <keep_state_fragment
            android:id="@+id/soundFragment"
            android:name="com.oaksdance.navigationdemo.ui.fragment.SoundFragment"
            android:label="@string/sound"
            tools:layout="@layout/fragment_sound" />
        <keep_state_fragment
            android:id="@+id/commonFragment"
            android:name="com.oaksdance.navigationdemo.ui.fragment.CommonFragment"
            android:label="@string/common"
            tools:layout="@layout/fragment_common" />
    </navigation>
    
    • graph文件支持可视化编辑,点击左下角design进入如图的可视化界面,点击红色箭头所示加号按钮即可添加destination节点,如下图3-2所示:
      image
    • graph文件针对不同层级关系采用不同的destination标签:
      • 对于并列关系的Fragment destination节点,需要拓展Naviagtion框架中的Navigator类,利用拓展的Navigator类对Fragment的添加和调用方式进行适应性改变。在Demo中com.oaksdance.navigationdemo.navigation.KeepStateNavigator就是这个拓展的类,它自定义了一个名称为keep_state_fragment的destination标签名。
      • 框架默认的Fragment destination节点是上下级关系型的,它每一次的显现其实都是一次replace,而默认的destination标签名就是fragment。
      • 在Demo中由如2-1所示的每组蓝色实线构成对应的graph文件其destination标签就是采用的keep_state_fragment而每组黄色实线构成对应的graph文件则采用框架默认的fragment标签名。

    step4 根据层级关系类型的不同采用不同方式调用graph文件:

    首先在Activity或Fragment的布局文件中引用fragment,并且指定它为androidx.navigation.fragment.NavHostFragment类,注意引用的时候一定不能忘记定义它的android:id。

    • 对于上下级关系的仅仅需要在app:navGraph配上graph文件的资源地址,如Demo中的NetContainerFragment:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.oaksdance.navigationdemo.ui.fragment.net.NetFragment">
        <fragment
            android:id="@+id/net_container_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="@dimen/fragment_content_top"
            app:defaultNavHost="true"
            app:navGraph="@navigation/net_container_navigation" />
    </LinearLayout>
    
    • 对于并列关系的,因为是自定义的Navigator所以需要使用代码向androidx.navigation.fragment.NavHostFragment中添加对应的graph文件,如Demo中的MainActivity:
    //获取NavHostFragment
    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.main_nav_host_fragment);// 实例化自定义的navigator
    Navigator navigator = new KeepStateNavigator(this, navHostFragment.getChildFragmentManager(), R.id.main_nav_host_fragment);// 将自定义的navigator配给NavHostFragment的NavController
    navHostFragment.getNavController().getNavigatorProvider().addNavigator(navigator);// 将graph文件赋给NavHostFragment的NavController
    navHostFragment.getNavController().setGraph(R.navigation.main_navigation);
    

    step5 调用graph文件中action ID或destination ID进行跳转操作:

    Action就是配置在graph中的一种表示跳转行为的标签,其往往配置在上下级关系类型的graph文件中,如Demo中的test1_container_navigation,根据其ID命名可以直观的了解到它的作用:

    <?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"
        app:startDestination="@id/test1Fragment">
        <fragment
            android:id="@+id/test1Fragment"
            android:name="com.oaksdance.navigationdemo.ui.fragment.test.test1.Test1Fragment"
            android:label="Test1Fragment">
            <action
                android:id="@+id/action_test1Fragment_to_child1Fragment"
                app:destination="@id/child1Fragment" />
        </fragment>
        <fragment
            android:id="@+id/child1Fragment"
            android:name="com.oaksdance.navigationdemo.ui.fragment.test.test1.Child1Fragment"
            android:label="Child1Fragment">
            <action
                android:id="@+id/action_child1Fragment_popUpTo_test1Fragment"
                app:popUpTo="@id/test1Fragment" />
        </fragment>
    </navigation>
    

    调用Action的场景有两种:

    • 内部调用,即在配置了Action的Fragment内部调用,如Test1Fragment调用ID为action_test1Fragment_to_child1Fragment的Action。
     NavHostFragment
        .findNavController(Test1Fragment.this)
             .navigate(R.id.action_test1Fragment_to_child1Fragment);
    
    • 越级调用,即子Fragment调用配置在父Fragment甚至是顶级Fragment内部的Action,如HotFragment调用它的父Fragment——NetFragment内部配置的Action,action_netContainerFragment_to_updateNameFragment和action_netContainerFragment_to_updatePasswordFragment:
     NavigationUtil.takeAction(
        HotFragment.this, 
        R.id.net_container_host_fragment, //NetFragment寄生的NavHostFragment的ID
        R.id.action_netContainerFragment_to_updateNameFragment //ActionID
     );
    

    注意:使用Action越级跳转是为了方便Fragment的返回退栈,因此这类Acition往往成对出现,例如UpdateNameFragment的返回,不过这次调用返回Action属于内部调用:

     NavHostFragment
        .findNavController(UpdateNameFragment.this)
            .navigate(R.id.action_pop_up_from_updateNameFragment);
    

    在一组并列关系的Fragment中,因为他们之间不存在返回,所以它们之间相互调转直接调用它们各自对应的destinationID,如MainActivity中:

    private void showFragment(int position) {
        Navigation
            .findNavController(this, R.id.main_nav_host_fragment)
             .navigate(getResId(position));
    }
    private int getResId(int position) {
        switch (position) {
            case 1:
                return R.id.blueToolFragment;
            case 2:
                return R.id.screenFragment;
            case 3:
                return R.id.soundFragment;
            case 4:
                return R.id.commonFragment;
            default:
                return R.id.testCrossLevelFragment;
        }
    }
    

    对于Navigation框架结合DrawerLayout,BottomNavigationView等等来使用的方法各式各样,但是查看源码可知,其跳转的逻辑都是想法设法获取NavController,然后通过NavController调用destinationID来跳转。

    参数传递和转场动画设置,根据官方Demo

    Navaigtion框架支持参数传递和转场动画设置,使用到的同样是NavController的navigate()方法:

    public void navigate(@IdRes int resId, @Nullable Bundle args,
            @Nullable NavOptions navOptions) {
        navigate(resId, args, navOptions, null);
    }
    
    • 参数传递

    首先在graph文件中destination节点下利用argument标签配置所要接受的参数名、参数默认值和参数类型,如:

     <fragment
            android:id="@+id/flow_step_one_dest"
            android:name="com.example.android.codelabs.navigation.FlowStepFirstFragment"
            tools:layout="@layout/flow_step_one_fragment">
           
            <argument
                android:name="flowStepNumber"
                android:defaultValue="1"
                app:argType="integer" />
                
            <action
                android:id="@+id/next_action"
                app:destination="@+id/flow_step_two_dest"/>
        </fragment>
    

    传递参数:

        val bundle = Bundle()
        bundle.putInt("flowStepNumber", 2)
        findNavController().navigate(R.id.flow_step_one_dest, bundle)
    

    接收参数:

      val flowStepNumber = arguments?.getInt("flowStepNumber")
    
    • 两种方法设置转场动画

    代码实例化一个NavOptions,然后把配置好的动画资源文件传给它,最后交给NavController的navigate()

    val options = navOptions {
        anim {
            enter = R.anim.slide_in_right
            exit = R.anim.slide_out_left
            popEnter = R.anim.slide_in_left
            popExit = R.anim.slide_out_right
        }
    }
    view.findViewById<Button>(R.id.navigate_destination_button)?
        .setOnClickListener {
            findNavController()
                .navigate(R.id.flow_step_one_dest, null, options)
    }
    
    

    在graph文件中Action配置转场动画

    <fragment
            android:id="@+id/home_dest"
            android:name="com.example.android.codelabs.navigation.HomeFragment"
            android:label="@string/home"
            tools:layout="@layout/home_fragment">
            <action
                android:id="@+id/next_action"
                app:destination="@+id/flow_step_one_dest"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right" />
        </fragment>
    

    三.源码分析UML图片来源Android官方架构组件Navigation:大巧不工的Fragment管理框架

    image

    NavGraphFragment Fragment容器

    两个作用:

    • 作为导航界面容纳Fragment的容器;
    • 管理并控制导航的行为本质上就是一个跟布局为FrameLayout的Fragment,它的OnCreateView方法如下:
    public View onCreateView(@NonNull LayoutInflater inflater, 
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
                             
            FrameLayout frameLayout = new FrameLayout(inflater.getContext());
            frameLayout.setId(getId());
            return frameLayout;
            
        }
    

    根据单一职责原则,管理并控制导航的行为 交给另外一个类,这个类的作用应该仅是 控制导航行为,命名为 NavController,由NavGraphFragment引用它的实例。这里同时将NavController的持有者抽象为一个接口NavHost,它只有一个方法getNavController()需要实现。

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

    为了保证导航的 安全,NavHostFragment 在其作用域内有且仅有一个NavController 的实例。然而Navigation.findNavController(View),参数中传递任意一个 view的引用似乎都可以获取 NavController——如何保证 NavController 的局部单例呢?

    事实上,NavHostFragment的View会以Tag的形式与其持有的NavController绑定,findNavController(View)内部实现是通过遍历View树,直到找到最底部 NavHostFragment中View持有的NavController对象,并将其返回的:

    //NavHostFragment的onViewCreated里调用 Navigation的setViewNavController()进行Tag绑定
    @Override
    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");
        }
        View rootView = view.getParent() != null ? (View) view.getParent() : view;
        Navigation.setViewNavController(rootView, mNavController);
    }    
    //遍历View树
    private static NavController findViewNavController(@NonNull View view) {
            while (view != null) {
                NavController controller = getViewNavController(view);
                if (controller != null) {
                    return controller;
                }
                ViewParent parent = view.getParent();
                view = parent instanceof View ? (View) parent : null;
            }
            return null;
      }
      
     //根据Tag找到NavController
     @Nullable
    private static NavController getViewNavController(@NonNull View view) {
        Object tag = view.getTag(R.id.nav_controller_view_tag);
        NavController controller = null;
        if (tag instanceof WeakReference) {
            controller = ((WeakReference<NavController>) tag).get();
        } else if (tag instanceof NavController) {
            controller = (NavController) tag;
        }
        return controller;
    }
    

    NavController导航控制器

    职责是:

    • 对navigation资源文件夹下nav_graph.xml的解析。
    • 通过解析xml,获取所有Destination(目标点)的引用或者Class的引用。
    • 记录当前栈中Fragment的顺序。
    • 管理控制导航行为。

    NavController 持有了一个 NavInflater ,并通过 NavInflater 解析xml文件。之后,获取了所有 Destination的 Class对象,并通过反射的方式,实例化对应的 Destination,通过一个队列保存:

    private NavInflater mInflater;  //NavInflater 
    private NavGraph mGraph;        //解析xml,得到NavGraph
    private int mGraphId;           //xml对应的id,比如 nav_graph_main
    //所有Destination的队列,用来处理回退栈
    private final Deque<NavDestination> mBackStack = new ArrayDeque<>(); 
    

    从这里可以看出Navigation是一个导航框架,今后可能并非只为Fragment导航。因此要将导航的Destination抽象出来,这个类叫做NavDestination——无论Fragment还是Activity,只要实现了这个接口,对于NavController来讲,他们都是Destination(目标点)而已。

    对于不同的 NavDestination 来讲,它们之间的导航方式是不同的,比如Activity 和 Fragment,这就需要根据不同的NavDestination进行不同的导航处理,遵循策略模式这里需要Navigator类。

    Navigator

    职责是:

    • 能够实例化对应的 NavDestination;
    • 能够指定导航;
    • 能够后退导航;

    以 FragmentNavigator为例,我们来看看它是如何执行的职责:

    public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
        //省略大量非关键代码,请以实际代码为主!
        @Override
        public boolean popBackStack() {
            return mFragmentManager.popBackStackImmediate();
        }
        @NonNull
        @Override
        public Destination createDestination() {
            // 核心代码如下,通过反射实例化Fragment
            Class<? extends Fragment> clazz = getFragmentClass();
            return  clazz.newInstance();
        }
        @Override
        public void navigate(@NonNull Destination destination, @Nullable Bundle args,
                                @Nullable NavOptions navOptions) {
            // 实际上还是通过FragmentTransaction进行的跳转处理
            final Fragment frag = destination.createFragment(args);
            final FragmentTransaction ft = mFragmentManager.beginTransaction();
            ft.replace(mContainerId, frag);
            ft.commit();
            mFragmentManager.executePendingTransactions();
        }
    }
    

    结尾

    至此关于Navigation框架的知识总结完毕,如果迭代将会持续更新,谢谢!!!

    展开全文
  • 最全面的Navigation的使用指南

    千次阅读 2019-06-11 18:45:52
    Navigation可以目前看做Google对于之前的Fragment的不满, 重新搭建的一套Fragment管理框架. 但是Navgation未来应该不仅限于Fragment导航. 并且Navigation可以和BottomNavigationView/NavigationView/Toolbar等结合...

    Navigation可以目前看做Google对于之前的Fragment的不满, 重新搭建的一套Fragment管理框架. 但是Navgation未来应该不仅限于Fragment导航.

    并且Navigation可以和BottomNavigationView/NavigationView/Toolbar等结合使用, 不再需要去写冗余代码管理Fragment.

    并且具备完善的Fragment回退栈管理.

    如果是使用Java语言我不推荐使用任何新框架了, 就自己玩自己的吧.

    ktx (除基本依赖外还包含一些Kotlin新特性的函数)

    implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0'
    implementation 'androidx.navigation:navigation-ui-ktx:2.0.0'
    复制代码

    一些常用的关键字解释

    navigation(nv): 导航, 即Navigation框架的fragment返回栈

    graph: 指一个描述返回栈关系的xml文件 (NavigationResourceFile) 也是布局编辑器用于显示图表的界面数据来源

    destination: 目标, 即在返回栈中要跳转的新页面

    pop: 弹出栈, 会弹出所有不符合目标的页面, 直至找到目标页面(默认情况不弹出目标页面也可以设置), 可以理解为Fragment的singleTask模式

    navHost: 即所有页面的容器. 类似网页中的host, 所有path路径都是在host之后跟随, host固定不变.

    XML编辑

    点击NavResourceFile中的Design即可查看布局编辑器, 布局编辑器分为三栏.

    左侧是已添加的导航, 中间是页面浏览, 中间栏的工具栏可以创建和快速添加标签以及整理页面, 右侧属性栏方便添加属性.

    navigation这是个嵌套的图表, 可以点击打开新的图表页面.

    Activity布局中

    <LinearLayout
        .../>
        <androidx.appcompat.widget.Toolbar
            .../>
        <fragment
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:id="@+id/my_nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:navGraph="@navigation/mobile_navigation"
            app:defaultNavHost="true"
            />
        <com.google.android.material.bottomnavigation.BottomNavigationView
            .../>
    </LinearLayout>
    复制代码
    android:name="androidx.navigation.fragment.NavHostFragment"  
    固定写法
    
    app:navGraph 
    指定navigation资源文件, 也可以不指定后面通过代码中动态设置
    
    app:defaultNavHost 
    是否拦截返回键事件, false表示不需要回退栈.
    复制代码

    NavigationResourceFile

    res目录创建 AndroidResourceFile 选择 Navigation. 然后 new-> NavigationResourceFile

    navigation

    app:startDestination="@+id/home_dest" 指定初始目标
    复制代码

    navigation可以嵌套navigation标签.

    在布局编辑器中会显示为

    嵌套navigation无法互相关联

    <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/nav_global"
        app:startDestination="@id/mainFragment">
    
     <!--   <action
            android:id="@+id/global_action"
            app:destination="@id/navigation" />-->
    
        <fragment
            android:id="@+id/mainFragment"
            android:name="com.example.frameexample.MainFragment"
            android:label="fragment_main"
            tools:layout="@layout/fragment_main">
            <action
                android:id="@+id/action_mainFragment_to_personInfoFragment"
                app:destination="@id/settingFragment" />
        </fragment>
    
        <navigation
            android:id="@+id/navigation"
            app:startDestination="@id/settingFragment">
            <fragment
                android:id="@+id/settingFragment"
                android:name="com.example.frameexample.SettingFragment"
                android:label="fragment_setting"
                tools:layout="@layout/fragment_setting" />
        </navigation>
    
    </navigation>
    复制代码

    上面的mainFragment无法直接app:destination="@id/settingFragment"这会导致运行错误. 只能先导航到navigation.(即NavHostFragment所在的界面)

    fragment

    android:id 不言而喻
    
    android:name 目标要实例化的fragment完全限定类名
     
    tools:layout 用于显示在布局编辑器
    
    android:label  用于后面绑定Toolbar等自动更新标题
    复制代码

    argument

    android:name="myArg"
    app:argType="integer"
    android:defaultValue="0"
    复制代码
    • 参数名称
    • 参数类型
    • 参数默认值

    在跳转导航页面的时候会自动在argument中带上参数(要求指定参数默认值). 数组和Paraclable/Serializable不支持默认值设置, 通过下面要讲的SafeArg可以在编译器校验参数类型安全问题.

    action

    动作 用于页面跳转时指定目标页面

    android:id="@+id/next_action" 
    动作id
    app:destination="@+id/flow_step_two_dest"> 
    目标页面
    app:popUpTo="@id/home_dest" 
    当前属于弹出栈
    app:popUpToInclusive="true/false" 
    弹出栈是否包含目标
    app:launchSingleTop="true/false" 
    是否开启singleTop模式
    
    app:enterAnim=""
    app:exitAnim=""
    导航动画
    
    app:popEnterAnim=""
    app:popExitAnim=""
    弹出栈动画
    复制代码

    如果从导航页面到新的Activity页面, 动画不支持. 请使用默认的Activity设置动画去支持.

    全局动作

    一般情况下NavController只能使用当前Fragment在NavXML中声明的子标签action, 但是可以通过直接给navigation标签创建子标签action实现全局动作, 即每个Fragment都能使用的动作.

    给navigation添加action子标签时要求给navigation指定熟悉android:id

    占位页面如果运行时没有指定Class并且导航到该占位页面时会抛出异常

    类关系

    涉及到的类关系

    • NavController 控制导航的跳转和弹出栈
    • NavOptions 控制跳转过程中的配置选项, 例如动画和singleTop模式
    • Navigation 工具类 创建点击事件或者获取控制器
    • NavHostFragment 导航的容器, 可以设置和获取导航图(NavGraph)
    • NavGraph 用于描述导航中页面关系的对象 可以增删改查页面,设置起始页等
    • NavigationUI 用于将导航和一系列菜单控件自动绑定的工具类
    • Navigator 页面的根接口, 如果想创建一个新的类型页面就要自定义他
    • NavDeepLinkBuilder 构建一个能打开导航页面的Intent

    NavController

    NavController用于跳转页面和参数传递等控制, 可以通过扩展函数得到实例.

    Fragment.findNavController()
    View.findNavController()
    Activity.findNavController(viewId: Int)
    复制代码

    导航

    public final void navigate (int resId)
    
    public final void navigate (int resId, 
                    Bundle args)
    
    public void navigate (int resId, 
                    Bundle args, 
                    NavOptions navOptions)
    
    public void navigate (NavDirections directions)
    
    public void navigate (NavDirections directions, 
                    NavOptions navOptions)
      
    public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions,
                           @Nullable Navigator.Extras navigatorExtras)
    
    private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
                            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras)
    
    public boolean navigateUp ()
    返回到上一个页面
    
    public boolean navigateUp (DrawerLayout drawerLayout)
    
    public boolean navigateUp (AppConfiguration appConfiguration)
    复制代码

    resId 可以是NavXML中的action或者destination标签id, 如果是action则会附带action的选项, 如果是页面destination则不会附带destination标签下的子标签action(写了白写).

    args 即需要在fragment之间传递的Bundle参数, 但是导航还支持另外一种插件形式的传递参数方式-安全参数SafeArgs, 后面提到.

    navOptions 即导航页面一些配置选项(例如动画)

    navigatorExtras 目前是用于支持转场动画的共享元素.

    ActivityNavigator和FragmentNavigator内部都实现了Navigator.Extras

    通过扩展函数可以快速创建

    fun FragmentNavigatorExtras(vararg sharedElements: Pair<View, String>) =
            FragmentNavigator.Extras.Builder().apply {
                sharedElements.forEach { (view, name) ->
                    addSharedElement(view, name)
                }
            }.build()
    
    fun ActivityNavigatorExtras(activityOptions: ActivityOptionsCompat? = null, flags: Int = 0) =
            ActivityNavigator.Extras.Builder().apply {
                if (activityOptions != null) {
                    setActivityOptions(activityOptions)
                }
                addFlags(flags)
            }.build()
    
    复制代码

    可以从源码看到内部都是使用的Extras.Builder构造器创建的.

    示例

        <fragment
            android:id="@+id/home_dest"
            android:name="com.example.android.codelabs.navigation.HomeFragment"
            android:label="@string/home"
            tools:layout="@layout/home_fragment">
    
            <action
                android:id="@+id/next_action"
                app:destination="@+id/flow_step_one_dest"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right" />
    
        </fragment>
    复制代码

    上面你给action指定动画, 但是如果你使用的navigate中的参数resId不是R.id.next_action而是R.id.home_dest. 那么你这个action相当于不生效.

    弹出栈, 即从Nav回退栈中清除Fragment.

    public boolean popBackStack (int destinationId,  // 目标id
                    boolean inclusive) // 是否包含参数目标
      
    public boolean popBackStack ()
    弹出当前Fragment
    复制代码

    监听导航

    public void addOnDestinationChangedListener (NavController.OnDestinationChangedListener listener)
    public void removeOnDestinationChangedListener (NavController.OnDestinationChangedListener listener)
    复制代码

    回调

        public interface OnDestinationChangedListener {
            /**
             * 导航完成以后回调函数(但是可能动画还在播放中)
             *
             * @param 控制导航到目标的导航控制器NavController
             * @param 目标页面
             * @param 导航到目标页面的参数
             */
            void onDestinationChanged(@NonNull NavController controller,
                    @NonNull NavDestination destination, @Nullable Bundle arguments);
        }
    复制代码

    状态保存和恢复

    public Bundle saveState ()
    public void restoreState (Bundle navState)
    复制代码
    public NavDestination getCurrentDestination ()
    
    public NavGraph getGraph ()
    public void setGraph (int graphResId)
    public void setGraph (NavGraph graph)
    
    public NavigatorProvider getNavigatorProvider ()
    
    public NavDeepLinkBuilder createDeepLink ()
    创建一个打开当前页面的深层链接构造器
    复制代码

    NavigatorProvider是一个提供者, 可以添加和查询Navigator.

    NavOptions

    属于导航时的附加选项设置

    相当于代码动态实现了NavigationResourceFile中的<action>标签的属性设置(但是无法指定目标).

    目前功能只有设置动画和singleTop(启动模式), popUp(弹出栈)

    提供一个DSL作用域

    fun navOptions(optionsBuilder: NavOptionsBuilder.() -> Unit): NavOptions
    复制代码

    示例

    val options = navOptions {
      anim {
        enter = R.anim.slide_in_right // 进入页面动画
        exit = R.anim.slide_out_left
        popEnter = R.anim.slide_in_left  // 弹出栈动画
        popExit = R.anim.slide_out_right
      }
    
      launchSingleTop = true
      popUpTo = R.id.categoryFragment
    }
    
    findNavController().navigate(R.id.flow_step_one_dest, null, options)
    复制代码

    如果是跳转到新的Activity当前设置的动画都不支持

    弹出栈

    public NavOptions.Builder setPopUpTo (int destinationId, 
                    boolean inclusive)
    复制代码

    NavOptions这个函数和NavController有所区别, 他并不会决定目的地, 只是在导航到目的地之前先执行一个弹出栈指令.

    场景: 例如我现在购买一个商品支付成功, 这个时候我要将前面的商品详情; 订单配置等页面全部关闭 然后进入<支付成功>页面

    顺便说下在之前的做法是发送事件然后finish

    Navigation

    工具类

    目前只支持创建点击事件和获取控制器

    public static View.OnClickListener createNavigateOnClickListener (int resId)
    public static View.OnClickListener createNavigateOnClickListener (int resId, 
                    Bundle args)
    快速创建一个跳转到目标的View.OnClickListenner
    
    
    public static NavController findNavController (Activity activity, 
                    int viewId)                
    public static NavController findNavController (View view)
    public static void setViewNavController (View view, 
                    NavController controller)
    以上都可以使用Kotlin扩展函数获取
    复制代码

    NavHostFragment

    该对象为Navigation提供一个容器

    一般使用情况是在布局中直接定义, 但是也可以通过代码构建实例, 然后通过代码创建视图(例如ViewPager等)

    public static NavHostFragment create (int graphResId)
    复制代码
    public static NavController findNavController (Fragment fragment)
    这个函数实际上就是findNavController()扩展函数的实际执行函数
    复制代码

    NavigationUI

    该工具类负责绑定视图控件和导航, 所有绑定都只需要id对应即可自动导航.

    设置导航到新页面时自动更新标题文字

    fun AppCompatActivity.setupActionBarWithNavController(
        navController: NavController,
        drawerLayout: DrawerLayout?
    ) 
    
    fun AppCompatActivity.setupActionBarWithNavController(
        navController: NavController,
        configuration: AppBarConfiguration = AppBarConfiguration(navController.graph)
    )
    复制代码

    这里出现个参数AppBarConfiguration, 用于配置Toolbar/ActionBar/CollapsingToolbarLayout.

    构造器模式使用Builder创建实例

    AppBarConfiguration.Builder(NavGraph navGraph)
    顶级目标是NavGraph的起始页面
    
    AppBarConfiguration.Builder(Menu topLevelMenu)
    菜单包含的全部是顶级目标
    
    AppBarConfiguration.Builder(int... topLevelDestinationIds)
    顶级目标集合
    AppBarConfiguration.Builder(Set<Integer> topLevelDestinationIds)
    复制代码

    顶级目标: 顶级目标即表示为回退栈最底位置, 无法再返回, 故可以理解为不需要返回键导航的页面(Toolbar等就不会显示返回箭头).

    函数

    AppBarConfiguration.Builder setDrawerLayout(DrawerLayout drawerLayout)
    绑定Toolbar同时绑定一个DrawerLayout联动
    
    AppBarConfiguration.Builder setFallbackOnNavigateUpListener(AppBarConfiguration.OnNavigateUpListener fallbackOnNavigateUpListener)
    
    AppBarConfiguration  build()
    复制代码

    AppBarConfiguration.OnNavigateUpListener 该回调接口会在每次点击向上导航时回调

    public interface OnNavigateUpListener {
    /**
    * 回调处理向上导航
    *
    * @return 返回true表示向上导航, false不处理
    */
    boolean onNavigateUp();
    }
    复制代码

    Toolbar也可以绑定Nav自动更新对应页面的标题

    fun Toolbar.setupWithNavController(
        navController: NavController,
        drawerLayout: DrawerLayout?
    ) {
        NavigationUI.setupWithNavController(this, navController,
            AppBarConfiguration(navController.graph, drawerLayout))
    }
    
    fun Toolbar.setupWithNavController(
        navController: NavController,
        drawerLayout: DrawerLayout?
    ) {
        NavigationUI.setupWithNavController(this, navController,
            AppBarConfiguration(navController.graph, drawerLayout))
    }
    复制代码

    CollapsingToolbarLayout

    fun CollapsingToolbarLayout.setupWithNavController(
        toolbar: Toolbar,
        navController: NavController,
        configuration: AppBarConfiguration = AppBarConfiguration(navController.graph)
    )
    
    fun CollapsingToolbarLayout.setupWithNavController(
        toolbar: Toolbar,
        navController: NavController,
        drawerLayout: DrawerLayout?
    )
    复制代码

    绑定菜单条目点击自动导航

    fun MenuItem.onNavDestinat ionSelected(navController: NavController): Boolean =
            NavigationUI.onNavDestinationSelected(this, navController)
    复制代码

    在onOptionsItemSelected

        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
        }
    复制代码

    Nav绑定NavigationView菜单

    
    
    fun NavigationView.setupWithNavController(navController: NavController) {
        NavigationUI.setupWithNavController(this, navController)
    }
    
    fun BottomNavigationView.setupWithNavController(navController: NavController) {
        NavigationUI.setupWithNavController(this, navController)
    }
    复制代码

    DeepLink

    Nav声明一个DeepLink(深层链接)只需要给Fragment添加一个子标签即可

    首先要在AndroidManifest中的activity中添加一个子标签nav-graph为NavRes注册DeepLink.

    <activity>
    	<nav-graph android:value="@navigation/mobile_navigation" />
    </activity>
    复制代码

    深层链接

    <deepLink app:uri="www.example.com/{myarg}" />
    复制代码

    通过ADB测试

    adb shell am start -a android.intent.action.VIEW -d "http://www.example.com/2334456"
    复制代码

    2334456即传递过去的参数

    {}包裹的字段属于变量, *可以匹配任意字符

    通过NavDeepLinkBuilder创建DeepLink Intent

    NavDeepLinkBuilder	setArguments(Bundle args)
    
    NavDeepLinkBuilder	setDestination(int destId)
    
    NavDeepLinkBuilder	setGraph(int navGraphId)
    
    NavDeepLinkBuilder	setGraph(NavGraph navGraph)
    复制代码

    生成PendingIntent可以用于开启界面(例如传给Notification)

    PendingIntent	createPendingIntent()
    
    TaskStackBuilder	createTaskStackBuilder()
    复制代码

    SafeArgs

    安全类型插件, 基于Gradle实现的插件.

    他的目的就是根据你在NavRes中声明argument标签生成工具类, 然后全部使用工具类而不是字符串去获取和设置参数. 避免前后两者参数类型不一致而崩溃.

    插件

    buildscript {
        repositories {
            jcenter()
            google()
        }
        dependencies {
            classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha01'
        }
    }
    复制代码

    应用插件

    apply plugin: 'androidx.navigation.safeargs'
    复制代码

    在NavigationResourceFile中声明<argument>标签后会自动生成类

    插件会根据页面自动生成*Directions类, 该类包含该页面能使用的所有跳转动作(包含全局动作和自身动作).

    生成类会包含一个有规则的静态函数用于获取Directions的实现类(*Directions的静态内部类), 函数名称规则为

    action<页面名称>To<目标页面名称>
    
    全局动作名称规则则为: 动作id的变量命名法
    
    例: public static ActionMainFragmentToPersonInfoFragment actionMainFragmentToPersonInfoFragment()
    复制代码

    这里看下NavDirections接口的含义

    public interface NavDirections {
    
        /**
         * 返回动作id
         *
         * @return id of an action
         */
        @IdRes
        int getActionId();
    
        /**
         * 返回目标参数
         */
        @NonNull
        Bundle getArguments();
    }
    复制代码

    可以总结为 包含携带参数和动作.

    但是如果navRes中还包含<argument>标签, 则还会生成对应的*Args类, 并且上面提到的自动生成的*Directions中的NavDirection静态内部类还会生成参数的构造和访问器

    还有一系列 hashCode/equals/toString 函数

    完整的导航页面且传递数据写法

    导航至目标页面

    val action =
    MainFragmentDirections.actionMainFragmentToPersonInfoFragment().setName("设计师吴彦祖")
    
    findNavController().navigate(action)
    复制代码

    在目标页面接受数据

    tv.text = PersonInfoFragmentArgs.fromBundle(arguments!!).name
    复制代码

    这里可以再次想下什么是安全类型参数.

    总结

    关于Google推动的SingleActivity构建应用我说下我的看法, 我认为整个应用使用一个Activity还是比较麻烦的.

    列举下所谓麻烦

    1. Fragment无法设置默认动画, 动画统一管理起来很麻烦
    2. 所谓Fragment减少内存开销用户都无法感知
    3. 很多框架还是基于Activity实现的(例如路由,状态栏), 可能某些项目架构会受到局限

    我认为Navigation替代FragmentManger还是得心应手的, 并且导航图看起来也很有逻辑感.

    转载于:https://juejin.im/post/5ccd38fd6fb9a031f0380b2b

    展开全文
  • Android Navigation 详解

    千次阅读 2019-08-18 22:01:19
    Navigation用于 Fragment的管理。他可以让Fragrant之间的切换,拥有像Activity间一样的跳转。与 DrawerLayout(抽屉式布局)、ActionBar(导航栏)等有简洁完美的对接。 二、开发环境设置 注意:Navigation需要在...

    一、导航概述

        Navigation 用于 Fragment 的管理。他可以让 Fragrant 之间的切换,拥有像 Activity 间一样的跳转。与 DrawerLayout(抽屉式布局)、ActionBar(导航栏)等有简洁完美的对接。

    二、开发环境设置

    注意:Navigation 需要在 Android Studio 3.3 或更高版本中才可使用。(并且在 androidx 中支持的更好,之前没有使用 androidx 一直无法添加 Fragment 目的地)

    若要在项目中包含导航支持,请将以下依赖项添加到应用程序的 build.gradle 文件中:

    dependencies {
      def nav_version = "2.1.0-rc01"
    
      // Java
      implementation "androidx.navigation:navigation-fragment:$nav_version"
      implementation "androidx.navigation:navigation-ui:$nav_version"
    
      // Kotlin
      implementation "androidx.navigation:navigation-fragment-ktx:$nav_dep"
      implementation "androidx.navigation:navigation-ui-ktx:$nav_dep"
    
    }

    三、创建 navigation graph (导航图)

      导航图是一个 XML 资源文件,在导航图中有两个概念:

        1.Destination:导航图中的每一个导航被称为 Destination (目的地)。

        2.Action:导航与导航之间使用 Action (事件) 连接,用于说明两个导航之间的跳转关系。

    若要向项目添加导航图,请执行以下操作:

        在项目的 res 目录下,新建一个 navigation 文件夹,右键 navigation 文件夹,依次选择:NewNavigation resource file

        填写文件名称,如:nav_graph,点击 OK。

    四、编辑导航图

        在编辑导航图前,需要先创建 Activity,与相关的 Fragment。(此处使用 1 个 Activity,三个 Fragment)

    /**
     * @Author: Eli Shaw
     * @Date: 2019-08-18 10:10:42
     * @Description: Activity 容器类
     */
    class NavigationActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_navigation)
        }
    }
    /**
     * @Author: Eli Shaw
     * @Date: 2019-08-18 10:07:13
     * @Description: 登录页
     */
    class LoginFragment : Fragment(), View.OnClickListener {
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            // Inflate the layout for this fragment
            val viewRoot = inflater.inflate(R.layout.fragment_login, container, false)
            return viewRoot
        }
    }
    /**
     * @Author: Eli Shaw
     * @Date: 2019-08-18 10:08:52
     * @Description: 注册页
     */
    class RegisterFragment : Fragment(), View.OnClickListener {
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            // Inflate the layout for this fragment
            val viewRoot = inflater.inflate(R.layout.fragment_register, container, false)
            return viewRoot
        }
    }
    /**
     * @Author: Eli Shaw
     * @Date: 2019-08-18 10:40:42
     * @Description: 首页
     */
    class HomeFragment : Fragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.fragment_home, container, false)
        }
    }

        创建好 nav_graph 导航图后,双击进行编辑(与常规布局文件很相似)

    1、指定 Activity 容器

        在 Activity 布局文件中指定导航图的容器

    <LinearLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".navigation.NavigationActivity">
    
        <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:navGraph="@navigation/nav_graph"/>
    </LinearLayout>

        android:name:指定 Fragment 的类型为 NavHostFragment。

        app:defaultNavHost="true":让 Navigation 容器处理返回事件,在 Navigation 容器中如果有页面的跳转,点击返回按钮会先处理 容器中 Fragment 页面间的返回,处理完容器中的页面,再处理 Activity 页面的返回。如果值为 false 则直接处理 Activity 页面的返回。

        app:navGraph:指定 Navigation 文件。

    2、添加 Action (事件)

        拖动选中的(目的地)右方的圆点到另一个(目的地)即可创建一个 Action(事件)。

        点击左下角的 Text ,将显示如下代码

    <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/nav_graph"
                app:startDestination="@id/loginFragment">
    
        <!--登录 Fragment 包含两个事件:一个是注册页,一个是首页 -->
        <fragment android:id="@+id/loginFragment" android:name="cn.eli.jetpack.navigation.LoginFragment"
                  android:label="fragment_login" tools:layout="@layout/fragment_login">
            <action android:id="@+id/actionLoginToRegister" app:destination="@id/registerFragment"/>
            <action android:id="@+id/actionLoginToHome" app:destination="@id/homeFragment"/>
        </fragment>
    
        <!--注册 Fragment 包含一个事件:返回到登录页-->
        <fragment android:id="@+id/registerFragment" android:name="cn.eli.jetpack.navigation.RegisterFragment"
                  android:label="fragment_register" tools:layout="@layout/fragment_register">
            <action android:id="@+id/actionRegisterToLogin" app:destination="@id/loginFragment"/>
        </fragment>
    
        <!--首页 Fragment 没有事件-->
        <fragment android:id="@+id/homeFragment" android:name="cn.eli.jetpack.navigation.HomeFragment"
                  android:label="fragment_home" tools:layout="@layout/fragment_home"/>
    </navigation>

    3、导航图的复用

        <navigation> 是导航图的根元素,Destination (目的地) 与 Action (事件) 都是 <navigation> 元素中的节点。

        <navigation> 中可以包含 <navigation> 节点,这样使用的目的是可以复用相同的导航图。例如:增加一个找回密码页,这个找回密码页在另一个导航图中,找回密码后再继续登录操作:

        此图是一个包含导航图的预览页,文本内容如下

    <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/nav_multiplex"
                app:startDestination="@id/findPwdFragment">
    
        <fragment android:id="@+id/findPwdFragment" android:name="cn.eli.jetpack.navigation.FindPwdFragment"
                  android:label="fragment_find_pwd" tools:layout="@layout/fragment_find_pwd">
            <action android:id="@+id/action_findPwdFragment_to_nav_graph" app:destination="@id/nav_graph"/>
        </fragment>
    
        <!--引入另一个导航图-->
        <include app:graph="@navigation/nav_graph"/>
    
    </navigation>

    五、导航图间的跳转

    1、获取 NavController 的方式

        导航图使用 NavController 控制(目的地)之间的跳转,在 Activity,Fragment 中获取 NavController 有以下 6 种方式:

    1.Fragment.findNavController()
    2.View.findNavController()
    3.Activity.findNavController(viewId: Int) //只有 Activity 中可以使用
    4.NavHostFragment.findNavController(Fragment)
    5.Navigation.findNavController(Activity, @IdRes int viewId) //只有 Activity 中可以使用
    6.Navigation.findNavController(View)

    2、导航中目的地间的传值

        导航中目的地间也可以使用 Bundle 进行传值

    //使用 bundle 传值
    val bundle = Bundle()
    bundle.putInt(ARG_NAV, navController)
    
    findNavController().navigate(R.id.actionLoginToRegister, bundle)

    下面是登录页、注册页完整代码

    private const val ARG_NAV = "nav"
    
    /**
     * @Author: Eli Shaw
     * @Date: 2019-08-18 10:07:13
     * @Description: 登录页
     */
    class LoginFragment : Fragment(), View.OnClickListener {
    
        private var navController: Int = 0
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            arguments?.let {
                navController = it.getInt(ARG_NAV)
            }
        }
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            // Inflate the layout for this fragment
            val viewRoot = inflater.inflate(R.layout.fragment_login, container, false)
            //注册点击事件
            viewRoot.findViewById<Button>(R.id.btnRegister).setOnClickListener(this)
            //登录点击事件
            viewRoot.findViewById<Button>(R.id.btnLogin).setOnClickListener {
                findNavController().navigate(R.id.actionLoginToHome)
            }
            return viewRoot
        }
    
        /**
         * 注册点击事件
         */
        override fun onClick(view: View) {
            if (++navController > 4)
                navController = 1
    
            //使用 bundle 传值
            val bundle = Bundle()
            bundle.putInt(ARG_NAV, navController)
    
            //根据 navController 值的不同,使用不同的获取 NavController 的方式
            when (navController) {
                //Fragment.findNavController()
                1 -> findNavController().navigate(R.id.actionLoginToRegister, bundle)
                //View.findNavController()
                2 -> view.findNavController().navigate(R.id.actionLoginToRegister, bundle)
                //NavHostFragment.findNavController(Fragment)
                3 -> NavHostFragment.findNavController(this).navigate(R.id.actionLoginToRegister, bundle)
                //Navigation.findNavController(View)
                4 -> Navigation.findNavController(view).navigate(R.id.actionLoginToRegister, bundle)
            }
        }
    }
    
    private const val ARG_NAV = "nav"
    
    /**
     * @Author: Eli Shaw
     * @Date: 2019-08-18 10:08:52
     * @Description: 注册页
     */
    class RegisterFragment : Fragment(), View.OnClickListener {
    
        private var navController: Int = 0
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            arguments?.let {
                navController = it.getInt(ARG_NAV)
            }
        }
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            // Inflate the layout for this fragment
            val viewRoot = inflater.inflate(R.layout.fragment_register, container, false)
            viewRoot.findViewById<Button>(R.id.btnBackLogin).setOnClickListener(this)
            return viewRoot
        }
    
        override fun onClick(view: View) {
            val bundle = Bundle()
            bundle.putInt(ARG_NAV, navController)
            
            view.findNavController().navigate(R.id.actionRegisterToLogin, bundle)
        }
    }

    3、导航中目的地间的转场动画

    自定义转场动画

    cut_to_enter.xml (入场动画)

    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
                android:toYDelta="0"
                android:fromYDelta="100%"
                android:duration="1500"/>
    </set>

    cut_to_exit.xml(出场动画)

    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
                android:duration="1500"
                android:fromYDelta="0"
                android:toYDelta="-100%"/>
    </set>

    在导航图中编辑

    展开全文
  • Navigation简单使用

    2020-07-14 00:26:26
    Navigation简单使用 针对Jitpack 组件中 Navigation的简单使用说明。 添加依赖 需要在对应模块层级的build.gradle文件中添加依赖: //版本指定为当时的最新稳定版本 ext.navigationVersion = "2.0.0" dependencies {...

    Navigation简单使用

    针对Jitpack 组件中 Navigation的简单使用说明。

    添加依赖

    需要在对应模块层级的build.gradle文件中添加依赖:

    //版本指定为当时的最新稳定版本
    ext.navigationVersion = "2.0.0"
    dependencies {
        //... 
        implementation "androidx.navigation:navigation-fragment-ktx:$rootProject.navigationVersion"
        implementation "androidx.navigation:navigation-ui-ktx:$rootProject.navigationVersion"
    }
    

    创建布局文件

    在需要使用navigation的布局中,创建对应的xml资源文件。其中Navigation中最重要的组成部分有三个。

    NavHostFragment

    navigation组件生效的fragment容器,指定NavHostFragmentNavigation才能实现后续的效果。

    <androidx.constraintlayout.widget.ConstraintLayout
        ...>
    
        <fragment
            android:id="@+id/my_nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:navGraph="@navigation/login_navigation"
            app:defaultNavHost="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    这里有几个属性需要进行说明:

    属性 说明 备注
    android:name 值必须是androidx.navigation.fragment.NavHostFragment,声明这是一个NavHostFragment 可以自己实现NavHostFragment
    app:navGraph 存放的是第二步建好导航的资源文件,也就是确定了NavigationGraph
    app:defaultNavHost 是否与系统的返回按钮相关联,默认true,表示关联

    NavigationGrap

    navigation事件的描述文件。

    1. 创建基础目录:资源文件res目录下创建navigation目录 -> 右击navigation目录New一个Navigation resource file
    2. 创建一个Destination,如果说Navigation是我们的导航工具,Destination是我们的目的地,
    <navigation
        ...>
    
        <fragment
            ...
            />
    
        <fragment
            android:id="@+id/welcome"
            >
            <action
                android:id="@+id/action_welcome_to_login"
                app:destination="@id/login"/>
            <action
                android:id="@+id/action_welcome_to_register"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:destination="@id/register"/>
        </fragment>
    
        <fragment
            android:id="@+id/register"
            ...
            >
    
            <argument
                android:name="EMAIL"
                android:defaultValue="2005@qq.com"
                app:argType="string"/>
        </fragment>
    </navigation>
    

    NavController

    实现NavigationGrap中的事件。参考NavigationGrap中的事件,在对应的app:startDestination:xxFragment中实现这些事件。

    更多

    Navigation可以绑定menusdrawersbottom navigation,这里我们以bottom navigation为例。

    布局文件:

    <LinearLayout
        ...>
    
        <fragment
            android:id="@+id/my_nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            app:navGraph="@navigation/main_navigation"
            app:defaultNavHost="true"
            android:layout_height="0dp"
            android:layout_weight="1"/>
    
        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/navigation_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            app:itemIconTint="@color/colorAccent"
            app:itemTextColor="@color/colorPrimary"
            app:menu="@menu/menu_main"/>
    
    </LinearLayout>
    

    XxActivity中的处理:

    class MainActivity : AppCompatActivity() {
    
        lateinit var bottomNavigationView: BottomNavigationView
    
        override fun onCreate(savedInstanceState: Bundle?) {
            //...
            val host: NavHostFragment = supportFragmentManager.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment
            val navController = host.navController
            initWidget()
            initBottomNavigationView(bottomNavigationView,navController)
        }
    
        private fun initBottomNavigationView(bottomNavigationView: BottomNavigationView, navController: NavController) {
            bottomNavigationView.setupWithNavController(navController)
        }
    
        private fun initWidget() {
            bottomNavigationView = findViewById(R.id.navigation_view)
        }
    }
    
    展开全文
  • 手把手教你使用Android官方组件Navigation

    万次阅读 热门讨论 2018-06-01 18:52:22
    Google 2018 I/O大会上,谷歌隆重推出一个新的架构组件:Navigation。从名字上可以看出他是一个导航,其实我们可以把它理解为FragmentNavigation。它提供了多Fragment之间的转场,栈管理,帮助你更轻松的使用Fragment。...
  • Navigation 详解二

    2019-04-20 11:03:25
    配合 BottomNavigationView 创建 Bottom Navigation Activity BottomNavigationView 以更简洁的方式来实现过去的 BottomNavigationBar 的样式。Android Studio 中创建一个 Bottom Navigation Activity,自动生成 ...
  • Navigation

    2018-05-30 10:11:53
    Navigation
  • 之前Activity和Fragment的跳转代码样式化且易出错,通过使用Jetpack中navigation组件,开发人员可以更直观的维护界面跳转。 使用时需要引入依赖 implementation "androidx.navigation:navigation-fragment-ktx:$r.....
  • Jetpack Navigation页面导航

    千次阅读 2020-08-18 10:51:18
    Jetpack Navigation页面导航 简介 该组件可以实现用户界面跳转、转场动画以及安全的参数传递等功能。无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。还可以使用 Android Studio ...
  • 在VUE中路由遇到Error: Avoided redundant navigation to current location:报错显示是路由重复, 虽然对项目无影响,但是看到有红的还是不舒服。 于是查了一下发现可以这样解决 在你引入VueRouter的时候再加上一句...
  • BottomNavigationView更换底部选中图标

    万次阅读 2018-03-01 21:18:36
    navigationView.setItemIconTintList(null); 2.选中时替换icon: private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNaviga...
  • BottomNavigationView设置选中

    千次阅读 2017-12-19 15:07:31
    BottomNavigationView的设计非常蛋疼(因为用的是Menu实现),没有诸如setSelectItem(int i) 这样的方法,所以就会导致设置BottomNavigationView选中状态老麻烦了。 下面是我的解决方法: navigation....
  • 使用react-navigation时,单页面设置navigationOptions中,进行Static中调用方法,不能像以下设置 onPress = {()=>this.navigatePress()} static navigationOptions = ({navigation, screenProps}) => ({ ...
  • 在VUE中路由遇到Error: Avoided redundant navigation to current location:报错显示是路由重复 解决:router文件夹下面的index.js中加上下面几句代码,搞定 // 解决ElementUI导航栏中的vue-router在3.0版本...
  • Android Studio自带的BottomNavigationView项目设置了noActionbar这个主题就会闪退,不设置noActionbar的话就无法使用toolbar,蛋疼。 下面我会两种BottomNavigationView的实现方法: 一种是Android Studio自带的...
  • vue-navigation一个原生路由插件

    千次阅读 2018-11-19 10:36:40
    github:https://github.com/zack24q/vue-navigation     需要 vue 2.x 与 vue-router 2.x。     导航默认行为类似手机APP的页面导航(A、B、C为页面): A前进到B,再前进到C; C返回到B时,B会...
  • npm install --save react-navigation 2.在需要用的页面 导入 import {StackNavigator} from 'react-navigation'; 3.使用 import Addfrom './Add'; import Delete from './Delete' const ...
  • 修改NavigationView中的Item的Icon大小

    万次阅读 2015-12-04 14:59:41
    嘿嘿嘿。闲着没事逛了逛 stackoverflow,发现有人问及这个问题: ...如何改变默认的NavigationView中的左侧icon大小?嘿嘿,看到没人回
  • google最新在android 5.2中推出的新控件NavigationView可以轻松实现侧拉菜单栏的效果,具体实现在网络上有很多文章描述,本文主要讲在设置菜单项选中效果中要注意的细节。 首先在布局文件中,NavigationView控件的...
  • 先上图: ...原文章链接:BottomNavigationView从入门到强行改造,取消位移动画?和ViewPager绑定?添加Badge?   原文章链接(详细用法):BottomNavigationViewEx     BadgeView使用参考...
1 2 3 4 5 ... 20
收藏数 125,721
精华内容 50,288
关键字:

navigation