navigation—这么好用的导航框架你确定不来看看?(代码片段)

初一十五啊 初一十五啊     2023-03-15     285

关键词:

前言

🏀什么是Navigation?官方文档的话有点不容易让人理解。所以,这里用我自己的话来总结一下,我们在处理Fragment是需要通过写Fragment的事务去操作Fragment的,而Navigation的出现是为了解决我们之前开发的一些痛点。Navigation主要用于实现Fragment代替Activity的页面导航功能,让Fragment能够轻松的实现跳转与传递参数,我们可以通过使用Navigation,让Fragment代替android项目中绝大多数的Activity。但需要注意的是在使用Navigation切换页面生命周期的变化情况,避免开发过程中踩坑。

前几天刚好录制了一个实战第三方框架扩展:重构Navigation资源体系,植入扩展业务
的视频,刚好可以对应这篇文章,结合看。

使用Navigation具有什么优势?

  • 处理Fragment事务
  • 默认情况下,能正确处理往返操作
  • 为动画和转换提供标准化资源
  • 实现和处理深层链接
  • 包括导航界面模式,例如抽屉式导航栏和底部导航,我们只需要完成少量的代码编写
  • Safe Args - 可在目标之间导航和传递数据时提供类型安全的Gradle插件
  • ViewModel支持 - 您可以将ViewModel的范围限定为导航图,以在图标的目标之间共享与界面相关的数据

如何使用Navigation呢?

Navigation目前仅AndroidStudio 3.2以上版本支持,如果您的版本不足3.2,请点此下载最新版AndroidStudio(2202年了应该没有人还在用3.2以下的版本吧!🐤)

在开始学习Navigation组件之前,我们需要先对Navigation主要组成部分有个简单的了解,Navigation由三部分组成:

  • Navigation graph:一个包含所有导航相关信息的 XML 资源
  • NavHostFragment:一种特殊的Fragment,用于承载导航内容的容器
  • NavController:管理应用导航的对象,实现Fragment之间的跳转等操作

下面我们正式开始学习Navigation

第一步:添加依赖

//project的Navigation依赖设置
dependencies 
  //文章发布时的最新稳定版本:
  def nav_version = "2.4.2"

  // 使用java作为开发语言添加下面两行:
  implementation "androidx.navigation:navigation-fragment:$nav_version"
  implementation "androidx.navigation:navigation-ui:$nav_version"

  // Kotlin:
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"


//Compose版本:
implementation "androidx.navigation:navigation-compose:$nav_version"

第二步:创建导航图

