Android Jetpack(7):Navigation

Navigation(导航)概述

Google 在2018年Google I/O开发者大会上发布了一系列辅助android开发者的实用工具,合称Jetpack。

从名字来看,我翻译它叫导航, 我们来看看Google官方对它的描述:

今天,我们宣布推出Navigation组件,作为构建您的应用内界面的框架,重点是让单 Activity 应用成为首选架构。利用Navigation组件对 Fragment 的原生支持,您可以获得架构组件的所有好处(例如生命周期和 ViewModel),同时让此组件为您处理 FragmentTransaction 的复杂性。此外,Navigation组件还可以让您声明我们为您处理的转场。它可以自动构建正确的“向上”和“返回”行为,包含对深层链接的完整支持,并提供了帮助程序,用于将导航关联到合适的 UI 小部件,例如抽屉式导航栏和底部导航。

在JetPack中有一个组件是Navigation,顾名思义它是一个页面导航组件,相对于其他的第三方导航,不同的是它是专门为Fragment的页面管理所设计的。它对于单个Activity的App来说非常有用,因为以一个Activity为架构的App页面的呈现都是通过不同的Fragment来展示的。所以对于Fragment的管理至关重要。通常的实现都要自己维护Fragment之间的栈关系,同时要对Fragment的Transaction操作非常熟悉。为了降低使用与维护成本,所以就有了今天的主角Navigation。

Navigation 用于 Fragment 的管理。他可以让 Fragrant 之间的切换,拥有像 Activity 间一样的跳转。与 DrawerLayout(抽屉式布局)、ActionBar(导航栏)等有简洁完美的对接。

注意:Navigation 需要在 Android Studio 3.3 或更高版本中才可使用,并且在 androidx 中支持的更好。

对于Navigation的使用,我将其归纳于以下四点:

Navigation的基本配置
Navigation的跳转与数据传递
Navigation的页面动画
Navigation的deepLink

Navigation使用流程

1. 在使用之前需要引入Navigation的依赖

dependencies {
  def nav_version = "2.3.1"

  // Java language implementation
  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"

  // Feature module Support
  implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"

  // Testing Navigation
  androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
}

2. 创建 navigation graph (导航图)

我们需要为Navigation创建一个配置文件,它将位于res/navigation/nav_graph.xml,导航图是一个 XML 资源文件。

在res文件夹上 点击右键 -> New -> Android resource file -> 输入文件名称,如:nav_graph -> 选择resource type为Navigation -> 点击 OK。

在这里插入图片描述

在这里插入图片描述

3. 配置创建的xml文件

上面我们创建了一个叫dnav_graph.xml的navigation文件,现在我们需要来设配它来管理fragment。

打开这个文件选择,切换到Design模式后,我们可以看到下面这个界面(恩,一片空白).,我们可以在左上角点击添加图标,进入添加内容的操作。

第一个 Create new destinattion,字面意思创建一个新目标(其实就是创建fragment,当然你也可以手动创建fragment不一定需要在这里创建)。

第二个 placeholder,这个就是重点了, 这是一个管理fragment跳转的节点,我们点击后可以创建它。

为了了解它的使用方式,点击Create new destinattion4次创建4个节点,也就是创建4个Fragment。

在这里插入图片描述
节点创建后可以看到4个节点(看下面图片,这些节点都是我已经导入fragment了,不要急后面会讲解如何导入),这里有一个重点! 你可以点击这些页面(会有一个蓝点),点击蓝点按住向右分配它需要跳转的另外一个页面(它会自动生成一些我们跳转的代码)

在这里插入图片描述

