展开说说:Android Fragment完全解析-卷一

1、是什么

Fragment 中文意思是碎片,Android 3.0推出的一个系统组件,主打一个在应用界面中可模块化又可重复使用。

Fragment 它很独立,它可以定义和管理自己的布局,具有自己的生命周期,并且可以处理自己的输入事件。

Fragment 很粘人,它不能独立存在。它们必须由 activity 或其他 fragment 托管即fragment可以内嵌fragment使用同一 activity 或多个 activity 中可以使用同一 fragment 类的多个实例但是要注意解耦避免让一个 fragment 依赖另一个 fragment 或一个 fragment 操控另一个 fragment。

2、怎么用

Fragment一般是两种用法,使用管理器FragmentManager+事务FragmentTransaction和fragment搭配Viewpager。本文将分析第一种,后面一种会在下一篇文章分析。

activity 是放置应用的全局的界面元素,而Fragment 更适合定义和管理单个屏幕或部分屏幕的界面,可以更轻松地在运行时修改 activity 的外观。大部分应用的首页都采用了Fragment+底部切换选项按钮的布局。

Fragment类有这么样一个提示:

<li>Your activity must extend {@link FragmentActivity}
<li>You must call {@link FragmentActivity#getSupportFragmentManager} to get the
{@link FragmentManager}

也就是说 Fragment 必须嵌入 AndroidX FragmentActivity 中使用并获取管理器FragmentManager。AppCompatActivity 是FragmentActivity 的子类,因此如果我们的Activity是继承AppCompatActivity 就可以放心使用。

举个例子,一个Activity有四个底部选项卡,点击以后会展示四个不同Fragment页面,分别是FragmentHome 、FragmentApp、FragmentMsg、FragmentMy;一般来底部可能是个RadioGroup+RadioButton或者图片加文字的布局,我这里偷懒只写了四个button。

2.1通过新建一个类继承Fragment来创建一个fragment

定义一个自己的FragmentHome 前面说过Fragment也有自己的声明和布局展示,因此我在onCreateView方法使用inflate加载一个布局。类似的还有其他三个,分别是FragmentApp

FragmentMsgFragmentMy

public class FragmentHome extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater,  ViewGroup container, Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fragment_home, null, false);Log.e(TAG, "onCreateView:     --" );return view;
}
}

2.2 将Fragment添加到Activity有两种方法,但是我们最常使用是第一种。

第一种:使用管理器FragmentManager和事务FragmentTransaction

activity布局应包含 一个容器组件来装载Fragment

<LinearLayoutandroid:id="@+id/fragAct_root"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginBottom="@dimen/margin_50"android:visibility="gone"app:layout_constraintTop_toTopOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"android:orientation="vertical" />

在Activity中将四个Fragment实例化,并创建FragmentManager

fragmentHome = new FragmentHome();
fragmentApp = new FragmentApp();
fragmentMsg = new FragmentMsg();
fragmentMy = new FragmentMy();
创建管理器FragmentManager:
FragmentManager manager = getSupportFragmentManager();

然后我们做增加、替换或者移除、显示、隐藏等都可以通过manager 获取事务来实现:

FragmentTransaction fragmentTransaction = manager.beginTransaction();

这里需要注意:fragmentTransaction只能提交一次无论是commit()或者commitNow(),超过一次会报错java.lang.IllegalStateException: commit already called

想要展示哪个fragment就通过事务的add或replace方法,添加当前fragment并放到前台展示。

先说add方法,一个简单的封装:add一个新的需要把其他的隐藏了否则可能会谍影重重:

