004_Fragment-FragmentManager 翻译学习

本文详细介绍了FragmentManager类的功能和使用方法,包括管理Fragment对象、执行事务操作、查找Fragment实例等,并提供了FragmentManager的主要公共方法概述。

FragmentManager(Added in API level 11)

官方:http://developer.android.com/reference/android/app/FragmentManager.html

类信息: java.lang.Object android.app. FragmentManager

简介:

用来管理Fragment对象,Activity都有一个自己的FragmentManager对象。

公共方法:

1、   public abstract void addOnBackStackChangedListener (FragmentManager.OnBackStackChangedListenerlistener)

添加一个Fragment回退栈监听器,用于监听回退栈发生的变化。

2、   public abstract FragmentTransaction beginTransaction ()

返回一个FragmentTransaction对象,以执行一连串的Fragment操作。

3、   public abstract void dump (String prefix, FileDescriptor fd, PrintWriterwriter, String[] args)

将FragmentManager的状态输出到一个流中。

4、   public static void enableDebugLogging (boolean enabled)

设置是否输出FragmentManager的日志到Log中。

5、   public abstract boolean executePendingTransactions ()

由于FragmentTransaction.commit()方法是异步的,调用此方法可以立即执行commit()方法,但是必须保证在主线程中调用executePendingTransactions();

6、   public abstract Fragment findFragmentById (int id)

通过指定ID寻找一个fragment对象,这个id可能是在布局文件中指定的,也可能是包含该fragment的父容器的id。它首先会在关联Activity的Fragmanager中寻找,然后会在当前回退栈所有fragment对象中寻找。

7、   public abstract Fragment findFragmentByTag (String tag)

tag可能是在布局文件中或者程序中指定。寻找方法与通过id类似。

8、   public abstract FragmentManager.BackStackEntry getBackStackEntryAt (intindex)

通过指定index获得BackStackEntry对象。Index是在调用FragmentTransaction.addToBackStack后由commit()方法返回。BackStackEntry是内部类。主要包括getid()、getname()方法。getname返回值就是addToBackStack(String)中的值。getid()返回值是Activity识别这个entry对象的唯一标识。getid()应该就是index。

9、   public abstract int getBackStackEntryCount ()

返回当前回栈中保存的对象个数。

10、public abstract FragmentgetFragment (Bundle bundle, String key)

从之前通过putFragment(Bundle, String,Fragment)存储的引用恢复fragment。

bundle:用于恢复fragment引用的bundle

key:bundle保存的entry的名称。

返回值:返回与给定引用绑定的fragment对象。

11、public voidinvalidateOptionsMenu ()

废止关联activity的options menu。这个会被延期到activity进入resumed状态时执行。

12、isDestroyed ()

在关联activity执行onDestroy()时返回true。

13、public abstract voidpopBackStack ()异步

弹出回退栈栈顶的fragment对象。

14public abstractvoid popBackStack (String name, int flags)

name:不为null时,表示一个要寻找的之前的BackStackEntry名,可能找到多个对象将被弹出,flags用来控制是否弹出这个BackStackEntry本身。为null时,则返回栈顶。

Flags:为POP_BACK_STACK_INCLUSIVE时表示弹出所有匹配的fragment对象包括这个BackStackEntry;

     为0时表示之弹出匹配的fragment对象但不包括这个BackStackEntry

15、public abstract voidpopBackStack (int id, int flags)

popBackStack(String name, int flags)类似。   IdFragmentTransaction.commit()返回的。

16public abstractboolean popBackStackImmediate ()  立刻执行

public abstractboolean popBackStackImmediate (int id, int flags) 立刻执行

public abstractboolean popBackStackImmediate (String name, int flags) 立刻执行

17public abstractvoid putFragment (Bundle bundle, String key, Fragment fragment)

添加一个fragment的引用到bundle中。这个bundle用来保存状态。当后面调用getFragment(Bundle,String)方法时将返回一个与当前状态相同的fragment对象。

bundle:用于保存fragment引用

keybundleentry

fragment:需要保存引用的fragment

18public abstractvoid removeOnBackStackChangedListener (FragmentManager.OnBackStackChangedListenerlistener)

移除一个回退栈监听器。

19public abstractFragment.SavedState saveFragmentInstanceState (Fragment f)

保存给定fragment的当前状态。这个可以用来在创建一个新fragment实例时将这个SavedState对象交给FragmentManager以重建一个相同的对象。这个方法的使用需要满足以下条件:

1、  这个fragment当前应该绑定之这个fragmentmanager

2、  新建的Fragment对象必须和保存的这个fragment时同一个类;

3、 保存的状态不可以包含其他fragment对象。