<?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"
    app:startDestination="@id/blankFragment1">


    <!--BlankFragment1包含1个事件:跳转到BlankFragment2 -->
    <fragment
        android:id="@+id/blankFragment1"
        android:name="com.zly.jetpack.BlankFragment1"
        android:label="fragment_blank1"
        tools:layout="@layout/fragment_blank1" >
        <action
            android:id="@+id/action_blankFragment1_to_blankFragment2"
            app:destination="@id/blankFragment2" />


    </fragment>

    <!--BlankFragment2包含2个事件:跳转到BlankFragment3, 跳转到BlankFragment4-->
    <fragment
        android:id="@+id/blankFragment2"
        android:name="com.zly.jetpack.BlankFragment2"
        android:label="fragment_blank2"
        tools:layout="@layout/fragment_blank2" >

        <action
            android:id="@+id/action_blankFragment2_to_blankFragment3"
            app:destination="@id/blankFragment3" />

        <action
            android:id="@+id/action_blankFragment2_to_blankFragment4"
            app:destination="@id/blankFragment4" />
    </fragment>

    <!--blankFragment3没有事件-->
    <fragment
        android:id="@+id/blankFragment3"
        android:name="com.zly.jetpack.BlankFragment3"
        android:label="fragment_blank3"
        tools:layout="@layout/fragment_blank3" />

    <!--blankFragment4没有事件-->
    <fragment
        android:id="@+id/blankFragment4"
        android:name="com.zly.jetpack.BlankFragment4"
        android:label="fragment_blank4"
        tools:layout="@layout/fragment_blank4" />

</navigation>

navigation相关属性

<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"
    app:startDestination="@id/blankFragment1">
android:id="@+id/nav_graph"

这个属性是你这个xml文件navigation的id,很重要,我们需要在activity的xml布局里引用,记得写上不要忘记。

  app:startDestination="@id/blankFragment1"

这个属性是你首次加载的第一个页面,很重要,一般就是第一个fragment。

fragment 里的属性

其实就是一个节点你也可以理解成一个fragment。

   <fragment
        android:id="@+id/blankFragment1"
        android:name="com.zly.jetpack.BlankFragment1"
        android:label="fragment_blank1"
        tools:layout="@layout/fragment_blank1" >
        <action
            android:id="@+id/action_blankFragment1_to_blankFragment2"
            app:destination="@id/blankFragment2" />

    </fragment>

android:id="@+id/blankFragment1"

每一个fragment节点都需要有自己的id,很重要,我们需要在后面的节点上使用这些id指定跳转目标。

 android:name="com.zly.jetpack.BlankFragment1"

这个属性是你这个节点所对应的fragment(需要你导入指定的fragment文件路径),这个很重要。

    android:label="fragment_blank1"

一个标签名称,用于记录这个节点的标签信息(大概可能是在代码里的Intent里获取来知晓此次是那个fragment节点在跳转,没深究了)。

   tools:layout="@layout/fragment_blank1" 

这个属性不是重要的,设置它后你可以在切换到Design模式后看到,视图页面的fragment的预览图(就在上面的图片里,可以直接看到fragment效果)。

action 里的属性

  android:id="@+id/action_blankFragment1_to_blankFragment2"

这个很重要,它是这个跳转动作的id, 这个id我们将在后面的代码中调用,用于执行fragment的跳转。

 app:destination="@id/blankFragment2" 

跳转的目标fragment,这个很重要。

4. 让navigation与Activity关联起来

现在我们已经创建了navigation,但是使用它还需要一个根Activity,它毕竟还是需要依托Activity的。

在Activity的布局文件中显示声明NavGraphFragment,并配置 app:defaultNavHost 和 app:navGraph属性。

<?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/demo_fragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

这是一个宽和高都 match_parent 的Fragment,它的作用就是 导航界面的容器

这并不难以理解,我们需要在Activity中通过 Navigation 展示一系列的Fragment,但是我们需要告诉Navigation 和Activity,这一系列的 Fragment 展示在哪——NavHostFragment应运而生,我把它的作用归纳为 导航界面的容器。

我们就关注fragment的一些属性

android:name="androidx.navigation.fragment.NavHostFragment"

指定 Fragment 的类型为 NavHostFragment。这个非常重要,这是你告知fragment需要使用navigation模式的关键属性,另外它是固定死的,你必需写。

 app:defaultNavHost="true"

将设备的回退操作进行拦截,并将其交给Navigation进行管理。

