Fragment简介

Fragment自从Android 3.0引入开始,它所承担的角色就是显而易见的。它之于Activity就如html片段之于页面,好处无需赘述。

Fragment的生命周期和Activity一样,都不是由开发人员而是由系统维护的。自然而然的,每当它们被重建时,系统只会去调用它们的无参构造器,带参构造器会被无视。那如果要在它们创建时传入初始化数据咋办呢?这也是为什么类似OnCreateXXX方法里会有Bundle这个玩意儿的存在,就是用于开发人员存取相关的数据,如下所示:

/**
* Use the [ThumbnailsFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class ThumbnailsFragment() : Fragment() {
private var albumTag: String? = null companion object {
@JvmStatic
fun newInstance(albumTag: String?) =
ThumbnailsFragment().apply {
arguments = Bundle().apply {
putString("albumTag", albumTag)
}
}
} override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
albumTag = it.getString("albumTag")
}
} /*other code*/
}

底部导航栏切换Fragment

效果如下

BottomNavigationView

底部是BottomNavigationView组件,各个菜单在另外xml中定义,然后通过app:menu="xxx"指定。此处菜单定义如下

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/title_home" /> <item
android:id="@+id/navigation_dashboard"
android:icon="@drawable/ic_dashboard_black_24dp"
android:title="@string/title_dashboard" /> <item
android:id="@+id/navigation_notifications"
android:icon="@drawable/ic_notifications_black_24dp"
android:title="@string/title_notifications" /> </menu>

然后在代码中设置BottomNavigationView.setOnNavigationItemSelectedListener,判断当前选中的菜单项,手动切换Fragment,需要用到FragmentTransaction。如下示例

    override fun onClick(view: View?) {
val trans = activity.supportFragmentManager.beginTransaction()
val fragments = activity.supportFragmentManager.fragments
fragments.forEach {
if (it.isVisible) {
trans.hide(it) //隐藏当前显示的fragment
}
}
val tag = (view as TextView).text.toString()
val thumbnailsFragment = ThumbnailsFragment.newInstance(tag)
//fragment_main_container就是居中的那块区域,用于显示各个fragment
trans.add(R.id.fragment_main_container, thumbnailsFragment, tag)
trans.show(thumbnailsFragment)
trans.addToBackStack(null) //将本次操作入栈
trans.commitAllowingStateLoss() //提交
}

注意addToBackStack方法,该方法是为了实现回退时——用户按返回按钮或程序执行回退(配合popBackStack)——界面能返回到本次操作前的状态。也可指定tag,在跨[多次]操作回退时有用。注意此处入栈的是操作信息,而非fragment。

Navigation

上述手动控制Fragment的切换太麻烦。2018 I/O大会上,Google隆重推出一个新的架构组件:Navigation。它提供了多Fragment之间的转场、栈管理。在抽屉式/底部/顶部导航栏的需求中都可以使用这个组件。

使用:在res目录下新建navigation文件夹,然后新建一个navigation graph设为bottom_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/mobile_navigation"
app:startDestination="@+id/navigation_home"> <fragment
android:id="@+id/navigation_home"
android:name="com.eixout.presearchapplication.ui.home.HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home" /> <fragment
android:id="@+id/navigation_dashboard"
android:name="com.eixout.presearchapplication.ui.dashboard.DashboardFragment"
android:label="@string/title_dashboard"
tools:layout="@layout/fragment_dashboard" /> <fragment
android:id="@+id/navigation_notifications"
android:name="com.eixout.presearchapplication.ui.notifications.NotificationsFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications" />
</navigation>

注意每个fragment的id要和之前定义的menu的id保持一致。可以设置转场动画,还可以设置每个fragment跳转的目标(destination),目标可以是 Activity或Fragment,也可以自定义。

