zoukankan      html  css  js  c++  java
  • Android开发navigation入门详解

    前言

    Google 在2018年推出了 Android Jetpack,在Jetpack里有一种管理fragment的新架构模式,那就是navigation. 字面意思是导航,但是除了做APP引导页面以外.也可以使用在App主页分tab的情况.. 甚至可以一个功能模块就一个activity大部分页面UI都使用fragment来实现,而navigation就成了管理fragment至关重要的架构.

    但是,它不单单只能管理fragment也可以管理activity.这点你格外注意.

    使用条件

    你的Android studio 必需升级到3.2版本以上,此博客正在写的时候Android studio已经到达4.2.1,所以升级到最新版本即可.

    依赖

    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
    

     Android studio的4.2.1版本默认是添加这两个依赖的

    使用流程

      创建navigation目录

    1.选中项目资源文件夹 res 右击 >> New >> New Resource Directory(或 Android Resource Directory)

    2.选中navigation 点击创建 (注意这个目录只有在Android studio3.2版本以上才能出现)

    3、选择Resource type 为 navigation, 点击OK

    点击OK

    然后选中navigation文件夹目录,创建navigation目录下的xml文件

    1.选中项目资源文件夹 res 右击 >> New >> New Resource File(或Navigation Resource File)

    2.选择navigation ,输入xml文件名称,点击ok创建

    配置创建的xml文件

     上面我们创建了一个叫demo_nav.xml的navigation文件,现在我们需要来设配它来管理fragment

     1.打开这个文件选模式使用视图手动配置.

     2.切换到Design模式后,我们可以看到下面这个界面(恩,一片空白). 我们可以在左上角点击添加图标,进入添加内容的操作.

    3.点击后,可以看到下面这个弹窗,这里解释一下:

    第一个 Create new destinattion,字面意思创建一个新目标(其实就是创建fragment,当然你也可以手动另外创建fragment不一定需要在这里创建)

    第二个 placeholder,这个就是重点了. 这是一个管理fragment跳转的节点,我们点击后可以创建它.为了了解它的使用方式,点击3次创建三个节点

     4.节点创建后可以看到三个节点(看下面图片,这些节点都是我已经导入fragment了.不要急后面会讲解如何导入).这里有一个重点! 你可以点击这些页面(会有一个蓝点),点击蓝点按住向右分配它需要跳转的另外一个页面.(它会自动生成一些我们跳转的代码)

    6.然后点击左下角的Text模式,在Text模式下,可以看到如下代码,在上面的图片中你可以很清楚的看到创建了3个节点,并且是一个跳转一个的.从第一个fragment跳转到第二个fragment,再从第二个fragment跳转到第三个fragment

    下面我们来重点讲解下下面的这些代码的关键点了:

    在<navigation里的属性:

    1.android:id="@+id/demo_nav" 这个属性是你这个xml文件navigation的id,很重要,我们需要在activity的xml布局里引用,记得写上不要忘记

    2.app:startDestination="@id/homeFragment" 这个属性是你首次加载的第一个页面,很重要,一般就是第一个fragment

    在<fragment 里的属性:

    其实就是一个节点你也可以理解成一个fragment

    1.android:id="@+id/homeFragment"  每一个fragment节点都需要有自己的id,很重要. 我们需要在后面的节点上使用这些id指定跳转目标

    2.android:name="com.example.jgdemo.fragments.HomeFragment"   这个属性是你这个节点所对应的fragment(需要你导入指定的fragment文件路径),这个很重要

    3.android:label="HomeFragment" 一个标签名称,用于记录这个节点的标签信息(大概可能是在代码里的Intent里获取来知晓此次是那个fragment节点在跳转,没深究了)

    4.tools:layout="@layout/fragment_home" 这个属性不是重要的,设置它后你可以在切换到Design模式后看到,视图页面的fragment的预览图(就在上面的图片里,可以直接看到fragment效果)

    在<action 里的属性:

    action 负责编写跳转动作

    1. android:id="@+id/action_homeFragment_to_myOneFragment" 这个很重要,它是这个跳转动作的id, 这个id我们将在后面的代码中调用,用于执行fragment的跳转

    2. app:destination="@id/myOneFragment" 跳转的目标fragment,这个很重要

    3、

    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"

    是使用的系统自带的动画

    整体如下:

    <?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/demo_nav"
        app:startDestination="@id/homeFragment"><!--首次加载的第一个页面-->
    
        <fragment
            android:id="@+id/homeFragment"
            android:name="com.example.jgdemo.fragments.HomeFragment"
            android:label="HomeFragment"
            tools:layout="@layout/fragment_home">
            <action
                android:id="@+id/action_homeFragment_to_myOneFragment"
                app:destination="@id/myOneFragment"
                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" />
            <argument
                android:name="userName"
                app:argType="string"
                app:nullable="true"
                android:defaultValue="unknown" />
            <argument
                android:name="age"
                app:argType="integer"
                android:defaultValue="0" />
    
        </fragment>
        <fragment
            android:id="@+id/myOneFragment"
            android:name="com.example.jgdemo.fragments.MyOneFragment"
            android:label="MyOneFragment"
            tools:layout="@layout/fragment_myone">
            <action
                android:id="@+id/action_myOneFragment_to_myTwoFragment"
                app:destination="@id/myTwoFragment" />
            <action
                android:id="@+id/action_myOneFragment_to_myThreeFragment"
                app:destination="@id/myThreeFragment" />
    
            <argument
                android:name="myname"
                app:argType="string"
                app:nullable="true"
                android:defaultValue="unknown" />
            <argument
                android:name="myage"
                app:argType="integer"
                android:defaultValue="0" />
        </fragment>
        <fragment
            android:id="@+id/myTwoFragment"
            android:name="com.example.jgdemo.fragments.MyTwoFragment"
            android:label="MyTwoFragment"
            tools:layout="@layout/fragment_mytwo"/>
        <fragment
            android:id="@+id/myThreeFragment"
            android:name="com.example.jgdemo.fragments.MyThreeFragment"
            android:label="MyThreeFragment"
            tools:layout="@layout/fragment_mythree"/>
    
    
    </navigation>
    

      

    让navigation与Activity关联起来

       现在我们已经创建了navigation,但是使用它还需要一个根Activity,它毕竟还是需要依托Activity的.

     1.创建了一个叫DemoActivity的Activity.这个没啥,下面来看这个Activity的布局xml怎么配(如下xml代码)

      我们就关注fragment的一些属性

    1.android:name="androidx.navigation.fragment.NavHostFragment"  这个非常重要,这是你告知fragment需要使用navigation模式的关键属性,另外它是固定死的.你必需写.

    2. app:defaultNavHost="true"  这是你实现物理按键(比如返回键),是按一下退出一个fragment 还是直接退出这个Activity的关键属性

    3.app:navGraph="@navigation/demo_nav"  很重要,这就是我们前面创建的navigation的xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout 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容器-->
        <androidx.fragment.app.FragmentContainerView
            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/demo_nav"></androidx.fragment.app.FragmentContainerView>
    </FrameLayout>
    

      

    实现fragment跳转与返回

     进入到MainActivity后,首先会自动加载到第一个fragment. 然后我们看看如何跳转到其他fragment中

     1.从第一个碎片跳转到第二个碎片,关键代码  Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment)

    class HomeFragment : Fragment(){
         //使用viewBinding直接省去findViewById
        private lateinit var binding: FragmentHomeBinding
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = FragmentHomeBinding.inflate(layoutInflater)
            binding.jumptomyoneBtn.setOnClickListener {
    //跳转到MyOneFragment Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment) } return binding.root } }

      2.从第二个碎片返回到第一个碎片,关键代码 Navigation.findNavController(binding.root).popBackStack()

    class MyOneFragment : Fragment(){
    //使用viewBinding直接省去findViewById
    private lateinit var binding: FragmentMyoneBinding
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = FragmentMyoneBinding.inflate(layoutInflater)
            //返回
            binding.closemyoneBtn.setOnClickListener {
                //关闭页面
                Navigation.findNavController(binding.root).popBackStack()
            }
          
            return binding.root
        }
    }

    上面我使用了谷歌的ViewBinding直接省去findViewById
    用法:在build.gradle的 android 闭包中添加
    buildFeatures {
            viewBinding true
       
        }
    

      

    实现fragment传值

    打开你的nvagation文件,选中Design

     选中你的Fragment视图

     然后右侧选中Arguments,点击加号,添加你要传递的参数

     然后会自动生成代码

             <argument
                android:name="userName"
                app:argType="string"
                app:nullable="true"
                android:defaultValue="unknown" />
            <argument
                android:name="age"
                app:argType="integer"
                android:defaultValue="0" />
    

     传递参数

                val bundle = HomeFragmentArgs("张三",24).toBundle()
                Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment,bundle)
    

      接收参数

    //         接收传递来的参数  方法1
            val bundle = arguments
            val username = bundle?.getString("userName")
            val age = bundle?.getInt("age",0)
            Log.i("打印接收传递来的数据:","用户名:$username ,年龄: ${age.toString()}")
    
            接收传递来的参数  方法2
            val bundle = arguments
            val username = arguments?.let { HomeFragmentArgs.fromBundle(it).userName }
            val age = arguments?.let { HomeFragmentArgs.fromBundle(it).age }
            Log.i("打印接收传递来的数据:","用户名:$username ,年龄: ${age.toString()}")
    

      使用了 safe args插件传递参数,配置如下

     在Project的build.gradle文件中添加safe args插件

            def nav_version = "2.3.0-alpha01"
            classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    

      如下:

    buildscript {
        ext.kotlin_version = "1.5.0"
    // ext.nav_version = "2.3.0-alpha01" repositories { google() mavenCentral() jcenter() } dependencies { classpath "com.android.tools.build:gradle:4.2.1" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } } allprojects { repositories { google() mavenCentral() } } task clean(type: Delete) { delete rootProject.buildDir }

      然后需要引用这个插件。在app的build.gradle中添加依赖。
    如果需要java或jave和kotlin的,添加这个:

    id 'androidx.navigation.safeargs'
    

      纯kotlin的,添加这个:

    id 'androidx.navigation.safeargs.kotlin'
    

      如图:

    最后上一下我完整代码:

    demo_nav.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/demo_nav"
        app:startDestination="@id/homeFragment"><!--首次加载的第一个页面-->
    
        <fragment
            android:id="@+id/homeFragment"
            android:name="com.example.jgdemo.fragments.HomeFragment"
            android:label="HomeFragment"
            tools:layout="@layout/fragment_home">
            <action
                android:id="@+id/action_homeFragment_to_myOneFragment"
                app:destination="@id/myOneFragment"
                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" />
            <argument
                android:name="userName"
                app:argType="string"
                app:nullable="true"
                android:defaultValue="unknown" />
            <argument
                android:name="age"
                app:argType="integer"
                android:defaultValue="0" />
    
        </fragment>
        <fragment
            android:id="@+id/myOneFragment"
            android:name="com.example.jgdemo.fragments.MyOneFragment"
            android:label="MyOneFragment"
            tools:layout="@layout/fragment_myone">
            <action
                android:id="@+id/action_myOneFragment_to_myTwoFragment"
                app:destination="@id/myTwoFragment" />
            <action
                android:id="@+id/action_myOneFragment_to_myThreeFragment"
                app:destination="@id/myThreeFragment" />
    
            <argument
                android:name="myname"
                app:argType="string"
                app:nullable="true"
                android:defaultValue="unknown" />
            <argument
                android:name="myage"
                app:argType="integer"
                android:defaultValue="0" />
        </fragment>
        <fragment
            android:id="@+id/myTwoFragment"
            android:name="com.example.jgdemo.fragments.MyTwoFragment"
            android:label="MyTwoFragment"
            tools:layout="@layout/fragment_mytwo"/>
        <fragment
            android:id="@+id/myThreeFragment"
            android:name="com.example.jgdemo.fragments.MyThreeFragment"
            android:label="MyThreeFragment"
            tools:layout="@layout/fragment_mythree"/>
    
    
    </navigation>
    

    HomeFragment

    class HomeFragment : Fragment(){
    
        private lateinit var binding: FragmentHomeBinding
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = FragmentHomeBinding.inflate(layoutInflater)
            binding.jumptomyoneBtn.setOnClickListener {
                //方法1、使用Directions
                //不传递参数
                //var action = HomeFragmentDirections.actionHomeFragmentToMyOneFragment()
                //findNavController().navigate(action)
    
                //传递参数,需要接收参数的fragment中写argument
                var action = HomeFragmentDirections.actionHomeFragmentToMyOneFragment("这是我的名字",24)//参数直接写入
                findNavController().navigate(action)
    
    
                //方法2、直接使用Id
                //不传递参数
                //Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment)
    
                //传递参数1 对应 接收参数1和2
                //val bundle = HomeFragmentArgs("张三",24).toBundle()
                //Navigation.findNavController(binding.root).navigate(R.id.action_homeFragment_to_myOneFragment,bundle)
    
            }
            return binding.root
        }
    }
    

      

    MyOneFragment

    class MyOneFragment : Fragment(){
    
        private lateinit var binding: FragmentMyoneBinding
    
        private val args: MyOneFragmentArgs by navArgs()
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = FragmentMyoneBinding.inflate(layoutInflater)
            //返回
            binding.closemyoneBtn.setOnClickListener {
                //方法1、使用Directions
               //findNavController().popBackStack()
                //方法2、
    
                Navigation.findNavController(binding.root).popBackStack()
    
            }
            //跳转到第三个页面
            binding.jumptomytwoBtn.setOnClickListener {
                //方法1、使用Directions
                //val action = MyOneFragmentDirections.actionMyOneFragmentToMyTwoFragment()
                //findNavController().navigate(action)
    
                //方法2、直接使用Id
                Navigation.findNavController(binding.root).navigate(R.id.action_myOneFragment_to_myTwoFragment)
            }
    
            //接收传递来的参数  方法1
            //val bundle = arguments
            //val username = bundle?.getString("userName")
            //val age = bundle?.getInt("age",0)
            //Log.i("打印接收传递来的数据:","用户名:$username ,年龄: ${age.toString()}")
    
            //接收传递来的参数  方法2
            //val bundle = arguments
            //val username = arguments?.let { HomeFragmentArgs.fromBundle(it).userName }
            //val age = arguments?.let { HomeFragmentArgs.fromBundle(it).age }
            //Log.i("打印接收传递来的数据:","用户名:$username ,年龄: ${age.toString()}")
    
            //接收传递来的参数
            Log.i("打印接收的参数",args.myname +" ,"+ args.myage)
    
    
            return binding.root
        }
    }
    

      

     

    完成

    参考:

    https://www.cnblogs.com/guanxinjing/p/11555217.html

    https://blog.csdn.net/nanquan11/article/details/109807501

    https://www.it1352.com/1534512.html

  • 相关阅读:
    airpods2连接win10的方法步骤
    JSON学习笔记
    TCP-IP总线与CAN总线优缺点对比
    svn切换目录
    SQLite学习笔记
    python-opencv安装及入门
    python数据可视化
    python-opencv视觉巡线
    python-opencv进阶应用
    QT窗口和部件
  • 原文地址:https://www.cnblogs.com/changyiqiang/p/14927567.html
Copyright © 2011-2022 走看看