这是你实现物理按键(比如返回键),是按一下退出一个fragment,还是直接退出这个Activity的关键属性。

让 Navigation 容器处理返回事件,在 Navigation 容器中如果有页面的跳转,点击返回按钮会先处理 容器中 Fragment 页面间的返回,处理完容器中的页面,再处理 Activity 页面的返回。如果值为 false 则直接处理 Activity 页面的返回。

也可以通过重写Activity中的onSupportNavigateUp()方法,Activity将它的 back键点击事件的委托出去,如果当前并非栈中顶部的Fragment, 那么点击back键,返回上一个Fragment。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    }

    override fun onSupportNavigateUp(): Boolean {
        return findNavController(this, R.id.demo_fragment).navigateUp()
    }
}
 app:navGraph="@navigation/nav_graph"

很重要,这就是我们前面创建的navigation的xml文件。

5. 导航图的复用

navigation 中可以包含 navigation 节点:

<?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"
    app:startDestination="@id/page1Fragment">

    <fragment
        android:id="@+id/page1Fragment"
        android:name="com.qingmei2.samplejetpack.ui.main.MainPage1Fragment"
        android:label="fragment_page1"
        tools:layout="@layout/fragment_main_page1">
        <action
            android:id="@+id/action_page2"
            app:destination="@id/page2Fragment" />
    </fragment>

    <fragment
        android:id="@+id/page2Fragment"
        android:name="com.qingmei2.samplejetpack.ui.main.MainPage2Fragment"
        android:label="fragment_page2"
        tools:layout="@layout/fragment_main_page2">
        <action
            android:id="@+id/action_page1"
            app:popUpTo="@id/page1Fragment" />
        <action
            android:id="@+id/action_page3"
            app:destination="@id/nav_graph_page3" />
    </fragment>

    <navigation
        android:id="@+id/nav_graph_page3"
        app:startDestination="@id/page3Fragment">
        <fragment
            android:id="@+id/page3Fragment"
            android:name="com.qingmei2.samplejetpack.ui.main.MainPage3Fragment"
            android:label="fragment_page3"
            tools:layout="@layout/fragment_main_page3" />
    </navigation>

</navigation>

使用include 可以复用相同的导航图:

<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_multiplex"
            app:startDestination="@id/findPwdFragment">
 
    <fragment android:id="@+id/findPwdFragment" 
    		  android:name="cn.eli.jetpack.navigation.FindPwdFragment"
              android:label="fragment_find_pwd" 
              tools:layout="@layout/fragment_find_pwd">
        <action android:id="@+id/action_findPwdFragment_to_nav_graph" 
                app:destination="@id/nav_graph"/>
    </fragment>
 
    <!--引入另一个导航图-->
    <include app:graph="@navigation/nav_graph"/>
 
</navigation>

6. 实现fragment跳转与返回

进入到MainActivity后,首先会自动加载到第一个fragment(BlankFragment1). 然后我们看看如何跳转到其他fragment中。

BlankFragment1跳转到BlankFragment2:

//跳转到BlankFragment2,这个id就是navigation里的action的id
Navigation.findNavController(it).navigate(R.id.action_blankFragment1_to_blankFragment2)

BlankFragment1.java

private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

class BlankFragment1 : Fragment() {
    private var param1: String? = null
    private var param2: String? = null

    companion object {
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            BlankFragment1().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_blank1, container, false)
    }


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)


        btn1.setOnClickListener {
            //跳转到BlankFragment2,这个id就是navigation里的action的id
            Navigation.findNavController(it).navigate(R.id.action_blankFragment1_to_blankFragment2)
        }
    }
}

BlankFragment2跳转到BlankFragment3和BlankFragment4:

       //跳转到BlankFragment2
        btn1.setOnClickListener {
            //这个id就是navigation里的action的id
            Navigation.findNavController(it).navigate(R.id.action_blankFragment2_to_blankFragment3)
        }

        //跳转到BlankFragment2
        btn2.setOnClickListener {
            //这个id就是navigation里的action的id
            Navigation.findNavController(it).navigate(R.id.action_blankFragment2_to_blankFragment4)
        }

