Android多回退栈实践(二)

上一篇:[Android多回退栈实践(一)]

在上一篇文章中,我们介绍了Android中的多回退栈,并使用FragmentManager实现了最朴素的多回退栈用例。接下来,我们将借助AndroidNavigation组件,更加方便的实现多回退栈。
已知我们已经有6个页面:
MusicFavoriteCollectionMusicDetailFavoriteDetailCollectionDetail
引入Jetpack Navigation之后,我们构建一个Graph:

<?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"
    android:id="@+id/nav_multi_stack"
    app:startDestination="@id/music">

    <navigation
        android:id="@+id/music"
        app:startDestination="@+id/music_main">
        <fragment
            android:id="@+id/music_main"
            android:name="xxx.MusicFragment"
            android:label="MusicFragment">
            <action
                android:id="@+id/action_music_to_detail"
                app:destination="@id/music_detail" />
        </fragment>
        <fragment
            android:id="@+id/music_detail"
            android:name="xxx.MusicDetailFragment"
            android:label="MusicDetailFragment" />
    </navigation>

    <navigation
        android:id="@+id/favorite"
        app:startDestination="@+id/favorite_main">
        <fragment
            android:id="@+id/favorite_main"
            android:name="xxx.FavoriteFragment"
            android:label="FavoriteFragment">
            <action
                android:id="@+id/action_favorite_to_detail"
                app:destination="@id/favorite_detail" />
        </fragment>
        <fragment
            android:id="@+id/favorite_detail"
            android:name="xxx.FavoriteDetailFragment"
            android:label="FavoriteDetailFragment" />
    </navigation>

    <navigation
        android:id="@+id/collection"
        app:startDestination="@+id/collection_main">
        <fragment
            android:id="@+id/collection_main"
            android:name="xxx.CollectionFragment"
            android:label="CollectionFragment">
            <action
                android:id="@+id/action_collection_to_detail"
                app:destination="@id/collection_detail" />
        </fragment>
        <fragment
            android:id="@+id/collection_detail"
            android:name="xxx.CollectionDetailFragment"
            android:label="CollectionDetailFragment" />
    </navigation>
</navigation> 

我们仔细看下这里面的细节,我们构建的一个大的Graph中,其实包含了三个嵌套的Graph,里面分别有不同的跳转Action
Music->MusicDetailFavorite->FavoriteDetailCollection->CollectionDetail
我们修改一下_Music->MusicDetail_的跳转代码,剩余两个同理,也需要修改:

root.thing.setOnClickListener {
    findNavController().navigate(R.id.action_music_to_detail)
} 

此时,我们的主Fragment跳转到详情FragmentAction就已经使用Navigation套件的跳转方法了。
现在,我们来配置主页。
主页的xml布局:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0px"
        android:layout_height="0px"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@id/bottom_nav"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_multi_stack" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        style="@style/Widget.MaterialComponents.BottomNavigationView.Colored"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:menu="@menu/bottom_navigation_multistack" />

</androidx.constraintlayout.widget.ConstraintLayout> 

更改后的主页代码:

class MultiStackPage : AppCompatActivity() {


    private var mSelectId = -1

    private val controller by lazy { (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_multi_stack)

        findViewById<BottomNavigationView>(R.id.bottom_nav).let { nav ->
            nav.setOnItemSelectedListener {

                if (mSelectId == it.itemId) {
                    return@setOnItemSelectedListener true
                }

                when (it.itemId) {
                    R.id.action_music -> {
                        controller.navigate(
                            R.id.music, null, NavOptions.Builder().setLaunchSingleTop(true).setRestoreState(true).setPopUpTo(
                                controller.graph.findStartDestination().id, inclusive = false, saveState = true
                            ).build()
                        )
                    }
                    R.id.action_favorite -> {
                        controller.navigate(
                            R.id.favorite, null, NavOptions.Builder().setLaunchSingleTop(true).setRestoreState(true).setPopUpTo(
                                controller.graph.findStartDestination().id, inclusive = false, saveState = true
                            ).build()
                        )
                    }

                    R.id.action_collection -> {
                        controller.navigate(
                            R.id.collection, null, NavOptions.Builder().setLaunchSingleTop(true).setRestoreState(true).setPopUpTo(
                                controller.graph.findStartDestination().id, inclusive = false, saveState = true
                            ).build()
                        )
                    }
                }

                mSelectId = it.itemId

                true
            }

            nav.selectedItemId = R.id.action_music
        }
    }
} 

其实非常简单,我们着重看一个跳转是如何处理的:

controller.navigate(
    R.id.music, null, NavOptions.Builder().setLaunchSingleTop(true).setRestoreState(true).setPopUpTo(
        controller.graph.findStartDestination().id, inclusive = false, saveState = true
    ).build()
) 

在点击BottomNavigationViewitem的时候,我们使用NavControler.navigate进行跳转,第一个参数R.id.music代表我们要跳转的嵌套图,我们现在有三个嵌套图,点击第一个_Music_的时候,跳转到_Music_的嵌套图,当然,我们还需要配置后面的NavOptions

  • setLaunchSingleTop如果为true,表示如果我们重新回到当前的item,该item放置在顶部,同时也可以避免多份拷贝。
  • setRestoreState如其函数名,表示恢复保存的状态到回退栈。
  • setPopUpTo,表示当前回退栈退栈,同时saveState = true保存当前弹出的操作。

我们在上一篇文章中,已经知道了restoreState,以及saveState的用法,这里不再赘述。
配置完成NavOptions之后,我们就可以进行正常的跳转了。

此时,我们就已经完成借助Navigation实现了多回退栈。

可能大部分读者会认为,这也太麻烦了。有没有更简单一点的方法实现?当然有,AndroidJetpack Navigation组件中,提供了一个androidx.navigation:navigation-ui-ktx的依赖,我们引入这个依赖,能更快速的实现BottomNavigationViewFragment的多回退栈策略。步骤如下:

  1. 更改Menu文件的实现,id需要和NavigationGraph中图的id保持一致。```

    <?xml version="1.0" encoding="utf-8"?>
    
    
  2. 修改主页的代码,只需要一步:```
    findViewById(R.id.bottom_nav).let { nav ->
    nav.setupWithNavController(controller)
    }

    
    

完毕。
此时,我们就借助NavigationUI实现了多回退栈,很方便,不是么?我们不需要编写额外的代码,只需要在定义Menu时注意Menu Itemid即可,NavigationUI早已为我们准备好了一切。

以上,我们便完成了关于Android多回退栈的所有实践方法,总结一下,有如下三种方法可供选择:

  • 使用FragmentManager手动控制回退栈,重点接口是saveBackStackrestoreBackStack
  • 借助于Navigation,使用NavControler.navigate控制回退栈。
  • 借助于NavigationUI,解放双手,无需做额外的管理。

在具体项目中,我们更推荐后两种方法,简单,高效。

祝各位好运!

文末

要想成为架构师,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

![在这里插入图片描述](https://img-blog.csdnimg.cn/06e41b3932164f0db07014d54e6e5626.png) 相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取【保证100%免费】↓↓↓

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值