IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android—Jetpack教程(七) -> 正文阅读

[移动开发]Android—Jetpack教程(七)

前言

在上一篇中,已经将Jetpack对应的Room讲解完毕。在本篇中将会开启Navigation初步讲解!

1、认识Navigation

1.1 Navigation的诞生

Activity嵌套多个Fragment的UI架构模式已经非常普遍,但是对Fragment的管理一直是一件比较麻烦的事情。我们需要通过FragmentManager和FragmentTransaction来管理Fragment之间的切换。

页面的切换通常包括对应用程序App Bar的管理、Fragment间的切换动画,以及Fragment间的参数传递。

纯代码的方式使用起来不是特别友好,并且Fragment和App bar在管理和使用的过程中显得混乱。

为此,Jetpack提供了Navigation组件,旨在方便我们管理页面和App Bar

1.2 Navigation的主要元素

  • Navigation Graph,一种新的XML资源文件,包含应用程序所有的页面,以及页面间的关系。
  • NavHostFragment,一个特殊的Fragment,可以将它看作是其他Fragment的容器,
    Navigation Graph中的Fragment正式通过NavHostFragment进行展示的。
  • NavController ,用于在代码中完成Navigation Graph中具体的页面切换工作。
  • 它们三者之间的关系
    • 当你想切换Fragment时,使用NavController对象,告诉它你想要去Navigation Graph 中的哪个Fragment,NavController会将你想去的Fragment展示NavHostFragment中。

概念说了一大堆,开始实战吧!

2、Navigation实战

2.1 准备工作

项目根目录对应的build.gradle需要引入

classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0-alpha06'

对应app module里面的build.gradle

plugins {
    id 'com.android.application'
    id 'kotlin-android'
//    id 'androidx.navigation.safeargs' //这个是java引入的,下面才是kotlin
    id 'androidx.navigation.safeargs.kotlin'
}
...略

dependencies {
...略
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
...}

好了准备工作做好了,开始实战吧!

2.2 开始实战

2.2.1 布局以及关系搭建

在这里插入图片描述
如图所示

  • 先创建对应的Fragment和对应的布局,并且相互关联
  • 在res资源目录下创建对应的Navigation资源文件(my_nav_graph.xml)

在这里插入图片描述
如图所示

  • 随后通过标注1,将对应的Fragment添加进来,随后将对应的Fragment添加对应的关系
  • 我这关系是:homeFragment可跳detailFragment;detailFragment也可跳homeFragment

来看看当前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/my_nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.dongnaoedu.navigation.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_homeFragment_to_detailFragment"
            app:destination="@id/detailFragment"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim" />

    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:name="com.dongnaoedu.navigation.DetailFragment"
        android:label="fragment_detail"
        tools:layout="@layout/fragment_detail" >
        <action
            android:id="@+id/action_detailFragment_to_homeFragment"
            app:destination="@id/homeFragment" />
    </fragment>
</navigation>

从这段代码可以看出:

  • 最外层为navigation标签,其次包裹着不同的fragment
  • 而不同的fragment里面有不同的action操作
  • 除了基础操作外,还可以在对应action里添加入场出场动画

2.2.2 业务逻辑实现

HomeFragment.kt

class HomeFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        var button: Button? = view?.findViewById(R.id.button)
        button?.setOnClickListener {
            val navController = Navigation.findNavController(it)
            //id 对应my_nav_graph.xml 里对应Fragment  里面 的 action ID
            navController.navigate(R.id.action_homeFragment_to_detailFragment)
        }
    }

}

DetailFragment.kt

class DetailFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_detail, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val button: Button? = view?.findViewById(R.id.button2)
        button?.setOnClickListener {
            val navController = Navigation.findNavController(it)
            //id 对应my_nav_graph.xml 里对应Fragment  里面 的 action ID
            navController.navigate(R.id.action_detailFragment_to_homeFragment)
        }
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

		//下面代码的 意思是:当切换对应的Fragment时,动态修改ActionBar标题文字,
		//目的为了让运行效果明显
        val navController = Navigation.findNavController(this, R.id.fragment)
        NavigationUI.setupActionBarWithNavController(this, navController)
    }
}

来看看运行效果
在这里插入图片描述

我们看到,这里Fragment相互跳转已经实现了!但是点击上方返回缺返回不了!要怎么解决呢?

