最近公司要求在原有的项目中添加一个扫码登录的功能,在调试好相机之后,发现相机返回到Surfaceview页面上的预览图片,与我们现实中物品的比例并不相同,在一块正方形的Surfaceview中,预览界面的物品,也都会被压缩成正方形比例。为解决这一问题,经过在网上的不断的查找,找到一篇很实用的博客。android Camera预览界面拉伸问题解决_雨幕青山的博客-CSDN博客
问题原因分析:
在手机上看到的预览页面被拉伸或是被压缩,是由于surfaceview的宽高比例和camera preview的宽高比例不一样才会产生这样的效果,所以我们得出结论,要想surfaceview的页面保持正常,就需要surfaceview的尺寸比例跟预览的尺寸比例相同。
解决方式:
因为我们的项目是基于开源的ZXing 项目,来实现相机扫描的,所以需要修改ZXing中的代码。
在相机参数设置步骤中,有两个方法的调用,一个是初始化相机参数initFromCameraParameters和设置相机参数的方法setDesiredCameraParameters
/**
* Opens the camera driver and initializes the hardware parameters.
*
* @param holder The surface object which the camera will draw preview frames
* into.
* @throws IOException Indicates the camera driver failed to open.
*/
public void openDriver(SurfaceHolder holder) throws IOException {
if (camera == null) {
camera = Camera.open();
if (camera == null) {
throw new IOException();
}
camera.setPreviewDisplay(holder);
if (!initialized) {
initialized = true;
configManager.initFromCameraParameters(camera);
}
configManager.setDesiredCameraParameters(camera);
// FIXME
// SharedPreferences prefs =
// PreferenceManager.getDefaultSharedPreferences(context);
// �Ƿ�ʹ��ǰ��
// if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false))
// {
// FlashlightManager.enableFlashlight();
// }
FlashlightManager.enableFlashlight();
}
}
在setDesiredCameraParameters方法中有一段代码,是设置相机预览界面的分辨率的。
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
而cameraResolution的参数是从initFromCameraParameters()中获取的
/**
* Reads, one time, values from the camera that are needed by the app.
*/
void initFromCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
previewFormat = parameters.getPreviewFormat();
previewFormatString = parameters.get("preview-format");
Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
screenResolution = new Point(display.getWidth(), display.getHeight());
Log.d(TAG, "Screen resolution: " + screenResolution);
cameraResolution = getCameraResolution(parameters, screenResolution);
Log.d(TAG, "Camera resolution: " + screenResolution);
}
所以我们改变cameraResolution就可以改变预览的分辨率,在上述代码中,我们可以看出cameraResolution的值是通过getOptimalPreviewSize(parameters, screenResolution)获取的。所以修改getOptimalPreviewSize方法
private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) {
String previewSizeValueString = parameters.get("preview-size-values");
// saw this on Xperia
if (previewSizeValueString == null) {
previewSizeValueString = parameters.get("preview-size-value");
}
Point cameraResolution = null;
if (previewSizeValueString != null) {
Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString);
// cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
List sizeList = parameters.getSupportedPreviewSizes();//获取所有支持的camera尺寸
cameraResolution = getOptimalPreviewSize(true, sizeList, screenResolution.x, screenResolution.y);//获取一个最为适配的屏幕尺寸
}
if (cameraResolution == null) {
// Ensure that the camera resolution is a multiple of 8, as the screen may not be.
cameraResolution = new Point(
(screenResolution.x >> 3) << 3,
(screenResolution.y >> 3) << 3);
}
return cameraResolution;
}
在上述代码中与源代码不同的是,修改了一行代码,将
cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
修改为
List sizeList = parameters.getSupportedPreviewSizes();
cameraResolution = getOptimalPreviewSize(true, sizeList, screenResolution.x, screenResolution.y);
然后根据getOptimalPreviewSize,通过对比得到与宽高比最接近的预览尺度
/**
* 通过对比得到与宽高比最接近的预览尺寸(如果有相同尺寸,优先选择)
*
* @param isPortrait 是否竖屏
* @param surfaceWidth 需要被进行对比的原宽
* @param surfaceHeight 需要被进行对比的原高
* @param preSizeList 需要对比的预览尺寸列表
* @return 得到与原宽高比例最接近的尺寸
*/
public static Point getOptimalPreviewSize(boolean isPortrait, List preSizeList, int surfaceWidth, int surfaceHeight) {
int reqTmpWidth;
int reqTmpHeight;
// 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高
if (isPortrait) {
reqTmpWidth = surfaceHeight;
reqTmpHeight = surfaceWidth;
} else {
reqTmpWidth = surfaceWidth;
reqTmpHeight = surfaceHeight;
}
//先查找preview中是否存在与surfaceview相同宽高的尺寸
for(Camera.Size size : preSizeList){
if((size.width == reqTmpWidth) && (size.height == reqTmpHeight)){
return new Point(size.width, size.height);
}
}
// 得到与传入的宽高比最接近的size
float reqRatio = ((float) reqTmpWidth) / reqTmpHeight;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
Camera.Size retSize = null;
for (Camera.Size size : preSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
return new Point(retSize.width, retSize.height);
}