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 入手。
- 修改下 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"/>
- 在 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。