声明 : 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.
直接截图了哈.是那回事吧
3,创建 Navigation Graph
选中 res 文件夹->New->Android Resource File,Resource Type选择(Navigation).
添加 fragment,
添加完了,用线连起来
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,基本的切换完成了.我们看下效果图.
录屏质量差了点....凑活着看吧
看起来很生硬啊,加个动画效果吧~~
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)
}
}
好了,基本上是完成了,剩下的就不展示了.都是一样的呀
展示:
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 的生命周期问题.⚠️⚠️⚠️========
我们打印一下,我依次点击了四个按钮. 循环一次,看图片
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"
}
}
看效果:
以上就是通过 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:浏览器的方式,我没实现,大概是因为我乱写的测试地址吧.....
看图