Navigation的诞生
- Activity嵌套多个fragment的ui架构模式已经非常普遍,但是对Fragment的管理一直是一件比较麻烦的事情,我们需要通过fragmentManager和fragmentTransaction来管理fragment之间的切换,页面的切换通常还包括对应用程序App bar的管理、Fragment间的切换动画,以及fragment间的参数传递。纯代码的方式使用起来不是特别的友好,并且fragment和app bar在管理和使用过程中显得混乱。
- 为此,jetpack提供了Navigation组件,旨在方便我们管理页面和APP Bar。
Navigation的优势
-
可视化的页面导航图,类似于Apple Xcode中的StoryBoard,便于我们清理页面关系。
-
通过destination和action完成页面间的导航。
-
方便添加页面切换动画。
-
页面间类型安全的参数传递。
-
通过NavigationUI,对菜单、底部导航栏、抽屉菜单导航进行统一管理。
-
支持深层链接DeepLink。
Navigation主要元素
- Navigation Graph,一种新的XML资源文件,包含应用程序所有的页面,以及页面间的关系。
- NavigationFrament,一种特殊的Fragment,可以将它看作是其他Fragment的容器,Navigation Graph中的Fragment正是通过NavHostFragment进行展示的。
- NavContriller,用于在代码中完成Navigation Graph中具体的页面切换工作。
三者之间的关系:
当你想要切换Fragment时,使用NavController对象,告诉它你想要去Navigation Graph中的哪个Fragmet,NavController会将你想取的Fragment展示NavHostFragment中。
Navigation应用
-
创建NavHostFragment
-
MainActivity中
-
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //创建fragment的管理对象 val navigationController = Navigation.findNavController(this, R.id.fragment) NavigationUI.setupActionBarWithNavController(this,navigationController) } //菜单栏的返回键 override fun onSupportNavigateUp(): Boolean { val navigationController = Navigation.findNavController(this, R.id.fragment) return navigationController.navigateUp() } }
-
<fragment android:id="@+id/fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:navGraph="@navigation/my_nav_graph" />
-
-
创建Fragment
-
创建Navigation Graph,指定destination
-
完成Fragment页面切换
-
使用NavController完成导航
-
class HomeFragment : Fragment() { 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 onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val button = view?.findViewById<Button>(R.id.home_button) //传递参数 方式一 // val bundle=Bundle() // bundle.putString("name","jack") // navigationController.navigate(R.id.action_homeFragment_to_detailFragment,bundle) button?.setOnClickListener { val args = HomeFragmentArgs.Builder().setUserName("Login").build().toBundle() val navigationController = Navigation.findNavController(it) navigationController.navigate(R.id.action_homeFragment_to_detailFragment,args) } } }
-
-
添加页面切换动画效果
-
普通方式与safe args插件方式参数传递
-
普通方式传递
//传递参数 方式一 // val bundle=Bundle() // bundle.putString("name","jack") // navigationController.navigate(R.id.action_homeFragment_to_detailFragment,bundle) //获取传递参数 方式一 // val arguments = arguments // val name = arguments?.get("name") // Log.d("get",name.toString())
-
safe args方式传递
- 在项目gradle中添加插件
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0-alpha06"
- 在模块gradle中启用插件
plugins { id 'com.android.application' id 'k
-
otlin-android’
id ‘androidx.navigation.safeargs’
}
```
* 在graph.XML中添加
* 传递参数
```kotlin
val args = HomeFragmentArgs.Builder().setUserName("Login").build().toBundle()
val navigationController = Navigation.findNavController(it)
navigationController.navigate(R.id.action_homeFragment_to_detailFragment,args)
```
* 接收参数
```kotlin
//获取参数 方式二
val args:HomeFragmentArgs = HomeFragmentArgs.fromBundle(arguments as Bundle)
val userName = args.userName
Log.d("username",userName)
```
NavigationUI
作用
Fragment的切换,除了Fragment页面本身的切换,通常还有APP Bar的变化。为了方便统一管理,Navigation组件引入了NavigationUI类。
案例:点击菜单实现跳转功能
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var navController: NavController
lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navController = Navigation.findNavController(this, R.id.fragment)
appBarConfiguration = AppBarConfiguration.Builder(navController.graph).build()
NavigationUI.setupActionBarWithNavController(this,navController,appBarConfiguration)
//监听页面切换
navController.addOnDestinationChangedListener { controller, destination, arguments ->
Log.d("OnDestinationChangedListener","切换成功")
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.menu_settings, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//首先交给NavigationUI控制ActionBar,搞不定就交给默认的
return NavigationUI.onNavDestinationSelected(item,navController)||super.onOptionsItemSelected(item)
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController,appBarConfiguration)||super.onSupportNavigateUp()
}
}
DetailFragment.kt
class DetailFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
setHasOptionsMenu(true)
return inflater.inflate(R.layout.fragment_detail, container, false)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
//清空菜单栏
menu.clear()
super.onCreateOptionsMenu(menu, inflater)
}
}
更多支持
- APP Bar
- ActionBar
- ToolBar
- CollapsingToolbarLayout
- menu
- 抽屉菜单(DrawLayout+Navigation View)
- 底部菜单(BottomNavigationView)
深层链接DeepLink
-
PendingIntent方式
-
当APP收到某个通知推送,我们希望用户在点击该通知时,能够直接跳转到展示该通知内容的页面,可以通过PendingIntent实现
//获取NotificationManager的实例 manager = Myapplication.context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //创建一个id为normal的通知渠道:创建通知渠道的代码只会在第一次执行的时候才会创建,下次执行时,检测到渠道存在就不会再次创建 val channel = NotificationChannel("normal", "Normal", NotificationManager.IMPORTANCE_DEFAULT) manager.createNotificationChannel(channel) } //按钮点击事件 button?.setOnClickListener { //发送通知 sendNotification() }
/* * 发送通知. * @Description 发送通知 * @Author 倪海 * @Date 21:26 2021/9/27 * @Param * @return **/ private fun sendNotification() { //传入通知渠道ID val notification = NotificationCompat.Builder(Myapplication.context, "normal") .setContentTitle("这是title") .setContentText("这是text") .setSmallIcon(R.drawable.ic_launcher_foreground) .setLargeIcon( BitmapFactory.decodeResource( resources, R.drawable.ic_launcher_foreground ) ) .setContentIntent(PendingIntent()) .build() manager.notify(1, notification) } //点击通知跳转到响应的fragment private fun PendingIntent(): PendingIntent? { val bundle=Bundle() bundle.putString("name","jack") return activity?.let { Navigation.findNavController(it, R.id.home_button) .createDeepLink() .setGraph(R.navigation.my_nav_graph) .setDestination(R.id.detailFragment) .setArguments(bundle) //传递参数 .createPendingIntent() } }
-
-
URL方式
- 当用户通过手机浏览器浏览网站上某个网页时,可以在网页上放置一个类似于“在应用内打开”的按钮,如果用户的手机安装有我们的App,那么通过DeepLink就能打开相应的页面,如果没有安装,那么网站可以导航到应用程序的下载页面,引导用户安装应用程序。
- adb shell am start -a android.intent.action.VIEW -d “http://dongnaoedu.com/fromWeb”