Navigation学习【一】——简单使用

Navigation的简单使用Demo

导航概述

导航组件由三个关键部分组成,它们相互协调工作。他们是:

  • 导航图(新XML资源)-这是一种资源,它在一个集中位置包含所有与导航有关的信息。这包括应用程序中的所有位置,称为destinations,以及用户可以通过您的应用程序选择的可能路径。
  • NavHostFragment(布局XML视图)-这是添加到布局中的特殊小部件。它从您的导航图显示不同的目的地。
  • NavController(Kotlin / Java对象)-这是一个跟踪导航图中当前位置的对象。NavHostFragment当您浏览导航图时,它会在中协调交换目标内容。

导航时,将使用NavController对象,在“导航图”中告诉它您想去的地方或要走的路径。然后,NavController它将在NavHostFragment中显示适当的目的地。

注意:如果要使用导航和 Android Studio,则必须使用 Android Studio 3.3 或更高版本。

导入

获取最新版本

appbuild.gradle 中添加依赖:

dependencies {
  def nav_version = "2.1.0"

  // Java
  implementation "androidx.navigation:navigation-fragment:$nav_version"
  implementation "androidx.navigation:navigation-ui:$nav_version"

  // Kotlin
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

}

使用

  1. 创建导航图nav_graph.xml

    在“Project”窗口中,右键点击 res 目录,然后依次选择 New > Android Resource File。此时系统会显示 New Resource File 对话框。在 File name 字段中输入名称,例如“nav_graph”。从 Resource type 下拉列表中选择 Navigation,然后点击 OK。

nav_graph.png
  1. Activity 添加 NavHost
<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!-- 添加导航activity -->
    <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"/>
</androidx.constraintlayout.widget.ConstraintLayout>

下面对三个比较重要的属性做一下解释:

  • android:name 属性包含 NavHost 实现的类名称。
  • app:navGraph 属性NavHostFragment 与导航图相关联。导航图会在此 NavHostFragment 中指定用户可以导航到的所有目的地。
  • app:defaultNavHost="true" 属性确保您的 NavHostFragment拦截系统返回按钮。请注意,只能有一个默认 NavHost 。如果同一布局(例如,双窗格布局)中有多个主机,请务必仅指定一个默认 NavHost
  1. 设置起始目的地

    res/navigation/nav_graph.xml中通过startDestination设置起始fragment,当然这里也可以通过design模式通过点击相关图标去设置,这里不好演示就通过展示代码来实现。

    <!-- nav_graph.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/nav_graph"
        app:startDestination="@id/firstFragment">
    
        <fragment
            android:id="@+id/firstFragment"
            android:name="com.yu.hu.demotest.FirstFragment"
            android:label="fragment_first"
            tools:layout="@layout/fragment_first" />
    
       
    </navigation>
    

ok,到了这里项目就可以正常运行了。

简单跳转

  1. res/navigation/nav_graph.xml中设置跳转Action

    <!-- nav_graph.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/nav_graph"
        app:startDestination="@id/firstFragment">
    
        <fragment
            android:id="@+id/firstFragment"
            android:name="com.yu.hu.demotest.FirstFragment"
            android:label="fragment_first"
            tools:layout="@layout/fragment_first" >
            
            <!-- 设置跳转至secondFragment的action 
             id  -- action的id
             destination -- 设置目的地
                enterAnim|exitAnim|popEnterAnim|popExitAnim 为相应的切换动画
             -->
            <action
                android:id="@+id/action_firstFragment_to_secondFragment"
                app:destination="@id/secondFragment"
                app:enterAnim="@anim/nav_default_enter_anim"
                app:exitAnim="@anim/nav_default_exit_anim"
                app:popEnterAnim="@anim/nav_default_pop_enter_anim"
                app:popExitAnim="@anim/nav_default_pop_exit_anim" />
        </fragment>
    
        <fragment
            android:id="@+id/secondFragment"
            android:name="com.yu.hu.demotest.SecondFragment"
            android:label="fragment_second"
            tools:layout="@layout/fragment_second" />
    </navigation>
    
  2. 使用Navigation实现跳转逻辑

    核心代码:Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);

    //FirstFragment.java
    public class FirstFragment extends Fragment {
    
        private View mView;
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            mView = inflater.inflate(R.layout.fragment_first, container, false);
            return mView;
        }
    
        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
    
            Button jumpBtn = mView.findViewById(R.id.jump_btn);
            jumpBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    NavController navController = Navigation.findNavController(mView);
                    //action_firstFragment_to_secondFragment 之前设置的action id
                    navController.navigate(R.id.action_firstFragment_to_secondFragment);
                }
            });
    
            //对于点击事件还可以这么做
            //jumpBtn.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_firstFragment_to_secondFragment));
        }
    }
    

