前言
即学即用Android Jetpack系列Blog的目的是通过学习Android Jetpack
完成一个简单的Demo,本文是即学即用Android Jetpack系列Blog的第一篇。
记得去年第一次参加谷歌开发者大会的时候,就被Navigation
的图形导航界面给迷住了,一句卧槽就代表了小王的全部心情~,我们可以看一下来自网络的一张图片:
所以,Android Jetpack学习之旅就开始了。
image
本人打算每周学习一个组件(上图的左上区域),最后将所学的组件组成一个简单的Demo。同时,刚刚过去的谷歌开发者大会宣布亲儿子Kotlin
成为开发Android的首选语言,所以本文的Demo也将都会采用Kotlin
编写。
本章结束后登录部分完成效果:
语言:Kotlin
Demo地址:/mCyp/Hoo
目录
一、简介
1. 定义
Navigation
是什么呢?谷歌的介绍视频上说:
Navigation
是一个可简化Android导航的库和插件
更确切的来说,Navigation
是用来管理Fragment
的切换,并且可以通过可视化的方式,看见App的交互流程。这完美的契合了Jake Wharton大神单Activity的建议。
2. 优点
处理Fragment
的切换(上文已说过)默认情况下正确处理Fragment
的前进和后退为过渡和动画提供标准化的资源实现和处理深层连接可以绑定Toolbar
、BottomNavigationView
和ActionBar
等SafeArgs
(Gradle插件) 数据传递时提供类型安全性ViewModel
支持
3. 准备
如果想要进行下面的学习,你需要 3.2 或者更高的Android studio
。
4. 学习方式
最好的学习方式仍然是通过官方文档,下面是官方的学习地址:
谷歌官方教程:Navigation Codelab
谷歌官方文档:Navigation
官方Demo:Demo地址
二、实战
在实战之前,我们先来了解一下Navigation
中最关键的三要素,他们是:
可能我这么解释还是有点抽象,做一个不是那么恰当的比喻,我们可以将Navigation Graph
看作一个地图,NavHostFragment
看作一个车,以及把NavController
看作车中的方向盘,Navigation Graph
中可以看出各个地点(Destination)和通往各个地点的路径,NavHostFragment
可以到达地图中的各个目的地,但是决定到什么目的地还是方向盘NavController
,虽然它取决于开车人(用户)。
第一步 添加依赖
模块层的build.gradle
文件需要添加:
ext.navigationVersion = "2.0.0"dependencies {//... implementation "androidx.navigation:navigation-fragment-ktx:$rootProject.navigationVersion"implementation "androidx.navigation:navigation-ui-ktx:$rootProject.navigationVersion"}
如果你要使用SafeArgs
插件,还要在项目目录下的build.gradle
文件添加:
buildscript {ext.navigationVersion = "2.0.0"dependencies {classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"}}
以及模块下面的build.gradle
文件添加:
apply plugin: 'kotlin-android-extensions'apply plugin: 'androidx.navigation.safeargs'
第二步 创建navigation导航
创建基础目录:资源文件res
目录下创建navigation
目录 -> 右击navigation
目录New一个Navigation resource file
创建一个Destination
,如果说navigation
是我们的导航工具,Destination
是我们的目的地,在此之前,我已经写好了一个WelcomeFragment
、LoginFragment
和RegisterFragment
,添加Destination
的操作完成后如下所示:
除了可视化界面之外,我们仍然有必要看一下里面的内容组成,login_navigation.xml
:
<navigation...android:id="@+id/login_navigation"app:startDestination="@id/welcome"><fragmentandroid:id="@+id/login"android:name="com.joe.jetpackdemo.ui.fragment.login.LoginFragment"android:label="LoginFragment"tools:layout="@layout/fragment_login"/><fragmentandroid:id="@+id/welcome"android:name="com.joe.jetpackdemo.ui.fragment.login.WelcomeFragment"android:label="LoginFragment"tools:layout="@layout/fragment_welcome"><action.../><action.../></fragment><fragmentandroid:id="@+id/register"android:name="com.joe.jetpackdemo.ui.fragment.login.RegisterFragment"android:label="LoginFragment"tools:layout="@layout/fragment_register"><argument.../></fragment></navigation>
我在这里省略了一些不必要的代码。让我们看一下navigation标签
的属性:
第三步 建立NavHostFragment
我们创建一个新的LoginActivity
,在activity_login.xml
文件中:
<androidx.constraintlayout.widget.ConstraintLayout...><fragmentandroid:id="@+id/my_nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"app:navGraph="@navigation/login_navigation"app:defaultNavHost="true"android:layout_width="match_parent"android:layout_height="match_parent"/></androidx.constraintlayout.widget.ConstraintLayout>
有几个属性需要解释一下:
第四步 界面跳转、参数传递和动画
在WelcomeFragment
中,点击登录和注册按钮可以分别跳转到LoginFragment
和RegisterFragment
中。
这里我使用了两种方式实现:
方式一 利用ID导航
目标:WelcomeFragment
携带key
为name
的数据跳转到LoginFragment
,LoginFragment
接收后显示。
Have a account ? Login
按钮的点击事件如下:
btnLogin.setOnClickListener {// 设置动画参数val navOption = navOptions {anim {enter = R.anim.slide_in_rightexit = R.anim.slide_out_leftpopEnter = R.anim.slide_in_leftpopExit = R.anim.slide_out_right}}// 参数设置val bundle = Bundle()bundle.putString("name","TeaOf")findNavController().navigate(R.id.login, bundle,navOption)}
后续LoginFragment
的接收代码比较简单,直接获取Fragment中的Bundle
即可,这里不再出示代码。最后的效果:
方式二 利用Safe Args
目标:WelcomeFragment
通过Safe Args
将数据传到RegisterFragment
,RegisterFragment
接收后显示。
再看一下已经展示过的login_navigation.xml
:
<navigation...><fragment.../><fragmentandroid:id="@+id/welcome"><actionandroid:id="@+id/action_welcome_to_login"app:destination="@id/login"/><actionandroid:id="@+id/action_welcome_to_register"app:enterAnim="@anim/slide_in_right"app:exitAnim="@anim/slide_out_left"app:popEnterAnim="@anim/slide_in_left"app:popExitAnim="@anim/slide_out_right"app:destination="@id/register"/></fragment><fragmentandroid:id="@+id/register"...><argumentandroid:name="EMAIL"android:defaultValue="@"app:argType="string"/></fragment></navigation>
细心的同学可能已经观察到navigation
目录下的login_navigation.xml
资源文件中的action
标签和argument
标签,这里需要解释一下:
action标签
argument标签
点击Android studio中的Make Project按钮,可以发现系统为我们生成了两个类:
image
WelcomeFragment
中的JOIN US
按钮点击事件:
btnRegister.setOnClickListener {val action = WelcomeFragmentDirections.actionWelcomeToRegister().setEMAIL("TeaOf1995@")findNavController().navigate(action)}
RegisterFragment
中的接收:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// ...val safeArgs:RegisterFragmentArgs by navArgs()val email = safeArgs.emailmEmailEt.setText(email)}
以及效果:
需要提及的是,如果不用Safe Args
,action
可以由Navigation.createNavigateOnClickListener(R.id.next_action, null)
方式生成,感兴趣的同学可以自行编写。
三、更多
Navigation
可以绑定menus
、drawers
和bottom navigation
,这里我们以bottom navigation
为例,我先在navigation
目录下新创建了main_navigation.xml
,接着新建了MainActivity
,下面则是activity_main.xml
:
<LinearLayout...><fragmentandroid:id="@+id/my_nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"app:navGraph="@navigation/main_navigation"app:defaultNavHost="true"android:layout_height="0dp"android:layout_weight="1"/><com.google.android.material.bottomnavigation.BottomNavigationViewandroid:id="@+id/navigation_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/white"app:itemIconTint="@color/colorAccent"app:itemTextColor="@color/colorPrimary"app:menu="@menu/menu_main"/></LinearLayout>
MainActivity
中的处理也十分简单:
class MainActivity : AppCompatActivity() {lateinit var bottomNavigationView: BottomNavigationViewoverride fun onCreate(savedInstanceState: Bundle?) {//...val host: NavHostFragment = supportFragmentManager.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragmentval navController = host.navControllerinitWidget()initBottomNavigationView(bottomNavigationView,navController)}private fun initBottomNavigationView(bottomNavigationView: BottomNavigationView, navController: NavController) {bottomNavigationView.setupWithNavController(navController)}private fun initWidget() {bottomNavigationView = findViewById(R.id.navigation_view)}}
效果:
四、总结
上图概括了本文的一些知识点,当然还有一些知识点没有涉及,比如深层连接
等,其次,本文只是一篇入门型博客,关于更深层次的学习,本人会逐步进行。本人水平有限,文章难免有误,欢迎指正。
如果觉得《现学现用Android Jetpack - Navigation》对你有帮助,请点赞、收藏,并留下你的观点哦!