private fun initView(savedInstanceState: Bundle?) { val navView: BottomNavigationView = binding.dashboardBottomTab navView.setOnItemSelectedListener { if (it.itemId != R.id.tab_network) { if (securityGuideTipView?.visibility == View.VISIBLE) { securityGuideTipView?.visibility = View.GONE TPLog.i(LOG_TAG, "initView remove tip") } } when (it.itemId) { R.id.tab_network -> { switchFragment(TAG_FRAGMENT_NETWORK, TAG_FRAGMENT_NETWORK) return@setOnItemSelectedListener true } R.id.tab_family -> { showFamilyTab() return@setOnItemSelectedListener true } R.id.tab_security -> { showSecurityTab() return@setOnItemSelectedListener true } R.id.tab_more -> { switchFragment(TAG_FRAGMENT_MORE, TAG_FRAGMENT_MORE) return@setOnItemSelectedListener true } R.id.tab_usb -> { switchFragment(TAG_FRAGMENT_USB, TAG_FRAGMENT_USB) return@setOnItemSelectedListener true } } false } private fun switchFragment(key: String, tag: String) { switchFragment(key, tag, arguments) } private fun switchFragment(key: String, tag: String, args: Bundle?) { if (context == null || (currentFragment != null && currentFragment!!.javaClass.name == key)) { return } val fragmentManager = childFragmentManager var fragment: Fragment? = fragmentMap[key] if (fragment == null) { fragment = fragmentManager.fragmentFactory.instantiate(requireContext().classLoader, key) (fragmentMap as ArrayMap)[key] = fragment } if (args != null) { fragment.arguments = args } val transaction = fragmentManager.beginTransaction() if (currentFragment != null) { transaction.hide(currentFragment!!) } if (fragment.isAdded) { transaction.show(fragment) } else { transaction.add(R.id.dashboard_container_view, fragment, tag) } transaction.commitAllowingStateLoss() currentFragment = fragment }这是什么写法呢
09-27
<think>我们需要分析这段代码的结构和功能。这段代码主要包含两个部分: 1. `initView` 函数:设置底部导航栏的点击监听器,根据点击的选项切换不同的Fragment。 2. `switchFragment` 函数(有两个重载):用于切换Fragment。 具体分析如下: ### 第一部分:initView 函数 这个函数接收一个可空的 Bundle 参数(savedInstanceState),然后: - 通过视图绑定获取 BottomNavigationView 实例(navView) - 为 navView 设置一个 ItemSelectedListener(使用 lambda 表达式) 在监听器中: - 使用 `it` 表示被选中的菜单项(MenuItem),它是 lambda 的单参数,类型为 MenuItem。 - 首先判断如果选中的不是“网络”标签(R.id.tab_network),并且当前有一个安全引导提示视图(securityGuideTipView)是可见的,则将其隐藏,并记录日志。 - 然后使用 when 表达式根据选中的菜单项 itemId 执行不同的操作: * R.id.tab_network -> 调用 switchFragment 切换到网络Fragment * R.id.tab_family -> 调用 showFamilyTab()(可能是切换到家庭标签页) * R.id.tab_security -> 调用 showSecurityTab() * R.id.tab_more -> 切换到更多Fragment * R.id.tab_usb -> 切换到USBFragment 每个分支都返回 true 表示已处理该事件(注意使用了 return@setOnItemSelectedListener 进行局部返回)。 最后,如果 when 表达式没有匹配到任何分支(理论上不会,因为所有选项都已覆盖),则返回 false。 ### 第二部分:switchFragment 函数 有两个重载: 1. 两个参数的 switchFragment(key: String, tag: String) 直接调用三个参数的版本,并传入 arguments(当前Fragment的arguments)作为第三个参数。 2. 三个参数的 switchFragment(key: String, tag: String, args: Bundle?) 功能:根据给定的key(类名)和tag(Fragment的标签)切换Fragment- 首先检查 context 是否为空,或者当前显示的Fragment的类名是否等于要切换的key(即已经是该Fragment),如果是则直接返回。 - 获取子FragmentManager(因为是Fragment中的Fragment,所以用childFragmentManager)。 -fragmentMap(一个映射,用于缓存已创建的Fragment)中查找是否已经有该key对应的Fragment,如果没有,则通过FragmentFactory实例化一个新的Fragment(使用类加载器加载key指定的类)。 - 将新创建的Fragment放入fragmentMap。 - 如果传入的args不为空,则设置给Fragment的arguments。 - 开始一个事务: * 如果当前有正在显示的Fragment(currentFragment),则先隐藏它。 * 如果要切换的Fragment已经被添加过(fragment.isAdded),则显示它;否则,将其添加到容器(R.id.dashboard_container_view)中,并指定tag。 - 使用 commitAllowingStateLoss() 提交事务(允许状态丢失,避免在状态保存后提交导致的异常)。 - 更新 currentFragment 为当前显示的Fragment。 ### 代码特点 1. **使用视图绑定**:通过 `binding.dashboardBottomTab` 获取 BottomNavigationView。 2. **Fragment缓存**:使用 fragmentMap(ArrayMap)缓存已创建的Fragment,避免重复创建。 3. **Fragment切换优化**:通过隐藏和显示来切换,而不是每次重新创建。 4. **使用 return@ 实现局部返回**:在 lambda 表达式中,使用 return@setOnItemSelectedListener 从 lambda 返回,而不是从外层函数返回。 5. **避免重复切换**:如果要切换的Fragment已经是当前显示的,则跳过。 ### 可能存在的问题 1. **commitAllowingStateLoss**:可能会导致界面状态丢失,但在某些情况下可以接受(例如用户交互触发的切换)。 2. **空安全**:代码中使用了非空断言(!!),如果 currentFragment 为 null 会抛出异常(但前面检查了 currentFragment != null,所以安全)。 3. **内存泄漏**:fragmentMap 会持有所有创建过的Fragment,如果Fragment很多且长时间不销毁,可能会占用较多内存。需要确保在适当的时候(如Fragment销毁时)清理 fragmentMap。 ### 总结写法 这是一种典型的在 Fragment 中使用底部导航栏切换子 Fragment 的写法,结合了 Fragment 的缓存和高效切换(隐藏/显示)机制。 注意:代码中有一个小错误,在 `initView` 的监听器最后返回了 `false`,但实际上每个分支都已经返回了 true,所以这个 false 永远不会被执行。可以删除。 §§相关问题§§ 1. 在 Kotlin 中,`return@setOnItemSelectedListener` 这种语法的作用是什么? 2. 为什么在切换 Fragment 时要使用 `commitAllowingStateLoss()` 而不是 `commit()`? 3. 使用 `fragmentMap` 缓存 Fragment 有哪些优缺点? 4. 如何避免在 Fragment 切换过程中出现内存泄漏? 5. 在什么情况下 `fragment.isAdded` 会为 true?如何正确管理 Fragment 的添加状态? </think>这段代码是典型的 **Android Fragment 管理实现**,采用了 **View Binding + Fragment Transaction + 缓存优化** 的架构模式,专门用于处理底部导航栏的 Fragment 切换。以下是关键解析: --- ### 核心架构解析 #### 1. **导航控制 (`initView`)** ```kotlin navView.setOnItemSelectedListener { when (it.itemId) { R.id.tab_network -> switchFragment(TAG_FRAGMENT_NETWORK, TAG_FRAGMENT_NETWORK) R.id.tab_more -> switchFragment(TAG_FRAGMENT_MORE, TAG_FRAGMENT_MORE) ... // 其他 Tab } } ``` - **`it` 指向**:`MenuItem` 对象(BottomNavigationView 的选中项) - **动态提示控制**:非网络 Tab 时自动隐藏安全提示 - **Fragment 标识**:通过常量 `TAG_FRAGMENT_XXX` 精准定位 Fragment #### 2. **Fragment 切换引擎 (`switchFragment`)** ```kotlin private fun switchFragment(key: String, tag: String, args: Bundle?) { // 1. 防重复切换检查 if (currentFragment?.javaClass?.name == key) return // 2. Fragment 缓存机制 var fragment = fragmentMap[key] ?: run { val newFragment = fragmentFactory.instantiate(requireContext().classLoader, key) fragmentMap[key] = newFragment // 存入缓存 newFragment } // 3. 事务处理 val transaction = childFragmentManager.beginTransaction() currentFragment?.let { transaction.hide(it) } // 隐藏当前 when { fragment.isAdded -> transaction.show(fragment) else -> transaction.add(R.id.container, fragment, tag) // 首次添加 } transaction.commitAllowingStateLoss() // 避免状态丢失崩溃 currentFragment = fragment // 更新当前Fragment } ``` ### 关键设计亮点 1. **Fragment 缓存池 (`fragmentMap`)** - 使用 `ArrayMap<String, Fragment>` 缓存已实例化的 Fragment - 避免重复创建:`fragmentMap[key]` 存在时直接复用 - 首次创建:通过 `fragmentFactory.instantiate` 动态实例化 2. **双标识符机制** - `key`:类全限定名(如 `com.example.MoreFragment`),用于精准匹配 - `tag`:自定义标签(通常与 key 相同),用于 FragmentManager 检索 3. **性能优化** - `hide()/show()` 代替 `replace()`:保留 Fragment 状态,减少重建开销 - `commitAllowingStateLoss()`:防止 Activity 状态保存后提交导致的崩溃 - 防重复切换检测:`currentFragment?.javaClass?.name == key` 4. **生命周期安全** - `requireContext()`:确保 Context 有效 - `childFragmentManager`:使用子 FragmentManager(适用于嵌套 Fragment--- ### 典型使用场景 ```mermaid graph TD A[用户点击导航Tab] --> B{是否已缓存?} B -->|是| C[从 fragmentMap 获取] B -->|否| D[通过 ClassLoader 动态实例化] C & D --> E[隐藏当前 Fragment] E --> F[显示/添加目标 Fragment] F --> G[更新 currentFragment] ``` --- ### 潜在优化点 1. **内存泄漏防护**: ```kotlin override fun onDestroyView() { fragmentMap.clear() // 清除缓存引用 super.onDestroyView() } ``` 2. **状态保存**: ```kotlin if (savedInstanceState == null) { // 初始 Fragment 加载 } ``` 3. **延迟加载**:结合 `setMaxLifecycle()` 控制非活跃 Fragment 生命周期
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值