Fragment 事务
本教程共 100 篇 · 第 40 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
40. Fragment 事务
本节目标:掌握 Fragment 事务的各种操作(add/replace/remove/hide/show),学会用返回栈实现「返回上一个 Fragment」,能给切换加动画,避免常见的事务坑。
事务是什么
打个比方:事务就像银行转账—你要么全部成功,要么全部不干。Fragment 事务把一组界面变更打包成一个原子操作:添加、替换、移除这些动作要么一起生效,要么一起撤销。
对 Fragment 的所有修改都要通过事务来完成。用 FragmentManager 开启事务,执行操作,最后提交。
基本操作
开启事务
supportFragmentManager.commit {
// 在这里做各种 Fragment 操作
}
这是 KTX 简化写法,等价于:
val transaction = supportFragmentManager.beginTransaction()
// 操作...
transaction.commit()
replace:替换
最常用的操作,把容器里的 Fragment 换成新的:
supportFragmentManager.commit {
replace(R.id.fragment_container, DetailFragment())
addToBackStack(null) // 加到返回栈,按返回键能退回上一个
}
Warning
replace()会先移除容器里现有的 Fragment,再添加新的。被替换的 Fragment 如果没加到返回栈,会被销毁(onDestroyView->onDestroy->onDetach)。如果加了返回栈,只会走到onDestroyView,Fragment 实例保留,返回时重建视图。
add:添加
add() 是往容器里加一个 Fragment,不移除已有的:
supportFragmentManager.commit {
add(R.id.fragment_container, HomeFragment(), "home_tag")
}
第三个参数是 Tag,之后可以用 findFragmentByTag("home_tag") 找到它。
remove:移除
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (fragment != null) {
supportFragmentManager.commit {
remove(fragment)
}
}
hide / show:隐藏和显示
hide() 和 show() 不销毁 Fragment 的视图,只是设为不可见/可见。适合在底部导航栏切换 Tab 时用,避免每次切换都重建:
supportFragmentManager.commit {
hide(currentFragment)
show(targetFragment)
}
Tip
- 底部导航栏推荐用
hide()/show()而不是replace()。replace每次都销毁重建,浪费时间、丢失滚动位置;hide/show只切可见性,体验更流畅。但要预加载所有 Fragment,内存占用稍高。
返回栈
默认情况下,Fragment 切换后按返回键会直接退出 Activity。加到返回栈后,按返回键会回退到上一个 Fragment。
supportFragmentManager.commit {
replace(R.id.fragment_container, DetailFragment())
addToBackStack(null) // 参数是事务的名称,可用于 popBackStack(name)
}
手动出栈
// 回退一步
supportFragmentManager.popBackStack()
// 回退到指定名称的事务
supportFragmentManager.popBackStack("transaction_name", 0)
// 回退到某个事务的状态(包含该事务)
supportFragmentManager.popBackStack(
"transaction_name",
FragmentManager.POP_BACK_STACK_INCLUSIVE
)
监听返回栈变化
supportFragmentManager.addOnBackStackChangedListener {
// 返回栈发生变化时回调
updateActionBar()
}
Note
- Android 13+ 引入了预测式返回手势。如果你用了返回栈,建议配合
OnBackPressedDispatcher处理返回逻辑,而不是老式的onBackPressed()。
事务动画
给 Fragment 切换加动画,提升体验。
方式一:标准动画资源
在 res/anim/ 下定义动画文件,比如 fade_in.xml:
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="300" />
事务里设置:
supportFragmentManager.commit {
setCustomAnimations(
R.anim.fade_in, // 新 Fragment 进入
R.anim.fade_out, // 旧 Fragment 退出
R.anim.fade_in, // 返回栈弹出时新 Fragment 进入
R.anim.fade_out // 返回栈弹出时旧 Fragment 退出
)
replace(R.id.fragment_container, DetailFragment())
addToBackStack(null)
}
方式二:过渡动画(Transition)
API 21+ 支持用 Transition API 做更丰富的动画:
// 在 Fragment 里设置
class DetailFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 进入时淡入
enterTransition = Fade()
// 退出时淡出
exitTransition = Fade()
}
}
Tip
- 用 Transition API 可以做出共享元素过渡效果(一个元素从列表页飞到详情页),体验很好。不过设置稍复杂,需要给共享视图加
transitionName。
commit vs commitNow
提交事务有两种方式:
commit():异步提交,加到主线程消息队列,不立即执行。commitNow():同步提交,立即执行完所有操作。
// 异步,常用
supportFragmentManager.commit { ... }
// 同步,立即生效
supportFragmentManager.commitNow { ... }
Warning
commit()在 Activity 保存状态之后(onSaveInstanceState之后)调用会崩溃。如果你必须在状态保存后提交,用commitAllowingStateLoss(),但要接受可能丢失状态的风险。
| 方法 | 执行时机 | 能在 onSaveInstanceState 后调用 |
|---|---|---|
commit() | 异步 | 否(崩溃) |
commitNow() | 同步 | 否(崩溃) |
commitAllowingStateLoss() | 异步 | 是 |
常见坑
坑一:重复添加
如果 Fragment 已经在容器里,又 add 一次会报错。先检查:
if (supportFragmentManager.findFragmentById(R.id.fragment_container) == null) {
supportFragmentManager.commit {
replace(R.id.fragment_container, HomeFragment())
}
}
坑二:在 onResume 之前操作 Fragment
Fragment 事务在 onResume() 之前提交可能有问题,特别是涉及返回栈的操作。如果需要在 Fragment 创建后立即做后续操作,用 postponeEnterTransition() + startPostponedEnterTransition() 延迟过渡。
坑三:状态丢失
在后台时(onStop 之后)执行 commit() 会崩溃。用 commitAllowingStateLoss() 替代,或者把操作延迟到 onResume()。
小结
Fragment 事务是修改界面的原子操作,replace 最常用,hide/show 适合 Tab 切换。返回栈让用户能回退,动画让切换更流畅。记住 commit 是异步的、状态保存后要用 commitAllowingStateLoss。下一节我们看 Fragment 之间怎么传数据。