限时福利领取


在Android视频开发中,ExoPlayer的全屏切换功能看似简单,却暗藏不少技术细节。今天我们就来拆解这个高频需求,从原理到实战,一步步实现流畅的全屏体验。

全屏切换效果对比

一、为什么全屏切换总会出问题?

开发中常见的三大痛点:

  1. 黑屏闪烁:横竖屏切换时视频内容短暂消失
  2. 状态丢失:播放进度、音量控制等状态不同步
  3. 响应迟钝:点击全屏按钮后有明显延迟

这些问题往往源于Activity默认重建机制与视频播放器的特殊生命周期冲突。

二、两种主流方案对比

方案A:Activity重建(传统方案)

  • 优点:系统自动处理布局切换
  • 缺点:
  • 需要手动保存播放状态
  • 无法避免黑屏现象
  • 需要处理configChanges配置

方案B:Fragment动态布局(推荐方案)

  • 优点:
  • 无需重建Activity
  • 可实现无缝切换
  • 更精细的动画控制
  • 缺点:
  • 需要手动管理视图层级
  • 横竖屏尺寸计算较复杂

三、核心实现三步走

  1. 配置PlayerView
playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
playerView.useController = true // 启用默认控制器
  1. 监听布局变化
view.viewTreeObserver.addOnGlobalLayoutListener {
    val isLandscape = resources.configuration.orientation == 
        Configuration.ORIENTATION_LANDSCAPE
    updateFullscreenState(isLandscape)
}
  1. 处理系统UI
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, playerView).let {
    it.hide(WindowInsetsCompat.Type.systemBars())
    it.systemBarsBehavior = 
        WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}

四、完整代码示例

代码结构示意图

class FullScreenHelper(
    private val activity: Activity,
    private val playerView: PlayerView
) {
    // 关键状态标记
    private var isFullscreen = false

    fun toggleFullscreen() {
        isFullscreen = !isFullscreen
        activity.requestedOrientation = if (isFullscreen) {
            ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
        } else {
            ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
        updateSystemUi()
    }

    private fun updateSystemUi() {
        val controller = WindowCompat.getInsetsController(
            activity.window, 
            playerView
        )
        if (isFullscreen) {
            controller.hide(WindowInsetsCompat.Type.systemBars())
        } else {
            controller.show(WindowInsetsCompat.Type.systemBars())
        }
    }
}

五、AI辅助开发技巧

当使用Copilot生成代码时:

  1. 明确提示词: "生成ExoPlayer全屏切换的Kotlin代码,使用ViewTreeObserver监听,包含横竖屏状态切换"

  2. 必须人工检查:

  3. 生命周期回调是否完整
  4. 是否存在静态context引用
  5. 横竖屏标志位是否同步

六、性能优化要点

  1. Player实例复用
  2. 在onDestroy时释放资源
  3. 避免在切换时重复创建Player

  4. Surface处理

    override fun onPause() {
        playerView.player?.pause()
        super.onPause()
    }

七、三大避坑指南

  1. 分屏模式适配
  2. 重写onMultiWindowModeChanged
  3. 调整PlayerView的宽高比

  4. 输入法动画冲突

  5. 添加windowSoftInputMode调整
  6. 使用adjustNothing避免布局跳动

  7. 控制器焦点丢失

  8. 重写dispatchKeyEvent处理按键事件
  9. 保持控制器与Player状态同步

八、延伸思考

  1. 如何实现YouTube那样的悬浮小窗播放?
  2. 在折叠屏设备上应该如何优化全屏体验?

通过本文介绍的方法,我们项目中的全屏切换耗时从原来的800ms降低到了200ms以内。希望这些实践对你有帮助,欢迎在评论区交流你的实现方案!

Logo

音视频技术社区,一个全球开发者共同探讨、分享、学习音视频技术的平台,加入我们,与全球开发者一起创造更加优秀的音视频产品!

更多推荐