一、问题背景
Android 设备的物理屏幕尺寸和播放的视频尺寸的比例关系不对等,例如下面这幅图,很明显是由于视频显示时被拉伸了,看上去十分别扭。不仅仅是 Android 设备,任何设备在播放视频时都存在尺寸拉伸等问题,本文从问题出发,提出在日常开发中经常用到的适配策略。
二、尺寸适配
上图的实验环境是:
- Nexus5 1080x1920 (adb shell wm size)
- Video 1080x608
为了方便描述,将视频的宽高比例记为 VideoAR,将手机的宽高比例记为 ViewAR,那么上图的 VideoAR 和 ViewAR 关系如下图:
显然视频的比例无法发生变化,所以只能改变 View 显示出来的比例关系,所以再引入一个 NewViewAR 表示经过适配之后的宽高比。那么视频的尺寸适配就等价于 ViewAR 和 NewViewAR 之间的关系了。常见的适配模式:
- Fill:NewViewAR = ViewAR,此时新的 View 的宽高和之前 View 的宽高一样,未发生变化。
- Fit: NewViewAR > ViewAR,按照视频的比例缩小显示,这种模式下看到的视频一般是左右或者上下留有黑边的。在上图这种情况下,会保持 View 的宽度不变,View 的高度会变小。
- Crop: NewViewAR > ViewAR,按比例放大显示,因为放大之后超出屏幕外的无法显示,造成一种被裁剪的视觉效果。在上图这种情况下,会保持 View 的高度不变,View 的宽度会变大。
如下图:
核心代码如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (videoAspectRatio <= 0) {
// Aspect ratio not set.
return;
}
int width = getMeasuredWidth();
int height = getMeasuredHeight();
float viewAspectRatio = (float) width / height;
switch (resizeMode) {
case RESIZE_FIT:
if (videoAspectRatio > viewAspectRatio) {
height = (int) (width / videoAspectRatio);
} else {
width = (int) (height * videoAspectRatio);
}
break;
case RESIZE_CROP:
if (videoAspectRatio > viewAspectRatio) {
width = (int) (height * videoAspectRatio);
} else {
height = (int) (width / videoAspectRatio);
}
default:
case RESIZE_FILL:
break;
}
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
适配之后的效果图:
Fit:
Crop: