美文网首页Android开发经验谈Android技术知识
现学现用Android Jetpack - Navigation

现学现用Android Jetpack - Navigation

作者: 码农小陈 | 来源:发表于2020-06-01 22:34 被阅读0次

    前言

    即学即用Android Jetpack系列Blog的目的是通过学习Android Jetpack完成一个简单的Demo,本文是即学即用Android Jetpack系列Blog的第一篇。

    记得去年第一次参加谷歌开发者大会的时候,就被Navigation的图形导航界面给迷住了,一句卧槽就代表了小王的全部心情~,我们可以看一下来自网络的一张图片:

    所以,Android Jetpack学习之旅就开始了。

    image

    本人打算每周学习一个组件(上图的左上区域),最后将所学的组件组成一个简单的Demo。同时,刚刚过去的2019年谷歌开发者大会宣布亲儿子Kotlin成为开发Android的首选语言,所以本文的Demo也将都会采用Kotlin编写。

    本章结束后登录部分完成效果:

    语言:Kotlin
    Demo地址:https://github.com/mCyp/Hoo

    目录

    一、简介

    1. 定义

    Navigation是什么呢?谷歌的介绍视频上说:

    Navigation是一个可简化Android导航的库和插件

    更确切的来说,Navigation是用来管理Fragment的切换,并且可以通过可视化的方式,看见App的交互流程。这完美的契合了Jake Wharton大神单Activity的建议。

    2. 优点

    • 处理Fragment的切换(上文已说过)
    • 默认情况下正确处理Fragment的前进和后退
    • 为过渡和动画提供标准化的资源
    • 实现和处理深层连接
    • 可以绑定ToolbarBottomNavigationViewActionBar
    • SafeArgs(Gradle插件) 数据传递时提供类型安全性
    • ViewModel支持

    3. 准备

    如果想要进行下面的学习,你需要 3.2 或者更高的Android studio

    4. 学习方式

    最好的学习方式仍然是通过官方文档,下面是官方的学习地址:
    谷歌官方教程:Navigation Codelab
    谷歌官方文档:Navigation
    官方Demo:Demo地址

    二、实战

    在实战之前,我们先来了解一下Navigation中最关键的三要素,他们是:

    名词 解释
    Navigation Graph(New XML resource) 如我们的第一张图所示,这是一个新的资源文件,用户在可视化界面可以看出他能够到达的Destination(用户能够到达的屏幕界面),以及流程关系。
    NavHostFragment(Layout XML view) 当前Fragment的容器
    NavController(Kotlin/Java object) 导航的控制者

    可能我这么解释还是有点抽象,做一个不是那么恰当的比喻,我们可以将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导航

    1. 创建基础目录:资源文件res目录下创建navigation目录 -> 右击navigation目录New一个Navigation resource file

    2. 创建一个Destination,如果说navigation是我们的导航工具,Destination是我们的目的地,在此之前,我已经写好了一个WelcomeFragmentLoginFragmentRegisterFragment,添加Destination的操作完成后如下所示:

      image

    除了可视化界面之外,我们仍然有必要看一下里面的内容组成,login_navigation.xml:

    <navigation
        ...
        android:id="@+id/login_navigation"
        app:startDestination="@id/welcome">
    
        <fragment
            android:id="@+id/login"
            android:name="com.joe.jetpackdemo.ui.fragment.login.LoginFragment"
            android:label="LoginFragment"
            tools:layout="@layout/fragment_login"
            />
    
        <fragment
            android:id="@+id/welcome"
            android:name="com.joe.jetpackdemo.ui.fragment.login.WelcomeFragment"
            android:label="LoginFragment"
            tools:layout="@layout/fragment_welcome">
            <action
                .../>
            <action
                .../>
        </fragment>
    
        <fragment
            android:id="@+id/register"
            android:name="com.joe.jetpackdemo.ui.fragment.login.RegisterFragment"
            android:label="LoginFragment"
            tools:layout="@layout/fragment_register"
            >
    
            <argument
                .../>
        </fragment>
    </navigation>
    
    

    我在这里省略了一些不必要的代码。让我们看一下navigation标签的属性:

    属性 解释
    app:startDestination 默认的起始位置

    第三步 建立NavHostFragment

    我们创建一个新的LoginActivity,在activity_login.xml文件中:

    <androidx.constraintlayout.widget.ConstraintLayout
        ...>
    
        <fragment
            android: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>
    
    

    有几个属性需要解释一下:

    属性 解释
    android:name 值必须是androidx.navigation.fragment.NavHostFragment,声明这是一个NavHostFragment
    app:navGraph 存放的是第二步建好导航的资源文件,也就是确定了Navigation Graph
    app:defaultNavHost="true" 与系统的返回按钮相关联

    第四步 界面跳转、参数传递和动画

    WelcomeFragment中,点击登录和注册按钮可以分别跳转到LoginFragmentRegisterFragment中。

    这里我使用了两种方式实现:

    方式一 利用ID导航

    目标:WelcomeFragment携带keyname的数据跳转到LoginFragmentLoginFragment接收后显示。
    Have a account ? Login按钮的点击事件如下:

    btnLogin.setOnClickListener {
                // 设置动画参数
                val navOption = navOptions {
                    anim {
                        enter = R.anim.slide_in_right
                        exit = R.anim.slide_out_left
                        popEnter = R.anim.slide_in_left
                        popExit = 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将数据传到RegisterFragmentRegisterFragment接收后显示。
    再看一下已经展示过的login_navigation.xml

    <navigation
        ...>
    
        <fragment
            ...
            />
    
        <fragment
            android:id="@+id/welcome"
            >
            <action
                android:id="@+id/action_welcome_to_login"
                app:destination="@id/login"/>
            <action
                android: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>
    
        <fragment
            android:id="@+id/register"
            ...
            >
    
            <argument
                android:name="EMAIL"
                android:defaultValue="2005@qq.com"
                app:argType="string"/>
        </fragment>
    </navigation>
    
    

    细心的同学可能已经观察到navigation目录下的login_navigation.xml资源文件中的action标签和argument标签,这里需要解释一下:
    action标签

    属性 作用
    app:destination 跳转完成到达的fragment的Id
    app:popUpTo fragment中弹出,直到某个Id的fragment

    argument标签

    属性 作用
    android:name 标签名字
    app:argType 标签的类型
    android:defaultValue 默认值

    点击Android studio中的Make Project按钮,可以发现系统为我们生成了两个类:

    image

    WelcomeFragment中的JOIN US按钮点击事件:

    btnRegister.setOnClickListener {
                val action = WelcomeFragmentDirections
                    .actionWelcomeToRegister()
                    .setEMAIL("TeaOf1995@Gamil.com")
                findNavController().navigate(action)
    }
    
    

    RegisterFragment中的接收:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
    
            // ...
            val safeArgs:RegisterFragmentArgs by navArgs()
            val email = safeArgs.email
            mEmailEt.setText(email)
    }
    
    

    以及效果:

    需要提及的是,如果不用Safe Argsaction可以由Navigation.createNavigateOnClickListener(R.id.next_action, null)方式生成,感兴趣的同学可以自行编写。

    三、更多

    Navigation可以绑定menusdrawersbottom navigation,这里我们以bottom navigation为例,我先在navigation目录下新创建了main_navigation.xml,接着新建了MainActivity,下面则是activity_main.xml:

    <LinearLayout
        ...>
    
        <fragment
            android: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.BottomNavigationView
            android: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: BottomNavigationView
    
        override fun onCreate(savedInstanceState: Bundle?) {
            //...
            val host: NavHostFragment = supportFragmentManager.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment
            val navController = host.navController
            initWidget()
            initBottomNavigationView(bottomNavigationView,navController)
        }
    
        private fun initBottomNavigationView(bottomNavigationView: BottomNavigationView, navController: NavController) {
            bottomNavigationView.setupWithNavController(navController)
        }
    
        private fun initWidget() {
            bottomNavigationView = findViewById(R.id.navigation_view)
        }
    }
    
    

    效果:

    四、总结

    上图概括了本文的一些知识点,当然还有一些知识点没有涉及,比如深层连接等,其次,本文只是一篇入门型博客,关于更深层次的学习,本人会逐步进行。本人水平有限,文章难免有误,欢迎指正。
    Over~

    作者:九心_
    链接:https://www.jianshu.com/p/66b93df4b7a6

    相关文章

      网友评论

        本文标题:现学现用Android Jetpack - Navigation

        本文链接:https://www.haomeiwen.com/subject/utpezhtx.html