Jetpack 之 Navigation 小白入手

声明 : https://www.jianshu.com/p/714062a9af75
目录

简介
原理
使用方法:

1,简单使用
2,添加页面动画
3,safe args插件传递参数
4,与BottomNavigationView配合使用
5,深层连接

简介:

  Android Jetpack提供的一个名为Navigation的UI架构组件,帮助我们更好的管理界面.我这篇文章只写,对 fragment 的管理哈.也是开发中最常见的.有以下优点
one:可视化的页面导航.
tow:通过 destination 和 action 完成页面间的导航.
three:方便添加转场动画.
four:页面之间传参数方便.
five:还可以支持抽屉式导航栏(DrawerLayout)和底部导航(BottomNavigationView)与顶部应用栏(Toolbar、CollapsingToolbarLayout、ActionBar),代码会展示BottomNavigationView的使用,其他雷同哦~~~
six: 支持深层连接 DeepLink.

工作原理:

了解几个元素:
1.Navigation Graph,这是一种新型的 XML 文件,其中包含所有的Fragment 以及页面之间的关系.
2.NavHostFragment,这是一个特殊的 Fragment,你可以认为他是其他 Fragment 的容器,Navigation Graph 中的 Fragment 正是通过 NAVHostFragment 进行展示的.
3.NavController,这是一个对象,用于在代码中完成 Navigation Graph 中具体的页面切换工作.
**重点来了:**
  当你想切换 Fragment 是,使用 NavController 对象,告诉它,你想要去Navigation Graph 中的哪个 Fragment,NavController 会将你想去的 Fragmen 展示在 NavHostFragment 中.

使用方法:
1,build.gradle 文件
    //java:
    implementation "androidx.navigation:navigation-fragment:$nav_version"
    implementation "androidx.navigation:navigation-ui:$nav_version"
   //kotlin:
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
2,创建 fragment.

直接截图了哈.是那回事吧


image.png

image.png
3,创建 Navigation Graph

选中 res 文件夹->New->Android Resource File,Resource Type选择(Navigation).


image.png

添加 fragment,


image.png

添加完了,用线连起来
image.png

Look 一下代码
nav_graph_main.xml



    
        
    
    
        
    
    
        
    
    

app:startDestination="@id/homeFragment"该属性指定起始的 destination 为 homeFragment.

以上体现了文章一开始说的第一个和第二个优点哦~~~

4,Activity中添加NavHost

敲黑板
one:android:name="androidx.navigation.fragment.NavHostFragment".这句代码,告诉我们,这是一个特殊的 Fragment.
otwo:app:defaultNavHost="true",设置为 true,意思是 Fragment 会处理物理返回键.当用户按下返回键的时候,系统将自动将当前所展示的 Fragment 退出.
three:app:navGraph="@navigation/nav_graph_main",设置该 Fragment 对应的导航图.

5,使用 NavController 完成导航

HomeFragment:

        home_tv.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_homeFragment_to_secondFragment)
        }

SecondFragment:

  second_tv.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_secondFragment_to_threeFragment)
        }

ThreeFragment:

  three_tv.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_threeFragment_to_myFragment)
        }

ok,基本的切换完成了.我们看下效果图.


哈哈哈.gif

录屏质量差了点....凑活着看吧
看起来很生硬啊,加个动画效果吧~~

6,添加页面的切换动画

先来几个动画
i_slide_in_right.xml



    

i_slide_out_left.xml


    

i_slide_in_left.xml



    

i_slide_out_right.xml


    

然后给 fragment 设置上:

        

呀呀呀

bingo 实现了
ps:
A 界面进入 B 界面
enterAnim B界面的进场动画 --->右进
exitAnim A 界面的退场动画 --->左出
点击Back
popEnterAnim A 界面回退进场的动画 --->左进
popExitAnim B 界面展示后退场动画 --->右出

7,利用safe args插件传递参数

安装插件
操作俩 build.gradle 文件

   //最外层build
   classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0"

   //app 目录下的 build 文件
   apply plugin: 'androidx.navigation.safeargs.kotlin'  //kotlin
   apply plugin:'androidx.navigation.safeargs'   //java

编辑 nav_graph_main.xml 文件:
两种方式:
在Text 中写代码
Design 中面板中添加

......
    
        
        
        
    
......

放出来一个看看哈.其他的几个雷同....不占地方了

HomeFragment:

  home_tv.setOnClickListener {
            var toBundle = HomeFragmentArgs("张三", 28).toBundle()
            Navigation.findNavController(it).navigate(R.id.action_homeFragment_to_secondFragment,toBundle)
        }