private void addFragment(Fragment selectFragment, String fragmentTag) {//这里你要过滤FragmentManager的fragment列表
List<Fragment> fragments = manager.getFragments();for (Fragment fragment : fragments) {if (fragment.isAdded()) {Log.e(TAG, "initFragment: 已添加-先隐藏 " + fragment.getClass().getSimpleName() + "     " + fragmentHome.isAdded() + "    " + fragmentHome.isVisible());manager.beginTransaction().hide(fragment).commit();}}Fragment fragmentByTag = manager.findFragmentByTag(fragmentTag);if (fragmentByTag != null && fragmentByTag.isAdded()) {Log.e(TAG, "initFragment: 已经添加过了,别再添加-2 " + selectFragment.getClass().getSimpleName() + "     " + selectFragment.isAdded() + "    " + selectFragment.isVisible() + "   " + selectFragment.isHidden());manager.beginTransaction().show(fragmentByTag).commit();} else {if (selectFragment.isAdded()) {Log.e(TAG, "initFragment: 已经添加过了,别再添加-1 " + selectFragment.getClass().getSimpleName() + "     " + selectFragment.isAdded() + "    " + selectFragment.isVisible() + "   " + selectFragment.isHidden());manager.beginTransaction().show(selectFragment).commit();} else {manager.beginTransaction().add(R.id.fragAct_root, selectFragment, fragmentTag).show(selectFragment).commit();Log.e(TAG, "initFragment: 再次添加 fragment   +" + selectFragment.getClass().getSimpleName() + "     " + selectFragment.isAdded() + "    " + selectFragment.isVisible() + "   " + selectFragment.isHidden());}}
}

再说replace方法,如果你不用add,用了replace那一行代码就足够了

manager.beginTransaction().replace(R.id.fragAct_root, fragmentHome).commit();

第二种在Xml布局中写

<fragmentandroid:id="@+id/fragAct_fragment"android:name="com.example.testdemo3.fragment.FragmentApp"android:layout_width="match_parent"android:layout_height="match_parent"/>

和普通组件一样使用在Activity中findviewbyid就可以了使用,这里注意fragment标签要小写、name属性对应的是你自定义fragment的全路径,后续fragment对象的使用方法同上面第一种。

3、常用方法

整体来看,fragment 应用界面中可模块化又可重复使用fragment 的视图层次结构会成为宿主的视图层次结构的一部分,或附加到宿主的视图层次结构并且可以方便将它增加、替换或者移除这些更改的记录会记录在由 activity 管理的返回堆栈中,以便撤消这些更改。

3.1 添加fragment

如需将 Fragment 添加到 FragmentManager通过事务调用 add()。此方法会收到用于存储此 Fragment 的容器的 ID,以及您要添加的 Fragment 的类名。添加的 Fragment 会转为 RESUMED 状态即最上层可以和用户交互的状态

3.2移除fragment

如需从宿主中移除 Fragment,调用 remove(),同时传入通过 findFragmentById() 或 findFragmentByTag() 从 Fragment 管理器检索到的 Fragment 实例。 如果 fragment 的视图之前已添加到容器中,则此时视图会从容器中移除。已移除的 fragment 会转为 DESTROYED 状态不可用。

3.3替换fragment

 replace() 将容器中现有的 fragment 替换为新 fragment 。调用 replace() 等同于对容器中的 Fragment 调用 remove() 后将新的 Fragment 添加到同一容器中就是系统帮你每次移除旧的并加入新的

3.4 提交

事务的提交操作为异步操作,调用 commit() 不会立即执行事务,而是会将事务调度为能在主界面线程上运行就在主界面线程上运行。如果使用 commitNow() 会立即在界面线程上运行 Fragment 事务,一般来说 commit() 即可。

4、避坑指南

4.1如果两次添加同一个Fragment对象,会报以下错误:
    java.lang.IllegalStateException: Fragment already added: FragmentHome{b2a5424 (a7835f58-5a56-49c9-b889-c0d8b8b17d8c) id=0x7f0800c5}  at androidx.fragment.app.FragmentManagerImpl.addFragment(FragmentManagerImpl.java:1379)
4.2如果同一个fragment使用了两个不同的tag进行add就会包如下错误:因此建议将TAG定义成常量
Caused by: java.lang.IllegalStateException: Can't change tag of fragment FragmentHome{595c6d5 (e4ff5b43-2336-422e-a219-008628a5c3b7) id=0x7f0800c5 fragmentHomeTag}: was fragmentHomeTag now FragmentHomeTag
        at androidx.fragment.app.FragmentTransaction.doAddOp(FragmentTransaction.java:172)

4.3 事务只能提交一次,无论是commit()或者commitNow(),超过一次会报错

java.lang.IllegalStateException: commit already called

4.4 横竖屏切换处理

我们在activity启动后应该自动展示一个fragment而不是一定要等用户主动触发再去加载展示对吧。如果你放到了onCreate中添加第一个fragment,那么在遇到横竖屏切换的话就会发现不论之前显示的个fragment切换以后都展示第一个fragment了,因为它重走生命周期了。其实呢可以这样避免:

