展开说说: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

<LinearLayout
    android: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布局中写

<fragment
    android: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方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

iOS重签名-超详细,附排错

文章目录 重签名步骤步骤 1: 准备必要的材料步骤 2: 解压 .ipa 文件步骤3:将 Provisioning Profile 复制到 Payload 目录步骤 4: 移除原来的签名步骤 5: 使用新的证书和 Provisioning Profile 进行重签名步骤 6: 重新打包 .ipa 文件步骤 7: 安装和测试得到provisioning file和…

nginx反向代理及负载均衡

node1192.168.136.55Nginx主负载均衡器node3192.168.136.57Web01服务器node4192.168.136.58Web02服务器node5192.168.135.131客户端&#xff08;测试&#xff09; nginx反向代理 1. 安装nginx 三台机器都安装nginx yum install nginx -y 2. 配置用于测试的Web服务(以下操作…

C++ 协程 学习笔记

协程的优势就是比线程切换的时间少很多&#xff0c;协程的切换时间是纳秒&#xff0c;而进行切换的时间是微秒 单线程用协程可以轻松的处理并发任务 co_yield和co_await可以将协程暂停下来 resume又把协程激活 如果c函数里有co_await、co_return、co_yield就会自动判定为协程…

牛客-小乐乐与欧几里得

目录 题目 描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 示例2 解题 题目 描述 小乐乐最近在课上学习了如何求两个正整数的最大公约数与最小公倍数&#xff0c;但是他竟然不会求两个正整数的最大公约数与最小公倍数之和&#xff0c;请你帮助他解决这个问题。 …

计算机网络 -- 多人聊天室

一 程序介绍和核心功能 这是基于 UDP 协议实现的一个网络程序&#xff0c;主要功能是 构建一个多人聊天室&#xff0c;当某个用户发送消息时&#xff0c;其他用户可以立即收到&#xff0c;形成一个群聊。 这个程序由一台服务器和n个客户端组成&#xff0c;服务器扮演了一个接受…

【汇编语言】汇编语言程序

【汇编语言】汇编语言程序 文章目录 【汇编语言】汇编语言程序前言一、用汇编语言写的源程序汇编语言编写程序的工作过程程序中的三种伪指令源程序编译链接后变为机器码汇编程序的结构如何写出一个程序来程序中可能的错误 二、由源程序到程序运行由源程序到执行可执行文件的过程…

区间图着色问题:贪心算法设计及实现

区间图着色问题&#xff1a;贪心算法设计及实现 1. 问题定义2. 贪心算法设计2.1 活动排序2.2 分配教室2.3 算法终止 3. 伪代码4. C语言实现5. 算法分析6. 结论7. 参考文献 在本文中&#xff0c;我们将探讨如何使用贪心算法解决一个特定的资源分配问题&#xff0c;即区间图着色问…

【深度学习-番外1】Win10系统搭建VSCode+Anaconda+Pytorch+CUDA深度学习环境和框架全过程

专栏的老读者们都知道&#xff0c;以前的文章以使用MATLAB的为多。 不过后续陆续开始展开深度学习算法的应用&#xff0c;就会逐渐引入Python语言了&#xff08;当然MATLAB的代码也会同步更新&#xff09;&#xff0c;这是由于在深度学习领域&#xff0c;Python应用更为广泛。…

Matlab|【复现】主动配电网故障定位方法研究

目录 1 主要内容 算例模型 期望故障电流状态函数 评价函数&#xff08;膨胀率函数&#xff09; 算例验证方法 详实的文档说明 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序方法复现了《基于改进多元宇宙算法的主动配电网故障定位方法研究》_郑聪&#xff0c;建…

在ELF 1开发环境中使用Qt Creator进行远程调试

Qt Creator是一款跨平台集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要适用于支持Qt框架的各类应用程序开发。其内置的远程调试机制使得开发者能够在本地开发环境中对部署在远程设备上的代码进行调试&#xff0c;无需直接对远程设备进行操作。Qt Creator会通过网络连…

2.Vue简介

Vue简介 Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0c;V…

在 Linux 中删除文件和文件夹

目录 ⛳️推荐 前言 删除文件 &#x1f3cb;️练习文件删除 小心删除 删除目录 &#x1f3cb;️练习文件夹删除 测试你的知识 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到…

VSCode搭建内核源码阅读开发环境

0. 参考链接 使用VSCode进行linux内核代码阅读和开发_vscode阅读linux内核-CSDN博客 1. 搭建Linux内核源码阅读环境 现状&#xff0c;Linux内核源码比较庞大文件非常多&#xff0c;其中又包含的众多的宏定义开关配置选项&#xff0c;这使得阅读内核源代码称为一件头疼的事。 …

电脑工作者缓解眼部疲劳问题的工具分享

背景 作为以电脑为主要工作工具的人群&#xff0c;特别是开发人员&#xff0c;我们每天都需要长时间紧盯着屏幕&#xff0c;进行代码编写、程序调试、资料查询等工作。这种持续的工作模式无疑给我们的眼睛带来了不小的负担。一天下来&#xff0c;我们常常会感到眼睛干涩、疲劳…

[笔试强训day02]

文章目录 BC64 牛牛的快递DP4 最小花费爬楼梯[编程题]数组中两个字符串的最小距离 BC64 牛牛的快递 BC64 牛牛的快递 #include<iostream> #include<cmath> using namespace std;double a; char b;int main() {cin>>a>>b;int ans0;if(a<1.0){ans20;…

Go程序设计语言 学习笔记 第十三章 低级编程

Go的设计保证了一系列安全性&#xff0c;限制了Go程序可能出现问题的方式。在编译期间&#xff0c;类型检查会检测到大多数试图将操作应用于不适合其类型的值的尝试&#xff0c;例如&#xff0c;从一个字符串中减去另一个字符串。严格的类型转换规则阻止了直接访问内置类型&…

数字接龙(蓝桥杯)

文章目录 数字接龙【问题描述】解题思路DFS 数字接龙 【问题描述】 小蓝最近迷上了一款名为《数字接龙》的迷宫游戏&#xff0c;游戏在一个大小为N N 的格子棋盘上展开&#xff0c;其中每一个格子处都有着一个 0 . . . K − 1 之间的整数。游戏规则如下&#xff1a; 从左上…

【图解计算机网络】从浏览器地址输入到网页显示的整个过程

从浏览器地址输入到网页显示的整个过程 整体流程DHCPhttp协议报文组装DNSTCP协议封装与TCP三次握手IP协议封装与路由表MAC地址与ARP协议交换机路由器 整体流程 从往浏览器输入一个地址到网页的显示&#xff0c;要经过很长的一个流程&#xff0c;中间涉及到计算机网络的许多知识…

力扣-LCP 02.分式化简

题解&#xff1a; class Solution:def fraction(self, cont: List[int]) -> List[int]:# 初始化分子和分母为 0 和 1n, m 0, 1# 从最后一个元素开始遍历 cont 列表for a in cont[::-1]:# 更新分子和分母&#xff0c;分别为 m 和 (m * a n)n, m m, (m * a n)# 返回最终的…

VOJ 等边三角形 题解 DFS

等边三角形 代码 #include <bits/stdc.h> using namespace std; typedef long long ll; int n, flag 0, sum 0, tag; int length[20]; // 木棍长度 int group[3] {0}; // 三条边的当前边长 void dfs(int i, int index) {group[index] length[i];if (group[1] &g…