然后在Activity布局文件中添加一个Fragment,设置name属性为android:name="androidx.navigation.fragment.NavHostFragment"。在传统的单Activity多Fragment场景中,我们往往需要为Activity添加一个FrameLayout作为Fragment的容器。在Navigation中HavHostFragment就是Fragment的容器(HavHostFragment继承了NavHost。The NavHost interface enables destinations to be swapped in and out.),其中设置app:navGraph="@navigation/bottom_navigation"使之与navigation graph建立联系。

    <fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/bottom_navigation"
other_config="..." />

app:defaultNavHost: If set to true, the navigation host will intercept the Back button.

最后将导航栏与Navigation关联

val navController = findNavController(R.id.nav_host_fragment)
bottomNavigationView.setupWithNavController(navController)

如此便大功告成了。

如果不依赖导航栏,而是手动跳转,则可以使用NavController的相关方法,比如在Activity里navController.navigate(actionId)

问题

Navigation和FragmentTransaction方式最好不要同时使用,它俩的返回堆栈似乎不是同一个,回退时会有问题。不能同时使用还使得下面两个问题不好解决。

  1. 使用Navigation,Fragment可以相互跳转没问题,但状态丢失了。比如A下滑一定距离后跳转到B,B回退到A,A的下滑状态丢失,仍是从头部开始显示。

  2. 每次点击BottomNavigationView的菜单项,对应的Fragment会recreate,这其实不是我们想要的,我们预期的应该是Fragment第一次创建后就一直复用,既保留了当前状态也不会对后端造成不必要的调用。

如果使用FragmentTransaction很好处理,只要缓存一个Fragment集合即可(若要保留Fragment的状态,比如滑动位置,可以使用supportFragmentManager.saveFragmentInstanceState(fragment)fragment.setInitialSavedState(savedState)加载,也可以使用hide/show(fragment)的方式),但用了Navigation后就没办法了。可以看看Navigation, Saving fragment state评论区的吐槽,里面也有临时的一些解决方案(不实用)。

FragmentTransaction本身也有对状态信息的处理考量,参看commit(), commitNow()和commitAllowingStateLoss()

参考资料

嵌套Fragment的使用及常见错误

Fragment 生命周期和使用

Android解惑 - 为什么要用Fragment.setArguments(Bundle bundle)来传递参数

BottonNavigationView+Fragment切换toolbar标题栏

手把手教你使用Android官方组件Navigation

Playing with Navigation Architecture Components

The Navigation Architecture Component Tutorial: Getting Started

Handle Complex Navigation Flow with Single-Activity Architecture and Android Jetpack’s Navigation component

导航到目的地-popUpTo 和 popUpToInclusive

Difference between add(), replace(), and addToBackStack()

【从零开始撸一个App】Fragment和导航中的使用的更多相关文章

  1. 【从零开始撸一个App】Kotlin

    工欲善其事必先利其器.像我们从零开始撸一个App的话,选择最合适的语言是首要任务.如果你跟我一样对Java蹒跚的步态和僵硬的语法颇感无奈,那么Kotlin在很大程度上不会令你失望.虽然为了符合JVM规 ...

  2. 【从零开始撸一个App】PKCE

    一个成功的App背后肯定有一堆后端服务提供支撑,认证授权服务(Authentication and Authorization Service,以下称AAS)就是其中之一,它是约束App.保障资源安全 ...

  3. 【从零开始撸一个App】Dagger2

    Dagger2是一个IOC框架,一般用于Android平台,第一次接触的朋友,一定会被搞得晕头转向.它延续了Java平台Spring框架代码碎片化,注解满天飞的传统.尝试将各处代码片段串联起来,理清思 ...

  4. 【从零开始撸一个App】RecyclerView的使用

    目标 前段时间打造了一款简单易用功能全面的图片上传组件,现在就来将上传的图片以图片集的形式展现到App上.出于用户体验考虑,加载新图片采用[无限]滚动模式,Android平台上我们优选Recycler ...

  5. Expo大作战(四)--快速用expo构建一个app,expo中的关键术语

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  6. 鼠标经过导航中li时,一个彩色模块跟着鼠标移动

    1.鼠标经过导航中li时,一个活动的li跟随鼠标移动,最终移动到鼠标的停留的位置.(如需鼠标离开后让活动的li回到初始位置,则用jq hover事件,当鼠标离开时,给活动的li设置left是0) 2. ...

  7. Android在应用中依据包名启动另外一个APP

    以下为TestIntentData工程 MainActivity如下: package cn.testintentdata; import java.util.List; import android ...

  8. Electron: 从零开始写一个记事本app

    Electron介绍 简单来说,Electron就是可以让你用Javascript.HTML.CSS来编写运行于Windows.macOS.Linux系统之上的桌面应用的库.本文的目的是通过使用Ele ...

  9. Eclipse创建第一个Servlet(Dynamic Web Project方式)、第一个Web Fragment Project(web容器向jar中寻找class文件)

    创建第一个Servlet(Dynamic Web Project方式) 注意:无论是以注解的方式还是xml的方式配置一个servlet,servlet的url-pattern一定要以一个"/ ...