①右键点击res目录,然后依次选择NewAndroid Resource Directory。此时系统会显示 New Resource Directory对话框。Directory name输入你的文件夹名(一般为navigation),Resource type选择navigation
②右键navigation文件夹,然后newNavigation Resource FileFile name中输入名称(常用nav_graph_mainnav_graph

第三步:创建Fragment

为了让跳转更加的丰富,我们这里创建三个Fragment

我们可以自己手动创建:

FirstFragment:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FirstFragment">
​
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="hello world" />
</FrameLayout>
class FirstFragment : Fragment() 
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? 
        return inflater.inflate(R.layout.fragment_first, container, false)
    

另外创建两个和FirstFragement一样的:SecondFragmentThirdFragment

我们也可以通过Navigation graph创建
我们在新建好的nav_graph_main.xml下,在右上角切换到Design模式,然后在Navigation Editor中,点击Create new destination,选择所需要的Fragment后,点击Finish,你就会发现Fragment已经出现在我们可以拖动的面板中了。

第四步:将Fragment拖入面板并进行跳转配置

只需要在Navigation Editor中双击想要的Fragment就会被加入到面板中啦。
点击其中一个Fragment,你会发现,在他的右边会有一个小圆点,拖曳小圆点指向想要跳转的那个Fragment,我们这里设置FirstFragmentSecondFragmentThirdFragmentFirstFragment

我们将nav_graph_main.xml切换到Code下,我们来解读一下xml

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph_main"
    app:startDestination="@id/firstFragment">
    <fragment
        android:id="@+id/firstFragment"
        android:name="com.taxze.jetpack.navigation.FirstFragment"
        android:label="fragment_first"
        tools:layout="@layout/fragment_first" >
        <action
            android:id="@+id/action_firstFragment_to_secondFragment2"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.taxze.jetpack.navigation.SecondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" >
        <action
            android:id="@+id/action_secondFragment_to_thirdFragment2"
            app:destination="@id/thirdFragment" />
    </fragment>
    <fragment
        android:id="@+id/thirdFragment"
        android:name="com.taxze.jetpack.navigation.ThirdFragment"
        android:label="fragment_third"
        tools:layout="@layout/fragment_third" >
        <action
            android:id="@+id/action_thirdFragment_to_firstFragment"
            app:destination="@id/firstFragment" />
    </fragment>
</navigation>
  • navigation是根标签,通过startDestination配置默认启动的第一个页面,这里配置的是firstFragment,我们可以在代码中手动改mainFragment(启动时的第一个Fragment),也可以在可视化面板中点击Fragment,再点击Assign Start Destination,同样可以修改mainFragment
  • fragment标签就代表这是一个Fragment
  • action标签定义了页面跳转的行为,就是上图中的每条线,destination定义跳转的目标页,还可以加入跳转时的动画.

💡注意:在fragment标签下的android:name属性,其中的包名的是否正确声明

第五步:处理MainActivity

①编辑MainActivity的布局文件,在布局文件中添加NavHostFragment。我们需要告诉Navigation和Activity,我们的Fragment展示在哪里,所以NavHostFragment其实就是导航界面的容器

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
​
    <fragment
        android:id="@+id/nav_host_fragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
  • fragment标签下的android:name是用于指定NavHostFragment
  • app:navGraph是用于指定导航视图的
  • app:defaultNavHost=true是在每一次Fragment切换时,将点击记录在堆栈中保存起来,在需要退出时,按下返回键后,会从堆栈拿到上一次的Fragment进行显示。但是在某些情况下,这样的操作不是很友好,不过好在我们只需要将app:defaultNavHost=true改为app:defaultNavHost=false或者删除这行即可。在其为false的情况下,无论怎么切换Fragment,再点击返回键就都直接退出app。当然我们也可以对其堆栈进行监听,从而来实现,点击一次返回键回到主页,再点击一次返回键退出app

②修改MainActivity

我们重写了onSupportNavigateUp,表示我们将Activityback点击事件委托出去

class MainActivity : AppCompatActivity() 
    override  fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    
​
    override fun onSupportNavigateUp(): Boolean 
        return findNavController(R.id.nav_host_fragment).navigateUp()
    

第六步:处理Fragment的对应跳转事件

Fragment布局:给一个按钮用于跳转,一个TextView用于标识,三个Fragment布局相同

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FirstFragment">
​
    <Button
        android:id="@+id/firstButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="点击跳转到第二个fragment" />
​
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="@string/hello_first_fragment" />
​
</FrameLayout>
​
//secondFragment中加入:
<Button
        android:id="@+id/firstButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="点击跳转到第三个fragment" />
​
//thirdFragment中加入:
<Button
        android:id="@+id/firstButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="返回第一个fragment" />

②配置Fragment对应的跳转事件

class FirstFragment : Fragment() 
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? 
        return inflater.inflate(R.layout.fragment_first, container, false)
    
   
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<Button>(R.id.firstButton).apply 
         setOnClickListener 
          it.findNavController().navigate(R.id.action_firstFragment_to_secondFragment2)
         
        
    

​
//secondFragment中加入:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<Button>(R.id.firstButton).apply 
         setOnClickListener 
           it.findNavController().navigate(R.id.action_secondFragment_to_thirdFragment2)
         
        

​
//thirdFragment中加入:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<Button>(R.id.firstButton).apply 
            setOnClickListener 
              it.findNavController().navigate(R.id.action_thirdFragment_to_firstFragment)
            
        

其中的R.id.action_firstFragment_to_secondFragment2这样的标识,是在nav_graph_main.xml的action标签下配置的。

<action
    android:id="@+id/action_firstFragment_to_secondFragment2"
    app:destination="@id/secondFragment" />

③最后的效果图:

跳转动画&自定义动画

我们会发现,刚刚的例子中,我们在跳转界面时,没有左滑右滑进入界面的动画,显得很生硬,所以我们要给跳转过程中添加上动画。

①添加默认动画:
nav_graph_main.xml文件中的Design模式下,点击连接两个Fragment的线。然后你会发现在右侧会出现一个Animations的面板,然后我们点击enterAnim这些选项最右侧的椭圆点,然后就会弹出Pick a Resoure的面板,我们可以在这里选择需要的动画,这里我们就设置nav_default_enter_anim。同理exitAnim我们也设置一个动画,nav_default_exit_anim。然后运行代码你就发现一个渐变的跳转动画。然后配置动画后会发现action多了动画相关的属性

<fragment
    android:id="@+id/firstFragment"
    android:name="com.taxze.jetpack.navigation.FirstFragment"
    android:label="fragment_first"
    tools:layout="@layout/fragment_first" >
    <action
        android:id="@+id/action_firstFragment_to_secondFragment2"
        app:destination="@id/secondFragment"
        app:enterAnim="@anim/nav_default_enter_anim"
        app:exitAnim="@anim/nav_default_exit_anim"
        />
</fragment>

  • enterAnim: 跳转时的目标页面动画
  • exitAnim: 跳转时的原页面动画
  • popEnterAnim: 回退时的目标页面动画
  • popExitAnim:回退时的原页面动画

②自定义动画

💡 在真正的业务需求中是会有很多不同的跳转动画的,官方提供的默认动画是不够的,所以我们要学会自定义动画。

⑴创建Animation资源文件
右键点击res目录,然后依次选择NewAndroid Resource File。此时系统会显示 New Resource File对话框。File name输入你的文件夹名(这里设置为slide_from_left),Resource type选择Animation,然后我们就可以发下在res目录下多了一个anim目录,里面存放着我们自定义的动画。

⑵编写我们的动画代码
这里举几个常用的例子:

//左滑效果
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="-100%"
        android:toXDelta="0%">
    </translate>
</set>
//右滑效果
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="0%"
        android:toXDelta="100%">
    </translate>
</set>
//旋转效果
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="1000"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.0"
        android:toYScale="1.0" />
​
    <rotate
        android:duration="1000"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="360" />
</set>

常用的属性:

常用设置:

⑶使用自定义动画文件

使用自定义动画也是和使用默认动画是一样的。

旋转效果:

如何传递数据?

💡Navigation 支持您通过定义目的地参数将数据附加到导航操作。一般情况下,建议传递少量数据。例如传递userId,而不是具体用户的信息。如果需要传递大量的数据,还是推荐使用ViewModel

nav_graph_main.xml文件中的Design模式下。点击选中其中的Fragment。在右侧的Attributes 面板中,点击Arguments选项右侧的加号。添加需要的参数。添加参数后,在箭头 Action 上点击,会在右边的 Argument Default Values中显示这个userId变量,在xml中也可以看到

<fragment
   ... 
   >
   ...
    <argument
        android:name="userId"
        android:defaultValue="1"
        app:argType="integer" />
</fragment>

代码处理:

//默认将 箭头 Action 中设置的参数传递过去
it.findNavController().navigate(R.id.action_firstFragment_to_secondFragment2)

动态传递数据
①我们可以使用Bundle在目的地之间传递参数

view.findViewById<Button>(R.id.firstButton).setOnClickListener 
    val bundle = Bundle()
    bundle.putString("userId", "1")
    val navController = it.findNavController()
    navController.navigate(R.id.action_firstFragment_to_secondFragment2, bundle)

②然后我们就可以接受参数啦

在对应的Fragment:

val tv = view.findViewById<TextView>(R.id.textView)
tv.text = arguments?.getString("userId")

Activity使用setGraph切换不同的Navigation

通常情况下,我们不止一个navigation的文件,我们需要根据业务情况去判断使用哪个,这里就可以用到我们的setGraph去切换不同的Navigation了。

①把activity_mainfragment标签下的app:navGraph这个属性去除

<fragment
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:defaultNavHost="true"/>

②在代码中通过setGraph设置app:navGraph

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initNav(1)
    
    private fun initNav(id: Int) 
        var controller = Navigation.findNavController(this@MainActivity, R.id.nav_host_fragment)
        if (id == 1) 
            //设置对应的app:navGraph
            controller.setGraph(R.navigation.first)
        
        if (id == 2) 
            controller.setGraph(R.navigation.second)
        
        this@MainActivity.finish()
    

到此,我们已经讲完了Navigation大部分的使用情况,下面来讲解几个重要的点,并通过一个案例来学习总结这篇文章吧。

NavController

💡 我们在前面已经讲了Navigation的使用,在处理Fragment的对应跳转事件时,我们用到了findNavController这个方法,其实呢,这个就是Navigation的三部分中的NavController中的一个api。那么什么是NavController呢?它是负责操作Navigation框架下的Fragment的跳转与退出、动画、监听当前Fragment信息,当然这些是基本操作。因为除了在Fragment中调用,在实际情况中它也可以在Activity中调用。如果能灵活的使用它,它可以帮你实现任何形式的页面跳转,也可以使用TabLayout配合Navigation在主页进行分页设计。

如何获取NavController实例呢?

在前面的基础使用中我们也用到了。

//activity:
//Activity.findNavController(viewId: Int)
findNavController(R.id.nav_host_fragment).navigateUp()
​
//Fragment:
//Fragment.findNavController()
//View.findNavController()
findNavController().navigate(R.id.action_thirdFragment_to_firstFragment)

Navigation常用操作
popBackStack弹出Fragment

现在我们从oneFragment跳转到secondFragment在到thirdFragment,然后我们想从thirdFragment回到secondFragment那儿就可以使用popBackStack

override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        .....
        btn.setOnClickListener
            //弹出Fragment
            controller.popBackStack()
        

②弹出到指定的Fragment
也是使用popBackStack,这个方法可以实现清空中间导航栈堆的需求

//xxxFragment弹出到指定的Fragment。
//第二个参数的布尔值如果为true则表示我们参数一的Fragment一起弹出,意思就是如果是false就popBackStack到
//xxxFragment,如果是true,就在xxxFragment在popBackStack()一次
controller.popBackStack(xxxFragment,true)

navigateUp() 向上导航

findNavController(R.id.nav_host_fragment).navigateUp()

navigateUp也是执行返回上一级Fragment的功能。和popBackStack功能一样。那么既然它存在肯定是有它特殊的地方的。navigateUp向上返回的功能其实也是调用popBackStack的。 但是,navigateUp的源码里多了一层判断,判断这个Navigation是否是最后一个Fragment。使用popBackStack()如果当前的返回栈是空的就会报错,因为栈是空的了,navigateUp()则不会,还是停留在当前界面。

④添加导航监听

val listener: NavController.OnDestinationChangedListener =
                object : OnDestinationChangedListener() 
                    fun onDestinationChanged(
                        controller: NavController,
                        destination: NavDestination,
                        @Nullable arguments: Bundle?
                    ) 
                         Log.d(TAG, "onDestinationChanged: id = $destination.getId()")
                  
          
//添加监听
controller.addOnDestinationChangedListener(listener)
//移除监听
controller.removeOnDestinationChangedListener(listener)

⑤获取当前导航目的地
使用getCurrentDestination可以获取当前导航的目的地

//获取
val destination = controller.getCurrentDestination()
Log.d(TAG, "onCreate: NavigatorName = $destination.getNavigatorName()")
Log.d(TAG, "onCreate: id = $destination.getId()")
Log.d(TAG, "onCreate: Parent = $destination.getParent()")

⑥判断当前页面显示的Fragment是不是目标Fragment

//可直接cv
fun <F : Fragment> isActiveFragment(fragmentClass: Class<F>): Boolean 
    val navHostFragment = this.supportFragmentManager.fragments.first() as NavHostFragment
    navHostFragment.childFragmentManager.fragments.forEach 
        if (fragmentClass.isAssignableFrom(it.javaClass)) 
            return true
        
    
    return false

使用 Safe Args 确保类型安全

💡 通过上面的NavController我们就可以实现fragment之间的跳转,但是Google建议使用 Safe Args Gradle 插件实现。这个插件可以生成简单的对象和构建器类,这些类就可以在目的地之间进行安全的导航和参数的传递啦。

那么,该如何使用 Safe Args呢。

①在项目最外面的build.gradle中加入

//将其放在plugins之前
buildscript 
    repositories 
        google()
    
    dependencies 
        def nav_version = "2.4.2"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    

②在appmodule下的build.gradle加入

如需生成适用于 Java 模块或JavaKotlin混合模块的Java语言代码

apply plugin: "androidx.navigation.safeargs"

如需生成适用于 Kotlin独有的模块的Kotlin代码,请添加以下行

apply plugin: "androidx.navigation.safeargs.kotlin"

💡 要使用Safe Args,必须在gradle.properties这个文件中加入

android.useAndroidX=true
android.enableJetifier=true

为了避免生成的类名和方法名过于复杂,需要对导航图进行调整,修改了actionid,因为自动生成的id太长了,会导致插件生成的类名和方法名也很长。

<fragment
    android:id="@+id/blankFragment"
    android:name="com.taxze.jetpack.navigation.BlankFragment"
    android:label="fragment_blank"
    tools:layout="@layout/fragment_blank" >
    <action
        android:id="@+id/toSecond"
            修改此处id
        app:destination="@id/blankFragment2" />
</fragment>

然后就会为我们生成BlankFragmentBlankFragment2的类啦,然后就可以和上面一样使用,传递参数啦。

var action = BlankFragment.actionJump("111")

也可以使用set方法对对应的参数进行赋值

action.setParam("222")

通过Navigation模仿WeChat底部跳转

💡通过Navigation实现开发中超级常用的底部跳转功能
话不多说,先上效果图:

①右键点击res目录,然后依次选择NewAndroid Resource File。此时系统会显示 New Resource File对话框。File name输入你的文件夹名(这里设置为menu),Resource type选择Menu,然后我们就可以发下在res目录下多了一个menu目录,里面存放着我们底部跳转的item

②进入menu.xmlDesign

填入四个Item,并分别设置idtitleicon

③创建四个Fragment,并在Navigation中建立链接

Fragment的布局十分简单,这里就放一个的代码。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HomeFragment">
​
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Home" />
</FrameLayout>
class HomeFragment : Fragment() 
​
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? 
        return inflater.inflate(R.layout.fragment_home, container, false)
    
​

nav_graph_main.xml建立连接,注意这里每个Fragmentid要和menu.xml中的id一一对应

④在activity_main中使用BottomNavigationView建立底部跳转

这里在drawable下创建一个selector_menu_text_color.xml用于区分当前的Item

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#818181" android:state_checked="false"/>
    <item android:color="#45C01A" android:state_checked="true"/>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
​
    <fragment
        android:id="@+id/nav_host_fragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph_main" />
​
    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:itemIconTint="@drawable/selector_menu_text_color"
        app:labelVisibilityMode="labeled"
        app:itemTextColor="@drawable/selector_menu_text_color"
        app:menu="@menu/menu" />
</androidx.constraintlayout.widget.ConstraintLayout>

⑤在MainActivty中设置切换逻辑

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        //去除标题栏
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(R.layout.activity_main)
​
        val navController: NavController = Navigation.findNavController(this, R.id.nav_host_fragment)
        val navigationView = findViewById<BottomNavigationView>(R.id.nav_view)
        NavigationUI.setupWithNavController(navigationView, navController)
    

这样我们就可以实现效果图中的效果啦,具体代码可以查看Git仓库😉

尾述

这篇文章已经很详细的讲了Jetpack Navigation的大部分用法,不过在看完文章后,你仍需多多实践,相信你很快就可以掌握Navigation啦😺

作者:编程的平行世界
链接:https://juejin.cn/post/7117476993372782629

vue-路由精讲-导航守卫(代码片段)

...,听起来怪怪的,但既然官方文档是这样翻译的,就姑且这么叫吧。1.全局守卫你可以使用 router.beforeEach 查看详情

这么好用的pdf密码移除器,你知道吗

现在网上很多pdf文件都是加密文件,想要使用pdf文件没有密码就无法打开使用,有网友就问了,有密码的pdf文件怎么打开使用呢?通过这篇文章。我就来告诉大家一个好用的PDF密码移除器。想要修改密码,便捷的迅捷PDF在线转换... 查看详情

1024程序猿节~~你确定不来吐槽一波?(满满福利等着你~)

今天小助手将#1024程序猿节#这篇文章发到技术群和朋友圈里,一石激起千层浪,立马引起激烈的讨论和朋友圈刷屏的现象,一方面是这篇文章引起了程序猿的强烈共鸣,另一方面,文末送的福利简直让人无法抗拒,血脉喷张~~不... 查看详情

官方架构组件navigation管理fragment框架(代码片段)

...除样板代码AndroidJetpack管理乏味的活动,例如后台任务、导航和生命周期管理,你可以专注于让你的app更棒的东西。构建高质量、健壮的app基于现代设计实践,AndroidJetpack组件可以减少崩溃和内存泄漏,且向后兼容。Navigation本文... 查看详情

vue---全局守卫(代码片段)

...,听起来怪怪的,但既然官方文档是这样翻译的,就姑且这么叫吧。全局守卫你可以使用 router.beforeEach 注册一个全局前置守卫:con 查看详情

android日志—navigation(代码片段)

...gation是一个框架,用于Android应用中的“目标”之间的导航,该框架提供一致的API,无论目标是作为Fragment、Activity还是其他组件的实现。NavGraph在一个集中位置包含所有导航相关信息的XML资源NavHost显示导航图中目标的... 查看详情

csdn编辑器这么好用的功能,还有人不知道?

听说还有人不知道CSDN编辑器更新了这些功能?这么好用的功能怎么还能不知道呢?今天我来给你捋一捋。功能一:定时发布富文本编辑展示定时发布功能位置,在发布文章最底下,见下图↓Markdown编辑器展示... 查看详情

说到jetpack路由组件熟悉的只有arouter框架?今天带你深入理解功能强大的navigation架构(代码片段)

...的一个路由组件。此刻你的脑海中可能会浮现阿里ARouter框架。如果你熟悉ARouter但是对Navigation比较陌生,那么你先简单把它们联系在一起,有个直观的感受。「如果你对ARouter和Navigation都不太熟悉, 查看详情

说到jetpack路由组件熟悉的只有arouter框架?今天带你深入理解功能强大的navigation架构(代码片段)

...的一个路由组件。此刻你的脑海中可能会浮现阿里ARouter框架。如果你熟悉ARouter但是对Navigation比较陌生,那么你先简单把它们联系在一起,有个直观的感受。「如果你对ARouter和Navigation都不太熟悉, 查看详情

在反应导航中禁用后退按钮(代码片段)

我正在使用反应原生导航(react-navigation)StackNavigator。它在应用程序的整个生命周期中从“登录”页面开始。我不想有后退选项,返回登录屏幕。有谁知道登录屏幕后它是如何隐藏在屏幕上的?顺便说一下,我还使用以下方法将... 查看详情

python爬虫资料!有案例有课件与代码,你确定不来领取吗?

为什么学python爬虫? 第一,市场需求旺盛,大数据时代到来,除了大公司有能力生产数据之外,其他公司都是依靠爬虫来获取数据。第二,爬虫的技术比较要求低,只要稍微有一定的基础,认真学... 查看详情

vue2.0实现导航守卫(路由守卫)

...,听起来怪怪的,但既然官方文档是这样翻译的,就姑且这么叫吧。贴上文档地址:https://router.vuejs.org/zh-cn/advanced/navig 查看详情

flutter系列之:在flutter中使用导航navigator(代码片段)

...?一起来看看吧。flutter中的NavigatorNavigator是flutter中用来导航的关键组件。我们先来看下Navigator的定义:classNavigatorextendsStatefulWidgetNavigator首先是一个StatefulWidget,为什么是一个有状态的widget呢?这是因为Navigator需要在内部报错一... 查看详情

如何修复实体框架中的“无法确定导航属性表示的关系”错误

】如何修复实体框架中的“无法确定导航属性表示的关系”错误【英文标题】:Howtofix\'Unabletodeterminetherelationshiprepresentedbynavigationproperty\'errorinEntityFramework【发布时间】:2019-09-0121:22:56【问题描述】:当我尝试在我的.NETCore2.1网站... 查看详情

reactnative导航栏不能并列

您好,ReactNative导航栏不能并列,这是因为ReactNative没有提供一个内置的导航栏组件。为了实现并列导航栏,你需要使用第三方库,比如react-native-navigation或react-navigation。这些库提供了一个可以用来创建并列导航栏的API,可以让... 查看详情

ons-navigator 一页模板中的多个导航器

】ons-navigator一页模板中的多个导航器【英文标题】:ons-navigatorMultipleNavigatorinonepagetemplate【发布时间】:2015-07-0321:46:10【问题描述】:我正在尝试从OnsenUI框架创建一个应用程序。我正在使用多个导航器。在第一页(模板)中,... 查看详情

jetpackcompose接收返回参数

...有在导航图中的所有屏幕都是可组合项的情况下,才可以这么做。因此,若要构建混合应用,我们建议使用基于fragment的Navigation组件,并使用fragment存储基于view的屏幕、Compose屏幕和同时使用view和Compose的屏幕。应用中的每个屏幕... 查看详情

vue-router实现导航守卫(路由卫士)(代码片段)

...,听起来怪怪的,但既然官方文档是这样翻译的,就姑且这么叫吧。贴上文档地址:https://router.vuejs.org/zh-cn/advanced/navig 查看详情