随着Android开发的快速发展,技术选型众多,Google希望实现一套工具集合帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码,于是提出了Jetpack
。Jetpack
分包括四大模块分别是Architecture
、Foundationy
、Behavior
、UI
。
Foundation
是基础组件,提供底层功能,如向后兼容性、测试、Kotlin
支持等。包括Android KTX
、AppCompat
、MultiDex
、Test
、Benchmark
等
Architecture
是架构组件,帮助开发者设计稳健、可测试且易维护的应用,包括Data Binding
、Lifecycles
、LiveData
、Navigation
、Paging
、Room
、ViewModel
、WorkManager
等。
Behavior
是行为组件,与标准Android服务相集成,包括CameraX
、DownloadManager
、Permissions
、Notifications
等。
UI
是界面组件,包含用于常见效果的内置动画、表情符号,其中使用Compose完成的界面也是UI
的范畴。
Jetpack
组件众多,咱们先从Architecture
开始,逐个讲解。首先来看一看Navigation
。Navigation
能够简化导航过程,通过图形化界面构建界面之间的跳转关系。
Navigation
可实现按钮点击、应用栏和抽屉式导航栏等导航
Navigation
包括三部分:Navigation Graph、NavHostFragment、NavController ,后面在具体的代码中讲解。
Navigation Graph 是XML文件,在
navigation
文件夹下。在Navigation Graph文件内进行路径的设置。这里需要注意:包含一个或多个目的地,可以由
或
元素表示
NavHostFragment 是容器,负责装载Navigation Graph。
NavController 负责具体的触发跳转,调用
navigate()
或popBackStack(),
等方法时,它会根据正在导航的目标目的地类型或起始目的地类型来将这些命令转换为相应的框架操作,当目的地是activity时,自动调用startActivity()
。
创建Navigation Graph ,Studio提供了直接创建的插件,工程根目录右键New->Android Resource File ->Resource type 选中Navigation -> 文件名称,这里会自动添加这两个库
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
Navigation Graph 文件右上角 New Destination
添加视图
添加NavHostFragment,可以是fragment,也可以是androidx.fragment.app.FragmentContainerView
推荐后者。
NavController 触发跳转事件
// 跳转方式二 actionId
binding.button.setOnClickListener(
Navigation.createNavigateOnClickListener(R.id.next_action)
)
binding.button.setOnClickListener {
// 跳转方式一 id
findNavController().navigate(R.id.flow_step_fragment, null)
// 跳转方式三 action
val directions = HomeFragmentDirections.nextAction()
findNavController().navigate(directions)
}
传递参数
传递参数使用 safe args 传参是目的地设置参数
打开项目 build.gradle
文件,添加 safe args 插件
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
打开 app/build.gradle
文件,应用插件
apply plugin: 'androidx.navigation.safeargs.kotlin'
Graph 文件 中目的地添加参数,这里序列化实例推荐Parcelable
方式
// 发送方
val flowStepNumber = 1
val product = Product("可乐")
val directions = HomeFragmentDirections.nextAction(flowStepNumber, product)
findNavController().navigate(directions)
// 接受方
private val safeArgs: FlowStepFragmentArgs by navArgs() // 全局变量
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val flowStepNumber = safeArgs.flowStepNumber
val product = safeArgs.product
Log.d("====", product?.name.toString())
binding = when (flowStepNumber) {
2 -> FragmentFlowStepTwoBinding.inflate(inflater, container, false)
else -> FragmentFlowStepOneBinding.inflate(inflater, container, false)
}
return binding.root
}
动画
两种添加转场动画的方式
方式一: xml 当中
方式二:逻辑代码当中
val options = navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
popEnter = R.anim.slide_in_left
popExit = R.anim.slide_out_right
}
}
val flowStepNumber = 1
val product = Product("可乐")
val directions = HomeFragmentDirections.nextAction(flowStepNumber, product)
findNavController().navigate(directions, options)
NavigationUI
具有将菜单项与导航目的地相关联的静态方法,navigation-ui-ktx
是执行关联操作的一组扩展函数。如果 NavigationUI
在当前导航图上找到与目的地具有相同 ID 的菜单项,就会将该菜单项配置为导航到该目的地。
步骤一: 创建menu文件 overflow_menu.xml
步骤二: 添加 menu 到菜单,添加选中事件
// 添加 menu 到菜单
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.overflow_menu, menu)
return true
}
// menu 选中
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
|| super.onOptionsItemSelected(item)
}
步骤三: 修改Graph 文件,注意id 和menu文件保持一致,这里我特意把目的地设为activity,因为我最先误解只能是fragment
步骤一: 添加 BottomNavigationView
步骤二: 编写menu, bottom_nav_menu.xml
步骤三: 关联NavController
// 底部Bottom
private fun setupBottomNavMenu(navController: NavController) {
binding.bottomNavView.setupWithNavController(navController)
}
步骤四: 设置顶级界面,
appBarConfiguration = AppBarConfiguration(
setOf(R.id.home_dest, R.id.deeplink_dest) // 顶级目的地
)
setupActionBar(navController, appBarConfiguration)
步骤一:添加NavigationView
...
步骤二: 编写menu,nav_drawer_menu.xml,同上, 略
步骤二: 关联NavController
appBarConfiguration = AppBarConfiguration(
setOf(R.id.home_dest, R.id.deeplink_dest), // 顶级目的地
binding.drawerLayout // DrawerLayout
)
这一小节的功能是通过桌面插件跳转至App某个页面
步骤一: 通过AppWidgetProvider创建桌面插件,AppWidgetProvider实质是BroadcastReceiver
class DeepLinkAppWidgetProvider : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
val remoteViews = RemoteViews(
context.packageName,
R.layout.deep_link_appwidget
)
val args = Bundle()
args.putString("myarg", "From 桌面")
val pendingIntent = NavDeepLinkBuilder(context)
.setGraph(R.navigation.mobile_navigation)
.setDestination(R.id.deeplink_dest) // 跳转目的地
.setArguments(args) // 参数
.createPendingIntent()
remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent)
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews)
}
}
步骤二: AppWidgetProvider实质是BroadcastReceiver,在配置文件当中注册
步骤三: 为了观察效果,接受方也要处理一下
val myArg = arguments?.getString("myarg")
binding.text.text = myArg
NavController支持将网址直接映射到导航图中的目的地。
步骤一: 在目的地添加
步骤二: 修改配置文件, 添加nav-graph
本来以为到这里就结束了,但是发现例子当中还有一个通知跳转,索性也写一下,顺便复习一下通知的写法: 创建通知,NotificationManager =》 检测版本 =》 Andorid 8以上 创建通道 =》notify
binding.button.setOnClickListener {
val etArg = binding.etArg.text.toString()
val args = Bundle()
args.putString("myarg", etArg)
val deepLink = findNavController().createDeepLink()
.setDestination(R.id.deeplink_dest)
.setArguments(args)
.createPendingIntent()
val notificationManager =
context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(
NotificationChannel(
"deepLink",
"消息",
NotificationManager.IMPORTANCE_HIGH
)
)
}
val notification = NotificationCompat.Builder(requireContext(), "deepLink")
.setContentTitle("Navigation")
.setContentText("Deep link to Android")
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(deepLink)
.setAutoCancel(true)
.build()
notificationManager.notify(0, notification)
}
知识点一: actionBar 绑定
要想lable 显示 Graph 文件当中配置的文字,以及actionBar 和 NavController关联起来,需要如下步骤
// 步骤一 显示 ActionBar
setSupportActionBar(binding.toolbar)
// 步骤二 找到 NavHostFragment
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment
val navController = host.navController
// 步骤三 关联 NavController 和 AppBarConfiguration
appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration)
知识点二: 点击事件
语法层面上方式二和方式一三相同,但是actionId 只使用于方式二,这个createNavigateOnClickListener()
内部创建View点击事件有关
// 跳转方式二 actionId
binding.button.setOnClickListener(
Navigation.createNavigateOnClickListener(R.id.next_action)
)
binding.button.setOnClickListener {
// 跳转方式一 id
findNavController().navigate(R.id.flow_step_fragment, null)
// 跳转方式三 action
val directions = HomeFragmentDirections.nextAction()
findNavController().navigate(directions)
}
知识点三:路由监听
NavController
提供了全局的监听接口
// NavController 提供了全局的监听接口
navController.addOnDestinationChangedListener { controller, destination, arguments ->
val dest: String = try {
resources.getResourceName(destination.id)
} catch (e: Resources.NotFoundException) {
destination.id.toString()
}
Log.d("NavigationActivity", "Navigated to $dest")
}
如果需要系统的jetpack学习资料,而我正好薅到这本阿里十年技术专家联合打造“最新”《Jetpack架构组件入门到精通》和《Jetpack强化实战手册》,是你学习Jetpack的葵花宝典。