Android架构之Navigation组件(一)

Jetpack组件系列文章
Android架构之LifeCycle组件
Android架构之Navigation组件(一)
Android架构之Navigation组件(二)
Android架构之Navigation组件(三)
Android架构之Navigation组件(四)
Android架构之ViewModel组件
Android架构之LiveData组件
Android架构之Room组件(一)
Android架构之Room组件(二)
Android架构之WorkManager组件
Android架构之DataBinding(一)
Android架构之DataBinding(二)
Android架构之Paging组件(一)
Android架构之Paging组件(二)
Jetpack与MVVM架构

Navigation介绍

Navigation是Jetpack中的一个组件,支持用户导航、进入和退出应用中不同内容片段的交互,方便我们管理页面和App bar, 它具有以下优势

  1. 可视化的页面导航图,便于我们理清页面间的关系。
  2. 通过destination和action完成页面间的导航
  3. 方便添加页面切换动画
  4. 页面间类型安全的参数传递
  5. 通过NavigationUI类,对菜单、底部导航、抽屉菜单导航进行统一的管理
  6. 支持深层链接DeepLink

看到这里是不是有个疑问? destination和action到底是什么呢?
大部分情况下一个destination就表示一个Fragment,同样也可以表示Activity,或者其它的导航图。 多个destination连接起来就组成了一个导航图。destination之间的连接就叫做action.

Navigation中的主要元素

  1. Navigation Graph.这是一种新型的XML资源文件,其中包含应用程序所有的页面,以及页面间的关系
  2. NavHost:显示导航图中目标的空白容器。导航组件包含一个默认 NavHost 实现 (NavHostFragment),可显示 Fragment 目标。 Navigation Graph中的Fragment正是通过NavHostFragment进行展示的
  3. NavController:在NavHost中管理应用导航的对象,用于在代码中完成Navigation Graph中具体的页面切换工作。

3种元素之间的关系:
当你想切换Fragment时,使用NavController对象,告诉它你想要去NavigationGraph中的哪个Fragment, NavController会将你想去的Fragment展示在NavHostFragment;

Navigation基本使用

添加依赖

implementation 'androidx.navigation:navigation-fragment:2.3.1'
implementation 'androidx.navigation:navigation-ui:2.3.1'

创建资源文件

选择res文件夹->New->Android Resource File, 新建一个Navigation Graph文件. 如下图所示: 将File name 设置为 nav_graph, Resource type设置为Navigation
在这里插入图片描述

在Activity添加NavHostFragment

NavHostFragment是一个特殊的Fragment,我们需要将其添加到Activity的布局文件中,作为其他Fragment的容器

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">
    
    <fragment
        android:id="@+id/nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        app:navGraph="@navigation/nav_graph"/>
</LinearLayout>
  • android:name 是NavHostFragment,它实现了NavHost,这是一个用于放置管理destination的空视图
  • app:navGraph: 用于将这个NavHostFragment和nav_graph.xml关联起来
  • app:defaultNavHost:设置为true,则该Fragment会自动处理系统返回键即当用户按下手机的返回按钮时,系统能自动将当前所展示的Fragment退出

创建destination

回到nav_graph.xml中,依次单机加号按钮、"Create new destination"按钮,创建一个destination
在这里插入图片描述
destination是"目的地"的意思,代表着你想去的页面,它可以是Fragment或Activity. 我们通过destination 创建一个名为MainFragment的Fragment,

创建完之后 如下图所示
在这里插入图片描述
面板中出现了一个mainFragment. "Start"表示该MainFragment是起始Fragment, 即NavHostFragment容器首先展示的Fragment

接着查看 nav_graph.xml文件,发现多出了一个fragment标签

<?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/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.jetpack.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >
    </fragment>
</navigation>

可以看到,在navigation标签下有一个startDestination属性,该属性指定起始destination为mainFragment

运行程序,即可以看到一个Fragment,即destination所指定的mainFragment

在这里插入图片描述

完成Fragment页面切换

与创建MainFragment的方式类似,先创建一个SecondFragment
在这里插入图片描述
单击mainFragment,用鼠标选中其右侧的圆圈,并拖拽至右边的secondFragmen
在这里插入图片描述
在nav_graph.xml文件中,发现mainFragment多出了一个标签

<?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/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.jetpack.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >
        <action
            android:id="@+id/action_mainFragment_to_secondFragment2"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.example.jetpack.secondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" />
</navigation>

app:destination: 指定要跳转到的Fragment的id
app:id 定义这个action的id, 代码里执行跳转时要用到

使用NavController 完成导航

跳转通过 NavController 对象,它有三种获取方法:

  • NavHostFragment.findNavController(Fragment)
  • Navigation.findNavController(Activity, @IdRes int viewId)
  • Navigation.findNavController(View)

回到MainFragment的布局文件中 添加一个Button

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainFragment">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:id="@+id/tv_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_blank_fragment" />
    <Button
        android:id="@+id/btntoSecondFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="to SecondFragment"
        android:layout_below="@id/tv_text"
        />

</RelativeLayout>

打开MainFragment,响应这个按钮的单击事件,完成具体的页面切换

 @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_main,container,false);

        //方法一
        view.findViewById(R.id.btntoSecondFragment).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Navigation.findNavController(v)
                        .navigate(R.id.action_mainFragment_to_secondFragment);
            }
        });

        //方法二
        view.findViewById(R.id.btntoSecondFragment).setOnClickListener(
                Navigation.createNavigateOnClickListener(R.id.action_mainFragment_to_secondFragment)
        );


        return view;
    }

调用 NavController 的 navigate 方法执行跳转,navigate 的参数可以是一个 destination(这里就是 fragment 在导航图 nav_graph 中的 id),也可以是 action 的 id。