返回上一个Fragment:

defaultNavHost 这个属性和返回键有关的,如果把这个属性改为 false,从第一个 Fragment 跳到第二个 Fragment 再按返回键就会直接退出程序。

//方式1
Navigation.findNavController(it).popBackStack()

//方式2		
Navigation.findNavController(it).navigateUp()

NavController 有 navigateUp() 和 popBackStack() 都可以返回上一级,有什么区别:popBackStack() 如果当前的返回栈是空的就会报错,因为栈是空的了,navigateUp() 则不会,还是停留在当前界面。

        //返回上个Fragment
        btnBack.setOnClickListener {
            Navigation.findNavController(it).popBackStack()
			
			// Navigation.findNavController(it).navigateUp()
        }

popBackStack还有个方法:

public boolean popBackStack(@IdRes int destinationId, boolean inclusive) {//...}

第一个参数是 Navigation 文件的 fragment 的 id,不是 action 的,
第二个参数是指是否包含第一个参数 id 那个也弹出栈

可以看到,我们对于Fragment 并非是通过原生的 FragmentManager 和 FragmentTransaction 进行控制的。

获取 NavController 的方式

导航图使用 NavController 控制(目的地)之间的跳转,在 Activity,Fragment 中获取 NavController 有以下 6 种方式:

1.Fragment.findNavController()
2.View.findNavController()
3.Activity.findNavController(viewId: Int) //只有 Activity 中可以使用
4.NavHostFragment.findNavController(Fragment)
5.Navigation.findNavController(Activity, @IdRes int viewId) //只有 Activity 中可以使用
6.Navigation.findNavController(View)

Navigation传参

Navigator提供的navigate方法可以进行传递bundle数据

       btn1.setOnClickListener {
            val bundle=Bundle().apply {
                putString("name","赵丽颖")
            }
            Navigation.findNavController(it).navigate(R.id.action_blankFragment1_to_blankFragment2,bundle)
        }

获取数据:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            val name = it.getString("name")
            Log.e("xyh", "onCreate: $name")
        }
    }

Navigation提供了一个Gradle插件来传递数据

bundle数据的方法在传递数据类型上并不能保证其一致性,为了减少人为精力上的错误,Navigation提供了一个Gradle插件,专门用来保证数据的类型安全

使用它的话需要引入该插件,方式如下:

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.3.0"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}
//java
apply plugin: "androidx.navigation.safeargs"

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

而它的使用方式也很简单,首先参数需要在nav_graph.xml中进行配置。

     <!--BlankFragment1包含1个事件:跳转到BlankFragment2 -->
    <fragment
        android:id="@+id/blankFragment1"
        android:name="com.zly.jetpack.BlankFragment1"
        android:label="fragment_blank1"
        tools:layout="@layout/fragment_blank1">
        <action
            android:id="@+id/action_blankFragment1_to_blankFragment2"
            app:destination="@id/blankFragment2"
            app:enterAnim="@anim/enter_from_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/enter_from_left"
            app:popExitAnim="@anim/slide_out_right"></action>

        <argument
            android:name="age"
            android:defaultValue="0"
            app:argType="integer" />

        <argument
            android:name="name"
            app:argType="string" />


    </fragment>

    <!--BlankFragment2包含2个事件:跳转到BlankFragment3, 跳转到BlankFragment4-->
    <fragment
        android:id="@+id/blankFragment2"
        android:name="com.zly.jetpack.BlankFragment2"
        android:label="fragment_blank2"
        tools:layout="@layout/fragment_blank2">

        <argument
            android:name="age"
            app:argType="integer" />

        <argument
            android:name="name"
            app:argType="string" />


        <action
            android:id="@+id/action_blankFragment2_to_blankFragment3"
            app:destination="@id/blankFragment3"
            app:enterAnim="@anim/enter_from_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/enter_from_left"
            app:popExitAnim="@anim/slide_out_right" />

        <action
            android:id="@+id/action_blankFragment2_to_blankFragment4"
            app:destination="@id/blankFragment4" />
    </fragment>

