You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android单Activity多Fragment全屏切换平滑动画实现问题

解决单Activity多Fragment全屏切换时的动画卡顿问题

我之前也碰到过一模一样的头疼问题——单Activity架构下,全屏Fragment和普通Fragment切换时,系统UI的显示/隐藏总是和转场动画打架,要么卡顿要么跳变。试了好几种方法后,终于找到几个规范且简洁的方案,分享给你:

方案一:使用WindowInsets API(推荐,兼容Android 11+及低版本)

旧的SYSTEM_UI_FLAG已经被官方废弃,现在推荐用WindowInsets和ViewCompat的API处理系统栏显示/隐藏,这种方式能和Android的布局动画系统深度协同,从根源避免冲突。

步骤1:MainActivity中统一处理系统栏内边距

在Activity里给Fragment容器(比如FrameLayout或NavHostFragment)设置默认的系统栏内边距,让普通Fragment自动适配系统栏:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        val container = findViewById<FrameLayout>(R.id.fragment_container)
        // 让容器自动适配系统栏内边距
        ViewCompat.setOnApplyWindowInsetsListener(container) { view, insets ->
            val systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            view.setPadding(systemBarInsets.left, systemBarInsets.top, systemBarInsets.right, systemBarInsets.bottom)
            insets
        }
    }
}

步骤2:全屏Fragment中消费Insets并隐藏系统UI

在全屏Fragment的根布局里,通过OnApplyWindowInsetsListener消费系统栏Insets(即不传递给父布局),同时隐藏系统UI:

class FullScreenFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val rootView = inflater.inflate(R.layout.fragment_full_screen, container, false)
        
        // 开启沉浸式全屏
        requireActivity().window.decorView.systemUiVisibility = (
            View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            or View.SYSTEM_UI_FLAG_FULLSCREEN
            or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        )
        
        // 消费Insets,让Activity容器的内边距不生效
        ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets ->
            insets.consume(WindowInsetsCompat.Type.systemBars())
        }
        
        return rootView
    }
    
    override fun onDestroyView() {
        super.onDestroyView()
        // 恢复系统UI显示
        requireActivity().window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
        // 通知Activity容器重新计算内边距,配合退出动画同步完成
        ViewCompat.requestApplyInsets(requireActivity().findViewById(R.id.fragment_container))
    }
}

方案二:配合Fragment转场动画延迟UI模式切换

如果暂时不想切换到WindowInsets API,可以把UI模式的切换时机延迟到转场动画的周期内,用postOnAnimation保证和动画同步:

全屏Fragment中调整时机:

override fun onStart() {
    super.onStart()
    // 延迟到下一帧动画周期再隐藏UI,避免和转场动画冲突
    requireActivity().window.decorView.postOnAnimation {
        requireActivity().window.decorView.systemUiVisibility = (
            View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            or View.SYSTEM_UI_FLAG_FULLSCREEN
            or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        )
    }
}

override fun onStop() {
    super.onStop()
    // 延迟到退出动画启动后再恢复UI
    requireActivity().window.decorView.postOnAnimation {
        requireActivity().window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
    }
}

同时确保Fragment转场动画是通过setCustomAnimations设置的,比如:

supportFragmentManager.beginTransaction()
    .setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right)
    .replace(R.id.fragment_container, FullScreenFragment())
    .addToBackStack(null)
    .commit()

方案三:优化全屏DialogFragment

之前你用DialogFragment出现卡顿,大概率是没设置正确的全屏样式和动画。可以这样优化:

1. 创建全屏Dialog样式

在styles.xml中添加:

<style name="FullScreenDialog" parent="Theme.AppCompat.Light.Dialog">
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowIsFloating">false</item>
    <item name="android:windowAnimationStyle">@style/DialogAnimation</item>
</style>

<style name="DialogAnimation">
    <item name="android:windowEnterAnimation">@anim/slide_in_right</item>
    <item name="android:windowExitAnimation">@anim/slide_out_right</item>
</style>

2. 在DialogFragment中应用样式

class FullScreenDialogFragment : DialogFragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setStyle(STYLE_NORMAL, R.style.FullScreenDialog)
    }
    
    // 其余逻辑和普通Fragment一致即可
}

这种方式下,DialogFragment的全屏由系统原生处理,转场动画能和UI隐藏完美同步,不会出现卡顿。

关键注意点

  • 避免在onCreateView或onDestroyView中直接修改系统UI模式,此时视图还未完全布局/销毁,容易触发布局重绘和动画冲突。
  • 优先选择WindowInsets API,这是Android官方推荐的现代方案,兼容性更好,也更贴合系统布局逻辑。
  • 如需支持Android 10及以下,务必使用ViewCompat的相关方法做兼容。

内容的提问来源于stack exchange,提问作者ribafish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:40:33