SecondFragment:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        var name = arguments?.let { HomeFragmentArgs.fromBundle(it).name }
        var age = arguments?.let { HomeFragmentArgs.fromBundle(it).age }
        second_tv.text = "名字:$name\n年龄:$age"
        second_tv.setOnClickListener {
            var toBundle = SecondFragmentArgs("${second_tv.text}\n吃饭睡觉打豆豆").toBundle()
            Navigation.findNavController(it)
                .navigate(R.id.action_secondFragment_to_threeFragment, toBundle)
        }
    }

好了,基本上是完成了,剩下的就不展示了.都是一样的呀
展示:


image.png
8,与底部导航的故事(BottomNavigationView)

先看下效果:

展示

看下代码
activity_main.xml



    

    

敲黑板
1,如果 tab 超过 3 个,不显示文字,加上app:labelVisibilityMode="labeled"这句代码.
2,去除水波纹,加上app:itemRippleColor="@null" 这句代码.
bttombar.xml


    
    
    
    

MainActivity

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bottomNV.itemIconTintList = null //自定义底部图标,需要加上这句代码
        var findNavController = Navigation.findNavController(this, R.id.main_nav_fragment)
        NavigationUI.setupWithNavController(bottomNV, findNavController)

    }
}

搞定,只需要上面 3 步,就可以实现效果了.
=======⚠️⚠️⚠️!!!但是!!! 有个问题,就 Fragment 的生命周期问题.⚠️⚠️⚠️========
我们打印一下,我依次点击了四个按钮. 循环一次,看图片

image.png

Fragment 每次生命周期每次都会被创建.....如果需要每次点击的时候,刷新 fragment 数据的话,就不要处理.如果不和BottomNavigationView一起使用的,正常切换,声明周期和 Activity 一样的.但是,如果不需要,就需要处理一下了.
原因呢???
过于具体的原因详情,巴拉 FragmentNavigator 源码看看吧,总结一句话就是,navigation切换 fragment 的时候,调用的 replace() 方法,不是 hide() 和 show().
如何解决?

很遗憾,我也没有找到更好的方法,网上一堆.......反正我没找到好用的.坐等有人帮我一把~~~

9,深层链接 DeepLink

Navigation 还有一个非常重要和实用的特性 DeepLink,深层链接.通过该特性,我们可以利用 PendingIntent 或者一个真实 URL 链接,直接跳转到应用中的某个界面.
方式一:PendingIntent
HomeFragment:

class HomeFragment : Fragment() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
   }

   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)
   }

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       super.onViewCreated(view, savedInstanceState)
       home_tv.setOnClickListener {
           sendNotifacaton()   //点击事件,发送通知
       }
   }
   private fun sendNotifacaton() {
       val channelId = "1212"
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           val importanceDefault = NotificationManager.IMPORTANCE_DEFAULT //重要等级
           val notificationManager: NotificationManager = requireActivity().getSystemService(NotificationManager::class.java)
           val channel = NotificationChannel(channelId, "渠道名称", importanceDefault)
           notificationManager.createNotificationChannel(channel)
       }
       val builder: NotificationCompat.Builder = NotificationCompat.Builder(requireActivity(), channelId)
           .setSmallIcon(R.mipmap.ic_launcher)
           .setContentTitle("标题")
           .setContentText("内容内容内容内容内容内容")
           .setContentIntent(getPIntent())
           .setAutoCancel(true)
       NotificationManagerCompat.from(requireActivity()).notify(0, builder.build())
   }

   //重点在这里
   fun getPIntent(): PendingIntent? {
       val bundle = Bundle()
       bundle.putString("aaa", "我是 大写的AAA")
       return Navigation.findNavController(requireActivity(), R.id.main_nav_fragment)
           .createDeepLink()
           .setGraph(R.navigation.nav_graph_main)
           .setDestination(R.id.threeFragment)
           .setArguments(bundle)
           .createPendingIntent()
   }
}

再看看,深度链接到的 Fragment:
ThreeFragment:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        //接受通知的内容
        var get = arguments?.get("aaa") as String
        three_tv.text = "$get"
    }
}

看效果:

800EB5BF7CF39A75C7FE0DD6DFEB3D58.gif

以上就是通过 PendingIntent 实现的 DeepLink,在 HomeFragment 中点击文字发送通知,然后点击通知,跳转到 ThreeFragement.即便app 处于后台,点击通知,也一样能打开.
方式二:URL
第一步:添加标签

    
         
        
    

第二步:为 Activity 设置标签.

     
            
                

                
            
             
            
        

第三步:SecondFragment:接受参数

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        var let = arguments?.let { it.getString("name") }
        second_tv.text = let
        
    }

第四步:通过 adb 命令模拟实现

adb shell am start -a android.intent.action.VIEW -d "http://www.test.com/我是name参数"

⚠️ps:浏览器的方式,我没实现,大概是因为我乱写的测试地址吧.....
看图


FFE40344D42F72D6BE1D483AE5629329.gif

END

image.png

你可能感兴趣的:(Jetpack 之 Navigation 小白入手)