//选出最近的fragment并展示,避免每次横竖屏切换就回到了第一个fragment
List<Fragment> fragments = manager.getFragments();
Fragment firstFragment = fragmentHome;
if (!fragments.isEmpty()) {firstFragment = fragments.get(fragments.size() - 1);
}
//上面2.2中写了这个方法
addFragment(firstFragment, firstFragment.getClass().getSimpleName() + "Tag");

横竖屏切换前的Fragment实例是怎样保存下来的呢?是的,是它。FragmentActivity的onSaveInstanceState方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/1005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4.18学习总结

多线程补充 等待唤醒机制 现在有两条线程在运行&#xff0c;其中一条线程可以创造一个特殊的数据供另一条线程使用&#xff0c;但这个数据的创建也有要求&#xff1a;在同一时间只允许有一个这样的特殊数据&#xff0c;那么我们要怎样去完成呢&#xff1f;如果用普通的多线程…

解决Error in writing header file of the driver

在源代码里面更新了一批常规的内容&#xff0c;编译的时候遇到一个error&#xff0c;一大片都是红的。XXX是项目名称。 Description Resource Path Location Type Generator: ERROR: Error in writing header file of the driver XXX Cpu Processor Expert Problem 表面意思是…

【学习】Jmeter、postman、python如何与数据库相互配合

在当今数字化时代&#xff0c;数据库已经成为我们日常生活中不可或缺的一部分。无论是购物、社交还是工作&#xff0c;数据库都在默默地为我们提供着高效、稳定的服务。而在众多的技术工具中&#xff0c;Jmeter、Postman和Python成为了操作数据库的三大主流技术。今天&#xff…

【嵌入式DIY实例】-指纹锁

DIY指纹锁 文章目录 DIY指纹锁1、硬件准备1.1 R307指纹传感器模介绍2、硬件接线原理图3、代码实现在这个项目中,我们将使用 Arduino 构建一个指纹门锁安全系统。 该系统可用于我们的家庭、办公室等提供安全保障。 我们还可以用它来打开门,只需将手指放在门锁上即可。 安全是许…

Linux管道共享内存

前言 进程虽然是独立运行的个体&#xff0c;但它们之间有时候需要协作才能完成一项工作&#xff0c;比如有两个进程需要同步数据&#xff0c;进程 A 把数据准备好后&#xff0c;想把数据发往进程 B&#xff0c;进程 B 必须被提前通知有数据即将到来&#xff0c;或者进程 A 想发…

项目七:学会使用python爬虫解析库(小白大成级)

前期我们学会了怎么使用python爬虫请求库和解析库的简单应用和了解&#xff0c;同时能够对爬虫有一个较为清晰的体系&#xff0c;毕竟简单的爬虫基本上都是请求数据——解析数据——存储数据的大概流程。 那么回忆一下&#xff0c;请求库我们学的是requests模块&#xff0c;解…

项目管理-项目开发计划介绍

目录 一、内容总览 二、项目开发计划概述 2.1 概述 2.2 项自开发计划的目的和作用 2.3 项目开发计划的内容 2.3.1 工作计划 2.3.2 人员组织计划 2.3.3 设备采购和资源供应计划 2.3.4 配置管理计划 2.3.5 进度安排计划 2.3.6 成本投资计划 2.3.7 质量保证计划 2.3.8…

实现I.MX6ULL开发板与Windows和Ubuntu系统之间的通信

虚拟机与主机之间的连接方式确实包括桥接模式、NAT模式和仅主机模式。 桥接模式&#xff08;Bridged&#xff09;&#xff1a;在桥接模式下&#xff0c;虚拟机通过虚拟交换机直接连接到主机的物理网络上&#xff0c;就像一台独立的物理机器一样&#xff0c;拥有自己的IP地址&a…

[阅读笔记23][JAM]JOINTLY TRAINING LARGE AUTOREGRESSIVE MULTIMODAL MODELS

这篇论文是24年1月发表的&#xff0c;然后是基于的RA-CM3和CM3Leon这两篇论文。它所提出的JAM结构系统地融合了现有的文本模型和图像生成模型。 主要有两点贡献&#xff0c;第一点是提出了融合两个模型的方法&#xff0c;第二点是为混合模型精心设计的指令微调策略。 下图是一个…