随机推荐

  1. Language Guide (proto3) | proto3 语言指南(十二)定义服务

    Defining Services - 定义服务 如果要在RPC(Remote Procedure Call,远程过程调用)系统中使用消息类型,可以在.proto文件中定义RPC服务接口,协议缓冲区编 ...

  2. vscode 刚安装运行cnpm命令报错

    平时的开发工具什么都用,最近手贱把vscode卸载掉了,然而重新安装时,自已以前的什么配置都没了~~~~~~,又开始从头搞起,但是一切安装配置完毕,执行cnpm命令时报错,晕!!!!!! 解决办法:执 ...

  3. SonarQube+jenkins-自动化持续代码扫描

    SonarQube+jenkins-自动化持续代码扫描 1.SonarQube 1.1 SonarQube介绍 1.1.1 SonarQube 工作流程 1. 2 Docker方式安装SonarQub ...

  4. centos /usr/local 和/opt 安装软件你什么不同../configure --prefix=/usr...

    /usr/local下一般是你安装软件的目录,这个目录就相当于在windows下的programefiles这个目录(所有文件在一个文件夹) /opt这个目录是一些大型软件的安装目录,或者是一些服务程 ...

  5. dedecms新建内容模型以及如何添加字段

    dedecms新建内容模型以及如何添加字段 内容模型就是我们所说的频道模型,利用频道模型可以实现其使用他的栏目具备一些功能,比如说,图片模型,在使用他的栏目中就可以发表多个图片,并且能够达到相册的功能 ...

  6. Spring MVC 处理一个请求的流程分析

    Spring MVC是Spring系列框架中使用频率最高的部分.不管是Spring Boot还是传统的Spring项目,只要是Web项目都会使用到Spring MVC部分.因此程序员一定要熟练掌握MV ...

  7. Java ArrayList源码分析(含扩容机制等重点问题分析)

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  8. Java-Graphics类的绘图方法实现

    Java-Graphics(画图类) 就比如画一个矩形,你给出矩形左上角坐标,再给出矩形长度和宽度就可以在JFrame上画出来一个矩形 除了矩形之外,还可以画椭圆.圆.圆弧.线段.多边形.图像等 下面 ...

  9. python对csv文件读写的两种方式 和 读写文件编码问题处理

    ''' 如果文件读取数据出错,可以考虑加一个encoding属性,取值可以是:utf-8,gbk,gb18030 或者加一个属性error,取值为ignore,例如 open(path, encodi ...

  10. Strongly connected HDU - 4635 原图中在保证它不是强连通图最多添加几条边

    1 //题意: 2 //给你一个有向图,如果这个图是一个强连通图那就直接输出-1 3 //否则,你就要找出来你最多能添加多少条边,在保证添加边之后的图依然不是一个强连通图的前提下 4 //然后输出你最 ...