Jetpack Navigation
Posted 编程的平行世界
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack Navigation相关的知识,希望对你有一定的参考价值。
前言
如果文章排版有问题,或者图片无法显示,请到我的主运营博客阅读
🏀什么是Navigation?官方文档的话有点不容易让人理解。所以,这里用我自己的话来总结一下,我们在处理Fragment是需要通过写Fragment的事务去操作Fragment的,而Navigation的出现是为了解决我们之前开发的一些痛点。Navigation主要用于实现Fragment代替Activity的页面导航功能,让Fragment能够轻松的实现跳转与传递参数,我们可以通过使用Navigation,让Fragment代替android项目中绝大多数的Activity。但需要注意的是在使用Navigation切换页面生命周期的变化情况,避免开发过程中踩坑。
🌟官方文档:https://developer.android.google.cn/jetpack/androidx/releases/navigation
🌟navigation项目地址:https://github.com/googlecodelabs/android-navigation
💡本文Demo地址:https://github.com/taxze6/Jetpack_learn/tree/main/Jetpack_basic_learn/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
目录,然后依次选择New
→ Android Resource Directory
。此时系统会显示 New Resource Directory
对话框。Directory name
输入你的文件夹名(一般为navigation
),Resource type
选择navigation
②右键navigation
文件夹,然后new
→Navigation Resource File
在File name
中输入名称(常用nav_graph_main或nav_graph)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rv4ObFso-1657166276980)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4f667ebfc2d242debcd50f9978baddb4~tplv-k3u1fbpfcp-watermark.image?)]
第三步:创建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
一样的:SecondFragment
,ThirdFragment
我们也可以通过Navigation graph
创建
我们在新建好的nav_graph_main.xml
下,在右上角切换到Design
模式,然后在Navigation Editor
中,点击Create new destination
,选择所需要的Fragment
后,点击Finish
,你就会发现Fragment
已经出现在我们可以拖动的面板中了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCf67eXE-1657166276982)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a863a4a9d78549e5b029e2c65e1b3e4a~tplv-k3u1fbpfcp-watermark.image?)]
第四步:将Fragment拖入面板并进行跳转配置
只需要在Navigation Editor
中双击想要的Fragment
就会被加入到面板中啦。
点击其中一个Fragment
,你会发现,在他的右边会有一个小圆点,拖曳小圆点指向想要跳转的那个Fragment
,我们这里设置FirstFragment
→ SecondFragment
→ ThirdFragment
→ FirstFragment
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-29JRH2s5-1657166276983)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9034fe09af4f468380a6db6dbdc9a841~tplv-k3u1fbpfcp-watermark.image?)]
我们将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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Oqt1Byf-1657166276984)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf5421b074df46d79ee9fcbe38c66721~tplv-k3u1fbpfcp-watermark.image?)]
fragment
标签就代表这是一个Fragmentaction
标签定义了页面跳转的行为,就是上图中的每条线,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
,表示我们将Activity
的back
点击事件委托出去
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" />
③最后的效果图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ogs53lc9-1657166276986)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/74bc27f878394383ad52f8f972c2b3f7~tplv-k3u1fbpfcp-watermark.image?)]
跳转动画&自定义动画
我们会发现,刚刚的例子中,我们在跳转界面时,没有左滑右滑进入界面的动画,显得很生硬,所以我们要给跳转过程中添加上动画。
①添加默认动画:
在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>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YjFcKytn-1657166276987)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d08632a0d1d442e4857165b30a720a8b~tplv-k3u1fbpfcp-watermark.image?)]
enterAnim
: 跳转时的目标页面动画exitAnim
: 跳转时的原页面动画popEnterAnim
: 回退时的目标页面动画popExitAnim
:回退时的原页面动画
②自定义动画
💡 在真正的业务需求中是会有很多不同的跳转动画的,官方提供的默认动画是不够的,所以我们要学会自定义动画。
⑴创建Animation资源文件
右键点击res
目录,然后依次选择New
→ Android 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>
常用的属性:
属性 | 介绍 |
---|---|
alpha | 透明度设置效果 |
scale | 大小缩放效果 |
rotate | 旋转效果 |
translate | 位移效果 |
常用设置:
属性 | 介绍 |
---|---|
android:duration | 动画时长 |
fromXX | 开始状态 |
toXX | 结束状态 |
⑶使用自定义动画文件
使用自定义动画也是和使用默认动画是一样的。
旋转效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25BfCEKe-1657166276988)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e7978e5abe9445e8b5219ba79c9ddca5~tplv-k3u1fbpfcp-watermark.image?)]
如何传递数据?
💡Navigation 支持您通过定义目的地参数将数据附加到导航操作。一般情况下,建议传递少量数据。例如传递userId,而不是具体用户的信息。如果需要传递大量的数据,还是推荐使用ViewModel。
在nav_graph_main.xml
文件中的Design
模式下。点击选中其中的Fragment
。在右侧的Attributes
面板中,点击Arguments
选项右侧的加号。添加需要的参数。添加参数后,在箭头 Action
上点击,会在右边的 Argument Default Values
中显示这个userId
变量,在xml中也可以看到
//伪代码,请勿直接cv
<fragment
...
>
...
<argument
android:name="userId"
android:defaultValue="1"
app:argType="integer" />
</fragment>
代码处理: