Android播放视频适配黑边问题类型总结
在Android开发视频播放类功能时,视频画面周边出现黑边是个很常见的问题,
下面把 Android 视频播放常见的「黑边」现象一次性梳理清楚——为什么会出现、如何快速定位、对应的 4 套解决方案。以后无论用 ExoPlayer、MediaCodec 还是第三方 SDK,直接按这张“排查表”打钩即可。
一、黑边产生的 3 类根因
类别 | 典型表现 | 触发场景 |
---|---|---|
① 内容本身带黑边 | 所有播放器、所有手机都能复现;ffplay 在 PC 上也能看到黑边 | 片源是 16:9,但上下或左右被硬编码了黑边(常见于转码或录屏) |
② 视频比例 ≠ 视图比例 | 换台手机黑边位置会变;横竖屏切换后黑边消失/出现 | 18:9、19.5:9 全面屏播放 16:9 视频;或者 4:3 老片在 16:9 屏播放 |
③ 视图没有真正“铺满” | 黑边高度正好 = 状态栏/导航栏高度;截图能看到系统装饰 | Android 10+ 手势导航、水滴屏、打孔屏等“安全区域”裁剪;TextureView 被系统强制限制 |
二、5 秒定位套路
电脑 ffplay 播放同一文件 → 仍有黑边 ⇒ ① 片源问题
换一台比例不同的手机 → 黑边位置变化 ⇒ ② 比例问题
黑边高度刚好 = 导航栏/状态栏高度 ⇒ ③ 视图问题
仅 Android 12+ 出现,且 TextureView → 99% 系统裁剪问题
三、4 套对应解决方案
场景 | 处理思路 | 代码/操作要点 |
---|---|---|
① 片源黑边 | 转码时直接裁掉黑边;或播放端动态裁剪 | ffmpeg -i in.mp4 -filter:v "crop=1280:720:0:60" out.mp4 |
② 比例不一致 | 让用户选“等比裁剪”还是“等比留边” | ExoPlayer: setResizeMode(RESIZE_MODE_ZOOM) 或 RESIZE_MODE_FIT |
③ 视图未铺满 | 真正意义上的全屏 + 透明导航栏 | WindowCompat.setDecorFitsSystemWindows(window,false) + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
④ Android12 TextureView 被裁 | 换成 SurfaceView;或手动把 Surface 尺寸设成屏幕物理高 | 布局把 surface_type="surface_view" ;或 textureView.setLayoutParams(height=getRealHeight()) |
四、常见 SDK 设置速查
ExoPlayer
playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
阿里云播放器
mAliPlayer.setScaleMode(IPlayer.SCALE_ASPECT_FILL);
即构 ZEGO
setRenderMode(ZegoVideoRenderMode.FILL);
// 铺满,裁剪腾讯 liteav
TXCloudVideoView.setRenderMode(RENDER_MODE_FULL_FILL_SCREEN);
五、Exoplayer+TextureView导航栏黑边解决案例
1.设置全屏+导航栏透明
@Override
protected void onCreate(Bundle savedInstanceState) {// 1. 让内容延伸到导航栏后面Window window = getWindow();View decor = window.getDecorView();decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // 延伸到状态栏| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // 延伸到导航栏| View.SYSTEM_UI_FLAG_LAYOUT_STABLE); // 稳定布局,避免闪动// 2. 把导航栏颜色设成透明,黑色条消失window.setNavigationBarColor(Color.TRANSPARENT);WindowCompat.setDecorFitsSystemWindows(window, false);super.onCreate(savedInstanceState);setContentView(R.layout.xxx);
}
这样设置之后,导航栏会隐藏掉,但是视频画面无法延申到导航栏区域,底部出现导航栏高度的黑色区域。可以在 onResume
或 onWindowFocusChanged
中手动设置 TextureView
的 SurfaceTexture
大小为 屏幕完整高度(含导航栏):
TextureView textureView = findViewById(R.id.texture_view);
textureView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,getResources().getDisplayMetrics().heightPixels // 强制全屏高
));
或者在原有设置的高度基础上加上导航栏高度,就可以让视频画面扩展到导航栏区域。
videoParams.height = (int) (pos_h + MainApplication.navgationHeight); /*** 获取导航栏高度** @return*/public int getNavigationBarHeight() {int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");int height = getResources().getDimensionPixelSize(resourceId);return height;}
注意:不要设置android:fitsSystemWindows="true"属性,这个会让画面缩回导航栏和状态栏之外。