进入MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
		//下面代码的 意思是:当切换对应的Fragment时,动态修改ActionBar标题文字,
		//目的为了让运行效果明显
        val navController = Navigation.findNavController(this, R.id.fragment)
        NavigationUI.setupActionBarWithNavController(this, navController)
    }

    override fun onSupportNavigateUp(): Boolean {
        val navController = Navigation.findNavController(this, R.id.fragment)
        return navController.navigateUp()
    }
}

我们看到重写了onSupportNavigateUp 方法,里面实现了对应逻辑(固定代码)。

再来看看运行效果
在这里插入图片描述

从这个运行效果可以看出,点击上面返回已经能够成功返回了。

现在Fragment跳转已经实现了,那么传递值该怎么传呢?

2.2.3 Fragment 数据传递

我们先看以前所使用的方式:

对应HomeFragment

class HomeFragment : Fragment() {
	...override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        var button: Button? = view?.findViewById(R.id.button)
        button?.setOnClickListener {
            var args = Bundle()
            args.putString("userName","hqk")
            val navController = Navigation.findNavController(it)
            navController.navigate(R.id.action_homeFragment_to_detailFragment, args)
        }
    }
}

对应DetailFragment

class DetailFragment : Fragment() {
	...override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        var args = arguments
        var userName: String? = args?.getString("userName")
//        args?.getInt("userName")
        Log.d("hqk", "userName is $userName")
        
        val button: Button? = view?.findViewById(R.id.button2)
        button?.setOnClickListener {
            val navController = Navigation.findNavController(it)
            navController.navigate(R.id.action_detailFragment_to_homeFragment)
        }
    }
}

这个是我们比较熟悉的方式,运行效果我就不展示了

  • 相信大家对bundle取值的时候,往往还会前往上一个页面复制粘贴对应的key值以及对比对应的数据类型。
  • 因为在bundle取值的时候,即使key传错了,或者对应的变量类型取错了,在代码里都不会有任何错误提示
  • 只有当运行到对应逻辑时,才知道对应取值有问题

那么有没有新颖的方式呢?既然问了那就肯定有!

继续进入my_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/my_nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.hqk.navigation.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_homeFragment_to_detailFragment"
            app:destination="@id/detailFragment"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim"/>

        <argument
            android:name="user_name"
            app:argType="string"
            android:defaultValue="unknown"/>
        <argument
            android:name="age"
            app:argType="integer"
            android:defaultValue="0"/>
    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:name="com.hqk.navigation.DetailFragment"
        android:label="fragment_detail"
        tools:layout="@layout/fragment_detail" >
        <action
            android:id="@+id/action_detailFragment_to_homeFragment"
            app:destination="@id/homeFragment" />
    </fragment>
</navigation>

我们看到,在对应的fragment里面添加了对应的argument标签,里面标注了对应的类型和变量名!

那么如何使用呢?

进入HomeFragment

class HomeFragment : Fragment() {

	...override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        var button: Button? = view?.findViewById(R.id.button)
        button?.setOnClickListener {

//            var args = Bundle()
//            args.putString("userName","hqk")
            
            var args = HomeFragmentArgs(userName = "hqk", 18).toBundle()

            val navController = Navigation.findNavController(it)
            navController.navigate(R.id.action_homeFragment_to_detailFragment, args)
        }
    }

}

我们看到使用了HomeFragmentArgs将参数传递过去,那么接收方呢?

DetailFragment.kt

class DetailFragment : Fragment() {
	...override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

//        var args = arguments
//        var userName: String? = args?.getString("userName")
//        args?.getInt("userName")
//        Log.d("hqk", "userName is $userName")

        val args = HomeFragmentArgs.fromBundle(requireArguments())
        val userName = args.userName
        val age = args.age
        Log.d("hqk", "$userName,$age")
        
        val button: Button? = view?.findViewById(R.id.button2)
        button?.setOnClickListener {
            val navController = Navigation.findNavController(it)
            navController.navigate(R.id.action_detailFragment_to_homeFragment)
        }
    }
}

我们这里看到:

  • 通过HomeFragmentArgs.fromBundle(requireArguments())获取到了前者传入过来的bundle信息,而取值就直接通过对应的属性访问!
  • 极大的避免了运行时出错!也不会来回切换前后者看对应的变量名以及变量类型

运行效果我这就不贴了,读者可尝试一下

结束语

好了本篇Navigation的讲解,到这里就结束了。当然Navigation可不止于此,因此在下一篇中,将会讲解NavigationUI以及DeepLink相关的内容

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-30 15:43:53  更:2021-11-30 15:45:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:37:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码