在这里插入图片描述
当点击按钮的时候,跳转到了secondFragment
在这里插入图片描述
这里可以看到Fragment完成了切换,但切换没有动画效果,显得很生硬。

添加页面切换动画效果

点击目标箭头,并在右边的Animations面板中为其设置动画文件
在这里插入图片描述
代码自动变成

 <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.jetpack.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >
        <action
            android:id="@+id/action_mainFragment_to_secondFragment"
            app:destination="@id/secondFragment"
            app:enterAnim="@android:anim/slide_out_right"
            app:exitAnim="@android:anim/slide_in_left"
            app:popEnterAnim="@android:anim/slide_in_left"
            app:popExitAnim="@android:anim/slide_out_right" />
    </fragment>

enterAnim和exitAnim是去往栈里添加一个destination时动画,popEnterAnim和popExitAnim是从栈里移除一个destination时的动画
运行程序,此时切换页面便可以看到动画效果。

传递参数

定义目的地参数

如需在目的地之间传递数据,首先请按照以下步骤将参数添加到接收它的目的地来定义参数:

  1. 在 Navigation Editor 中,点击接收参数的目的地。 在 Attributes 面板中,点击 Add (+)。
  2. 在显示的 Add Argument Link 窗口中,输入参数名称、参数类型、参数是否可为 null,以及默认值(如果需要)。
  3. 点击 Add。请注意,该参数现在会显示在 Attributes 面板的 Arguments 列表中。
  4. 接下来,点击会将您转到此目的地的相应操作。在 Attributes 面板中,您现在应该会在 Argument DefaultValues 部分中看到新添加的参数。
  5. 您还可以看到该参数已添加到 XML 中。点击 Text 标签页以切换到 XML 视图,就会发现您的参数已添加到接收该参数的目的地。相关示例如下所示:
<fragment android:id="@+id/myFragment" >
     <argument
         android:name="age"
         app:argType="integer"
         android:defaultValue="0" />
 </fragment>
支持的参数类型

在这里插入图片描述

使用Safe Args传递安全的数据

Navigation 组件具有一个名为 Safe Args 的 Gradle 插件,该插件可以生成简单的 object 和 builder 类,以便以类型安全的方式浏览和访问任何关联的参数

在项目的build.gradler文件中添加safe args插件

 dependencies {
        def nav_version = "2.3.0-alpha05"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }

在app的build.gradle文件中添加对safe args的依赖

apply plugin: "androidx.navigation.safeargs"
在导航图添加标签

可以直接在Text中编写,也可以通过Design面板进行添加

<fragment
        android:id="@+id/mainFragment"
        android:name="com.example.jetpack.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >
        <action
            android:id="@+id/action_mainFragment_to_secondFragment"
            app:destination="@id/secondFragment"
            app:enterAnim="@android:anim/slide_out_right"
            app:exitAnim="@android:anim/slide_in_left"
            app:popEnterAnim="@android:anim/slide_in_left"
            app:popExitAnim="@android:anim/slide_out_right" />
        <!--添加参数-->
        <argument
            android:name="user_nmae"
            app:argType="string"
            android:defaultValue="unknown"
            />
        <argument
            android:name="age"
            app:argType="integer"
            android:defaultValue="0"
            />
    </fragment>
  1. android:name 是传递参数的名称
  2. app:argType 是传递参数的类型
  3. android:defaultValue: 是传递参数的默认值,需要根据app:argType进行添加

添加,对项目进行编译,便可以在app/generatedJava目录下看到safe args插件为我们生成的代码文件了.在这些代码文件中包含了参数所对应的Getter 和Setter方法
在这里插入图片描述
最后利用生成的代码文件,进行Fragment之间进行参数传递

使用 Bundle 对象在目的地之间传递参数

在MainFragment进行参数传递

view.findViewById(R.id.btntoSecondFragment).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Bundle bundle = new MainFragmentArgs.Builder().
                                    setUserNmae("Shaoyi")
                                    .setAge(20)
                                    .build().toBundle();
                Navigation.findNavController(v)
                        .navigate(R.id.action_mainFragment_to_secondFragment,bundle);
            }
        });

参数接收
在secondFragment进行参数接收

 Bundle bundle = getArguments();
        if(bundle!=null){
            String userName = MainFragmentArgs.fromBundle(bundle).getUserNmae();
            int age = MainFragmentArgs.fromBundle(bundle).getAge();
            Toast.makeText(getActivity(), "名字为"+userName+"年龄为"+age, Toast.LENGTH_SHORT).show();
        }

运行结果如下
在这里插入图片描述
正如插件safe args名字所代表的意思,它的主要好处在于安全的参数类型。Getter和Setter的方式令参数的操作更友好,更直观,且更安全。

不用action进行切换页面 不用safe args传递参数

编写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/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.jetpack.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >     
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.example.jetpack.secondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" />
</navigation>

mainFragment中的代码

传递参数

Bundle bundle = new Bundle();
bundle.putString("user_name","shaoyi");
bundle.putInt("")
Navigation.findNavController(v)
    .navigate(R.id.secondFragment,bundle);

只需要将navigate()方法中的id改成其他的Fragment的id即可(原先是action的id),但是需要注意的是,该Fragment必须要在nav_graph.xml进行注册.
而且参数传递 也可以不用标签,也就是不使用Safe Args插件,直接使用Bundle对象在目的地之间进行传递。

secondFragment中的代码

接收参数

 Bundle bundle = getArguments();
        if(bundle!=null){
            String userName = bundle.getString("user_name");
            int age = bundle.getInt("age");
            Toast.makeText(getActivity(), "名字为"+userName+"年龄为"+age, Toast.LENGTH_SHORT).show();
        }

在这里插入图片描述

参考:

  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值