JetPack之Navigation

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

JetPack之Navigation_第1张图片
JetPack之Navigation_第2张图片

  • 完成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)
      
              }
          }
      }
      
  • 添加页面切换动画效果
    JetPack之Navigation_第3张图片

  • 普通方式与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![在这里插入图片描述](https://img-blog.csdnimg.cn/3525ab8b20e34c1e9ce95b73fc3e8cb3.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5YCq5piv5LiA5LiqSVTmsJHlt6U=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
      

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”

你可能感兴趣的:(android,jetpack)