基于ExoPlayer实现可滚动视频并无损裁剪为1:1比例的技术求助
嘿,我来帮你搞定这个ExoPlayer的需求!下面分核心模块拆解方案,代码示例和逻辑都给你安排得明明白白:
实现可滚动视频 + 1:1无损裁剪的ExoPlayer方案
我们分两个核心部分解决你的需求:根据视频尺寸动态添加滚动 和 1:1固定尺寸无损裁剪,还可以结合两者做模式切换。
一、动态实现滚动逻辑
要根据视频实际尺寸决定是否启用滚动,核心是监听视频尺寸变化,再和容器尺寸对比调整外层滚动容器的显示。
1. 基础布局结构
用NestedScrollView(支持垂直滚动)嵌套HorizontalScrollView(支持水平滚动),包裹PlayerView。这样能同时应对横竖方向的滚动需求:
<!-- 滚动容器:默认隐藏,根据视频尺寸动态显示 --> <androidx.core.widget.NestedScrollView android:id="@+id/scrollContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical" android:visibility="gone"> <HorizontalScrollView android:layout_width="wrap_content" android:layout_height="wrap_content" android:scrollbars="horizontal"> <!-- 核心视频播放视图 --> <com.google.android.exoplayer2.ui.PlayerView android:id="@+id/playerView" android:layout_width="wrap_content" android:layout_height="wrap_content" app:resizeMode="none" /> </HorizontalScrollView> </androidx.core.widget.NestedScrollView>
2. 监听视频尺寸并启用滚动
通过ExoPlayer的Player.Listener获取视频原生尺寸,对比容器尺寸后动态调整滚动状态:
val playerView = findViewById<PlayerView>(R.id.playerView) val scrollContainer = findViewById<NestedScrollView>(R.id.scrollContainer) val horizontalScroll = scrollContainer.getChildAt(0) as HorizontalScrollView // 初始化ExoPlayer val player = ExoPlayer.Builder(this).build() playerView.player = player // 监听视频尺寸变化 player.addListener(object : Player.Listener { override fun onVideoSizeChanged(videoSize: VideoSize) { super.onVideoSizeChanged(videoSize) // 获取旋转后的视频原生宽高(已自动适配视频横竖屏) val videoWidth = videoSize.width val videoHeight = videoSize.height // 获取容器可用尺寸(这里以屏幕尺寸为例,你也可以替换为自定义容器尺寸) val displayMetrics = resources.displayMetrics val containerWidth = displayMetrics.widthPixels val containerHeight = displayMetrics.heightPixels // 判断是否需要滚动 val needVerticalScroll = videoHeight > containerHeight val needHorizontalScroll = videoWidth > containerWidth // 显示/隐藏滚动容器 scrollContainer.visibility = if (needVerticalScroll || needHorizontalScroll) View.VISIBLE else View.GONE // 控制滚动条显示 horizontalScroll.isHorizontalScrollBarEnabled = needHorizontalScroll scrollContainer.isVerticalScrollBarEnabled = needVerticalScroll // 设置PlayerView为视频原生尺寸,确保滚动能显示完整内容 playerView.layoutParams.width = videoWidth playerView.layoutParams.height = videoHeight playerView.requestLayout() } }) // 设置播放源(替换成你的视频地址/本地路径) val mediaItem = MediaItem.fromUri("your_video_url_or_path") player.setMediaItem(mediaItem) player.prepare() player.play()
二、实现1:1固定尺寸无损裁剪
要做到无损裁剪,核心是让PlayerView固定为正方形,再用ExoPlayer的resizeMode="crop"模式——这种模式会直接裁剪视频的中心区域为1:1,不会缩放像素,完全保留画质。
1. 调整PlayerView布局
把PlayerView设置为固定正方形,这里提供两种常见方式:
- 方式1:适配屏幕宽度的正方形
<com.google.android.exoplayer2.ui.PlayerView android:id="@+id/playerView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_aspectRatio="1:1" app:resizeMode="crop" /> <!-- 关键:居中裁剪为1:1 -->
- 方式2:固定具体尺寸的正方形(比如300dp×300dp)
<com.google.android.exoplayer2.ui.PlayerView android:id="@+id/playerView" android:layout_width="300dp" android:layout_height="300dp" app:resizeMode="crop" />
2. 无损画质的关键要点
- 确保PlayerView的尺寸不超过视频原生分辨率:比如视频是1080×1920(竖屏),PlayerView设为1080×1080,裁剪后取视频中间1080×1080区域,画质完全无损。
- 避免使用
fit/zoom等缩放模式,这些会改变像素导致画质损失,crop是直接截取原视频区域,不会有画质损耗。
三、两种模式的切换逻辑
如果需要让用户在「完整滚动查看」和「1:1裁剪查看」之间切换,可以加个按钮实现:
// 切换到1:1裁剪模式 btnSwitchToCrop.setOnClickListener { playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_CROP // 设置PlayerView为屏幕宽度的正方形 val squareSize = resources.displayMetrics.widthPixels playerView.layoutParams.width = squareSize playerView.layoutParams.height = squareSize playerView.requestLayout() // 隐藏滚动容器 scrollContainer.visibility = View.GONE } // 切换回完整滚动模式 btnSwitchToFull.setOnClickListener { playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_NONE // 恢复PlayerView为视频原生尺寸 val videoSize = player.videoSize playerView.layoutParams.width = videoSize.width playerView.layoutParams.height = videoSize.height playerView.requestLayout() // 重新判断是否需要滚动 val containerWidth = resources.displayMetrics.widthPixels val containerHeight = resources.displayMetrics.heightPixels val needScroll = videoSize.height > containerHeight || videoSize.width > containerWidth scrollContainer.visibility = if (needScroll) View.VISIBLE else View.GONE }
内容的提问来源于stack exchange,提问作者Android Developer
相关产品推荐
相关产品推荐