现在我们从BlankFragment1跳转到BlankFragment2,需要在BlankFragment1的对应action中添加argument,声明对应的参数类型与参数名,也可以通过defaultValue定义参数的默认值与nullable标明是否可空。对应的BlankFragment2接收参数也是一样。

一旦我们如上配置了argument,插件就会自动生成一个以[类名]+Directions的类,而自动生成的类本质是做了跳转与参数的封装,源码如下👇

class BlankFragment1Directions private constructor() {
  private data class ActionBlankFragment1ToBlankFragment2(
    val age: Int,
    val name: String
  ) : NavDirections {
    override fun getActionId(): Int = R.id.action_blankFragment1_to_blankFragment2

    override fun getArguments(): Bundle {
      val result = Bundle()
      result.putInt("age", this.age)
      result.putString("name", this.name)
      return result
    }
  }

  companion object {
    fun actionBlankFragment1ToBlankFragment2(age: Int, name: String): NavDirections =
        ActionBlankFragment1ToBlankFragment2(age, name)
  }
}

跳转并传递:

 btn1.setOnClickListener {
            Navigation.findNavController(it).navigate(
                BlankFragment1Directions.actionBlankFragment1ToBlankFragment2(18, "赵丽颖"
                )
            )
        }

接受参数:

通过BlankFragment2Args来获取传递过来的页面数据。

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            Log.e("xyh", "onCreate: " + BlankFragment2Args.fromBundle(it).name)
            Log.e("xyh", "onCreate: " + BlankFragment2Args.fromBundle(it).age)
        }
    }

跳转动画

在action中不仅可以配置跳转的destination,还可以定义对应页面的转场动画,使用非常简单。

enterAnim:配置进场时目标页面动画
exitAnim: 配置进场时原页面动画
popEnterAnim: 配置回退pop时目标页面动画
popExitAnim: 配置回退pop时原页面动画

     <!--BlankFragment2包含2个事件:跳转到BlankFragment3, 跳转到BlankFragment4-->
    <fragment
        android:id="@+id/blankFragment2"
        android:name="com.zly.jetpack.BlankFragment2"
        android:label="fragment_blank2"
        tools:layout="@layout/fragment_blank2">

        <action
            android:id="@+id/action_blankFragment2_to_blankFragment3"
            app:destination="@id/blankFragment3"
            app:enterAnim="@anim/enter_from_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/enter_from_left"
            app:popExitAnim="@anim/slide_out_right"/>

        <action
            android:id="@+id/action_blankFragment2_to_blankFragment4"
            app:destination="@id/blankFragment4" />
    </fragment>

enter_from_right.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <!--  从右向左滑入  -->
    <translate
        android:duration="1500"
        android:fromXDelta="100%"
        android:toXDelta="0" />

</set>

slide_out_left.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <!--从右向左滑出-->
    <translate
        android:duration="1500"
        android:fromXDelta="0"
        android:toXDelta="-100%" />

</set>

enter_from_left.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <!--  从左向右滑入  -->
    <translate
        android:duration="1500"
        android:fromXDelta="-100%"
        android:toXDelta="0" />

</set>

slide_out_right.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <!--从左向右滑出-->
    <translate
        android:duration="1500"
        android:fromXDelta="0"
        android:toXDelta="100%" />

</set>

动态加载Navigation

有时候不想马上启动 Start Destination,或者从别的地方收到传过来的数据,然后要在 Start Destination 中用的需求,这时就不能在 layout 中写 navGraph,因为写了 navGraph 一启动就会去加载 Start Destination,这时可以用代码去动态加载 Navigation 文件的内容,从 NavHostFragment 入手。

  1. 修改下 Activity 的 layout,把 NavHostFragment 的 navGraph 属性去掉
  <fragment
        android:id="@+id/demo_fragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>
  1. 在 Activity里加载
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var navHostFragment: NavHostFragment = supportFragmentManager.findFragmentById(R.id.demo_fragment) as NavHostFragment
        var navSimple: NavGraph = navHostFragment.navController.navInflater.inflate(R.navigation.nav_graph)
        navHostFragment.navController.graph = navSimple

    }
}