Unity射击游戏开发教程:(1)玩家控制

玩家的移动 玩家控制和移动是视频游戏中最酷的事情之一,因为你正在控制游戏中的某些东西 现在游戏中的玩家是我们的蓝色方块英雄。我在游戏开发中了解到,游戏是用简单的对象制作原型,然后添加所有漂亮的艺术和声音。代码… 我们要做的第一件事是在游戏开始时为玩家提供一个…

MATLAB 体素滤波(62)

MATLAB 体素滤波(62) 一、算法介绍二、算法实现1.代码(已验证,直接运行)一、算法介绍 这里的代码完成文件读入,体素滤波,效果显示,结果输出的操作,下面是效果截图,后面是代码。 体素滤波(Voxel Filtering)是一种用于三维点云数据处理的方法,其原理类似于二维图像…

力扣HOT100 - 142. 环形链表 II

解题思路&#xff1a; public class Solution {public ListNode detectCycle(ListNode head) {Set<ListNode> set new HashSet<>();while (head ! null) {if (!set.add(head)) {return head;}head head.next;}return null;} }

广西建筑模版厂家批发供应,当天发货全国可达

近年来&#xff0c;建筑行业蓬勃发展&#xff0c;对高质量的建筑模板需求量逐渐增加。在这个竞争激烈的市场中&#xff0c;贵港市能强优品木业有限公司以其出色的产品和卓越的服务迅速崭露头角&#xff0c;成为知名的建筑模板生产厂家。 作为一家拥有25年生产经验的公司&#x…

用FRP配置toml文件搭建内网穿透

需求场景 1、一台外网可访问的有固定ip的云服务器&#xff0c;Ubuntu系统 2、一台外网无法访问的无固定ip的本地家用电脑&#xff0c;Ubuntu系统 需求&#xff1a;将云服务器搭建为一台内网穿透服务器&#xff0c;实现通过外网访问家用电脑&#xff08;网页&#xff09;的功能。…

人工智能|机器学习——基于机器学习的信用卡办卡意愿模型预测项目

一、背景介绍 在金融领域&#xff0c;了解客户的信用卡办卡意愿对于银行和金融机构至关重要。借助机器学习技术&#xff0c;我们可以根据客户的历史数据和行为模式预测其是否有办理信用卡的倾向。本项目通过Python中的机器学习库&#xff0c;构建了两个常用的分类模型&#xff…

Ardupilot无人船(车)自动调参

无人船&#xff08;车&#xff09;快速调参脚本简化了为无人船&#xff08;车&#xff09;角速率和速度控制器调参的过程。 该脚本应该在无人船&#xff08;车&#xff09;在Circle模式下循环绕圈时运行。它记录转向和油门输出以及由此产生的响应(例如无人船&#xff08;车&am…

高精度算法(2)

前言 延续上次所讲的内容再对乘法和除法进行说明&#xff0c;希望有所帮助 注意这里的乘除法都是针对于整数如果要是涉及到小数&#xff0c;我们得使用二分法 通过二分同样可以解决小数精度问题 高精度乘法 思路 我们只能用字符串来读取一个很大很大的数&#xff0c;所以…

阿赵UE学习笔记——28、粒子系统Niagara简介

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的使用。这次开始学习粒子系统的使用。 一、Cascade系统 在介绍UE5的Niagara系统之前&#xff0c;必须先介绍一下旧版本的粒子系统。   在UE4的时候&#xff0c;虚幻引擎的粒子系统叫做Cascade&#x…

【论文阅读】Attention is all you need

摘要 主要的序列转换模型是基于复杂的循环或卷积神经网络&#xff0c;其中包括一个编码器和一个解码器。性能最好的模型还通过一种注意力机制将编码器和解码器连接起来。我们提出了一种新的简单的网络架构&#xff0c;Transformer&#xff0c;完全基于注意机制&#xff0c;完全…

杜牧是唐朝最风流的诗人

杜牧&#xff0c;有“杜紫薇”之称&#xff0c;是宰相杜佑的孙子。李白、杜甫是“大李杜”。杜牧、李商隐是“小李杜”。 杜牧23岁写了《阿房宫赋》&#xff0c;一朝成名天下知。 唐朝晚年&#xff0c;科举和仕途没有出路&#xff0c;杜牧寄情享乐。杜牧怀才不遇&#xff0c;…