传递参数

  1. res/navigation/nav_graph.xml设置所需参数

    <!-- nav_graph.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/nav_graph"
        app:startDestination="@id/firstFragment">
    
        ...
    
        <fragment
            android:id="@+id/secondFragment"
            android:name="com.yu.hu.demotest.SecondFragment"
            android:label="fragment_second"
            tools:layout="@layout/fragment_second" >
    
            <!-- 
             设置此fragment所需参数
             name  -- name
             argType -- 参数类型
             nullable  -- 是否可为空
             defaultValue  -- 默认值
                 -->
            <argument
                android:name="content"
                app:argType="string"
                app:nullable="true"
                android:defaultValue="second_fragment" />
        </fragment>
    </navigation>
    

Navigation 库支持以下参数类型:

类型 app:argType 语法 是否支持默认值? 是否支持 null 值?
整数 app:argType="integer"
浮点数 app:argType="float"
长整数 app:argType="long" 是 - 默认值必须始终以“L”后缀结尾(例如“123L”)。
布尔值 app:argType="boolean" 是 -“true”或“false”
字符串 app:argType="string"
资源引用 app:argType="reference" 是 - 默认值必须为“@resourceType/resourceName”格式(例如,“@style/myCustomStyle”)或“0”
自定义 Parcelable app:argType="<type>",其中 <type> 是 Parcelable 的完全限定类名称 支持默认值“@null”。不支持其他默认值。
自定义 Serializable app:argType="<type>",其中 <type> 是 Serializable 的完全限定类名称 支持默认值“@null”。不支持其他默认值。
自定义 Enum app:argType="<type>",其中 <type> 是 Enum 的完全限定名称 是 - 默认值必须与非限定名称匹配(例如,“SUCCESS”匹配 MyEnum.SUCCESS)。
  1. 启动并传参

    • 不使用Safe Args

      //发送方通过bundle存储数据并通过navigate方法传递
      Button jumpBtn = mView.findViewById(R.id.jump_btn);
      jumpBtn.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              NavController navController = Navigation.findNavController(mView);
              //action_firstFragment_to_secondFragment 之前设置的action id
              Bundle bundle = new Bundle();
              bundle.putString("content","from firstFragment");
              navController.navigate(R.id.action_firstFragment_to_secondFragment,bundle);
          }
      });
      
      //接收方:
      TextView textView = mView.findViewById(R.id.second_tv);
      textView.setText(getArguments().getString("content"));
      
  • 使用 Safe Args

    1. 请在顶级 build.gradle 文件中包含以下 classpath

       buildscript {
           repositories {
               google()
           }
           dependencies {
               def nav_version = "2.1.0"
               classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
           }
       }
      
    2. 将以下行添加到应用或模块build.gradle 文件中:

      #java
      apply plugin: "androidx.navigation.safeargs"
      
      #kotlin
      apply plugin: "androidx.navigation.safeargs.kotlin"
      
    3. 使用:

      //发送方  通过自动生成的xxDirections.action去构建
      FirstFragmentDirections.ToSecond toSecond = FirstFragmentDirections.toSecond("from FirstFragment")
                              /*.setContent("sss")*/;  //如果参数可为空可通过set方法添加
      Navigation.findNavController(mView).navigate(toSecond);
      
      //接收方
      TextView textView = mView.findViewById(R.id.second_tv);
      String content = SecondFragmentArgs.fromBundle(getArguments()).getContent();
      textView.setText(content);
      

混淆

  • 通过 @keep 注解:

    @Keep public class ParcelableArg implements Parcelable { ... }
    
  • 使用keepnames规则

    ...
    
        -keepnames class com.path.to.your.ParcelableArg
        -keepnames class com.path.to.your.SerializableArg
        -keepnames class com.path.to.your.EnumArg
    
        ...
    

补充

  • Activity中获取NavController
//activity中获取NavController  第二个参数为navigation的id
NavController navController = Navigation.findNavController(this, R.id.nav_graph);
navController.handleDeepLink(new Intent());

参考文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342