这里先通过 FragmentManager 找到 NavHostFragment,navHostFragment 有 getNavController() 方法,NavController 里 getNavInflater() 方法获得 NavInflater,NavInflater 这个类似 LayoutInflater, 通过 inflate() 去加载 Navigation,设置了数据后通过 NavController 的 setGraph(NavGraph graph) 就加载出来了。

DeepLink的使用

我们回想一下对于多个Activity我需要实现deepLink效果,应该都是在AndroidManifest.xml中进行配置scheme、host等。而对于单个Activity也需要实现类似的效果,Navigation也提供了对应的实现,而且操作更简单。

Navigation组件提供了对深层链接(DeepLink)的支持。通过该特性,使用deep-link可以创建深层链接,类似activity的自定义URL使用Scheme方式来跳转,可以直接跳转到指定fragment/activity。

Navigation提供的是deepLink标签,可以直接在nav_graph.xml进行配置,例如👇

<fragment android:id="@+id/ballFragment"
          android:name="com.fomin.demo.bar.BallFragment"
          android:label="BallFragment"
          tools:layout="@layout/ball_fragment">
          
    <deepLink app:uri="http://www.fomin.com/login"/>
</fragment>

需要在AndroidManifest.xml中将我们的deepLink进行配置,在Activity中使用nav-graph标签:

        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <nav-graph android:value="@navigation/nav_graph"/>

        </activity>

NavGraphFragment:导航界面的容器

即使我们使用原生的API,想展示一个Fragment,我们首先也需要 定义一个容器承载它。以往,它可能是一个 RelativeLayout 或者 FrameLayout,而现在,它被替换成了 NavGraphFragment。

这也就说明了,我们为什么要往Activity的layout文件中提前扔进去一个NavGraphFragment,因为我们需要导航的这些Fragment都展示在NavGraphFragment上面。

NavGraphFragment内部实例化了一个FrameLayout, 作为ViewGroup的载体,导航并展示其它Fragment。

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        FragmentContainerView containerView = new FragmentContainerView(inflater.getContext());
        // When added via XML, this has no effect (since this FragmentContainerView is given the ID
        // automatically), but this ensures that the View exists as part of this Fragment's View
        // hierarchy in cases where the NavHostFragment is added programmatically as is required
        // for child fragment transactions
        containerView.setId(getContainerId());
        return containerView;
    }
public final class FragmentContainerView extends FrameLayout {
}

Navigation和BottomNavigationView的使用,可以实现底部导航栏

新建activity,选择Bottom Navigation Activity,代码都已经自动生成。

在这里插入图片描述

单Activity多Fragment逻辑

之前的应用开发大部分应该是每个独立的页面对应一个Activity, 一个App中包含多个Activity. 在Navigation组件的设计中是单Activity对应多Fragment,一个App中只有一个Activity, 而且这个Activity通常只是一个容器,用来处理后面Fragment之间的切换。

在Fragment刚出来时就有这个方向的讨论,现在看来应该比较明确了,官方一直在优化Fragment, 解决生命周期相关的问题。以后肯定希望开发者更多的使用Fragment.

Activity在设计的时候虽然比较独立,完善。但是后面开发的时候越来越臃肿,而且性能不是特别高,例如在启动时和大量数据传递的时候。这是由很早之前的设计原理决定的,短时间内也无法修改。所以官方一直想解决该问题。随着现在AndroidX中的Fragment组件越来越完善,问题越来越少,也建议大家尽量使用Fragment来替换Activity。

Navigation博客

Android Navigation

Android架构组件-Navigation的使用(一)

Android Navigation Architecture Component 使用详解

Android官方架构组件Navigation:大巧不工的Fragment管理框架

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值