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

Pixel 7上Jetpack Compose+ExoPlayer竖屏初始加载视频裁剪问题

解决Jetpack Compose中ExoPlayer竖屏首次加载视频裁剪问题

问题核心

首次竖屏打开时,ExoPlayer的Surface还未适配Compose fillMaxSize布局的最终尺寸,导致视频渲染时出现裁剪;而横竖屏切换会触发布局重测与Surface重新适配,因此问题消失。直接去掉fillMaxSize虽能解决,但无法满足控制器置于底部的布局需求。

可行解决方案

1. 监听布局尺寸变化,动态适配Surface

利用Compose的Modifier.onSizeChanged监听视频容器的尺寸,待尺寸稳定(宽高均大于0)后,再配置ExoPlayer的Surface或调整视频缩放模式:

Box(modifier = Modifier.fillMaxSize()
    .onSizeChanged { size ->
        if (size.width > 0 && size.height > 0) {
            exoPlayer?.videoScalingMode = C.VIDEO_SCALING_MODE_FIT
            exoPlayerView?.layoutParams = FrameLayout.LayoutParams(
                size.width,
                size.height
            )
        }
    }) {
    AndroidView(
        factory = { context ->
            StyledPlayerView(context).apply {
                player = exoPlayer
                useController = false // 自定义控制器
            }
        },
        modifier = Modifier.matchParentSize()
    )
    // 底部自定义控制器
    Row(
        modifier = Modifier.align(Alignment.BottomCenter)
            .fillMaxWidth()
            .background(Color.Black.copy(alpha = 0.5f))
            .padding(8.dp)
    ) {
        // 播放/暂停、进度条等控件
    }
}

2. 结合AspectRatio修饰符固定视频比例

在fillMaxSize的容器内,给视频区域添加aspectRatio修饰符,确保视频渲染时匹配原始比例,避免裁剪:

Box(modifier = Modifier.fillMaxSize()) {
    AndroidView(
        factory = { context ->
            StyledPlayerView(context).apply {
                player = exoPlayer
                useController = false
                resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
            }
        },
        modifier = Modifier.fillMaxSize()
            .aspectRatio(16f / 9f) // 替换为视频实际宽高比
            .align(Alignment.Center)
    )
    // 底部控制器
    // ...
}

这里的关键是给StyledPlayerView设置resizeMode = RESIZE_MODE_FIT,配合Compose的aspectRatio修饰符,让视频在容器内按比例适配,同时外层fillMaxSize保证控制器能置于底部。

3. 延迟初始化ExoPlayer至布局完成

通过ViewTreeObserver监听布局完成事件,等Compose布局稳定后再初始化ExoPlayer,避免Surface提前渲染:

AndroidView(
    factory = { context ->
        val playerView = StyledPlayerView(context)
        playerView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                playerView.viewTreeObserver.removeOnGlobalLayoutListener(this)
                val exoPlayer = ExoPlayer.Builder(context).build()
                exoPlayer.setMediaItem(MediaItem.fromUri(uri))
                exoPlayer.playWhenReady = true
                playerView.player = exoPlayer
            }
        })
        playerView
    },
    modifier = Modifier.fillMaxSize()
)

为什么之前的方案无效?

  • AspectRatioFrameLayout:若未同步Compose的布局更新时机,Surface可能在布局测量完成前就已初始化,导致比例适配失效。
  • 初始化时强制切换横竖屏:属于临时hack,部分设备上布局未完全稳定就切回竖屏,Surface仍未适配正确尺寸,问题复现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 03:17:35