在surfaceView设置相机预览保持宽高比与实际屏显一致


实现方式一:
try{
Camera.Parameters params = mCamera.getParameters();
Point realScreenSize = DisplayUtil. getRealScreenMetrics(CameraActivity. this);
Point previewSize = DisplayUtil. getBestCameraResolution(params, realScreenSize);
params.setPreviewSize(previewSize. x, previewSize. y); // 获得摄像区域的大小
try{
mCamera.setParameters(params); // 把上面的设置 赋给摄像头
} catch(Exception e) {
e.printStackTrace();
}
mCamera.setPreviewDisplay(surfaceHolder); // 把摄像头获得画面显示在 SurfaceView 控件里面
mCamera.startPreview(); // 开始预览
setCameraDisplayOrientation(CameraActivity. this, mCurrentCamIndex, mCamera); // 调节预览方向,参数 mCurrentCamIndex是相机的id
} catch(Exception e) {
e.printStackTrace();
}
基于屏幕的宽度和包含底部虚拟按钮的屏幕高度的比值,然后从camera获取其参数——Camera.Parameters,利用camera参数获取相机支持的预览尺寸集,即:getSupportedPreviewSizes()(返回值为List<Camera.Size>),从中选择出宽高比与屏幕宽高比最接近的宽高比,最后将此宽高比对应的Camera.Size设置为预览尺寸,然后再将设置好的params重新设置给camera。

以下是根据屏幕实际尺寸的宽高比选出相机支持的预览尺寸中与之最为接近的尺寸:
/**
* 获取最佳预览大小
*
* @param parameters 相机参数
* @param screenResolution 屏幕宽高
* @return
*/
public staticPoint getBestCameraResolution(Camera.Parameters parameters, Point screenResolution) {
floattmp = 0f;
floatmindiff = 100f;
floatx_d_y = ( float) screenResolution. x/ ( float) screenResolution. y;
Camera.Size best = null;
List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
for(Camera.Size s : supportedPreviewSizes) {
tmp = Math. abs((( float) s. height/ ( float) s. width) - x_d_y);//通过调试运行可以发现,这里的宽是大于高的,也就是横置屏幕的效果。所以我们用高/宽来作为实际竖屏时的宽高比。
if(tmp < mindiff) {
mindiff = tmp;
best = s;
}
}
return newPoint(best. width, best. height);
}

如果不是从camera的预览尺寸集中选取尺寸的话,在相机setParameters时会报错: java.Lang.RuntimeException, setParameters failed ......
一般出现此错误的原因是:设置的预览尺寸previewSize或设置的输出图片尺寸pictureSize等不合理(底层不支持)。
比如我的手机分辨率是1920*1080但经一般的测量方式计算后,屏幕的宽是1080,高是1800,高度上减少是因为底部的虚拟按钮的高度没被计算进来,但我所测量的宽和高是实际的宽高,包含状态栏和底部虚拟按键(底部导航栏)。以下是获取实际宽高代码:
public staticPoint getRealScreenMetrics(Activity context) {
intrealWidth = 0, realHeight = 0;
try{
Display display = context.getWindowManager().getDefaultDisplay();
DisplayMetrics metrics = newDisplayMetrics();
display.getMetrics(metrics);
if(android.os.Build.VERSION. SDK_INT>= 17) {
Point size = newPoint();
display.getRealSize(size);
realWidth = size. x;
realHeight = size. y;
} else if(android.os.Build.VERSION. SDK_INT< 17
&& android.os.Build.VERSION. SDK_INT>= 14) {
Method mGetRawH = Display. class.getMethod( "getRawHeight");
Method mGetRawW = Display. class.getMethod( "getRawWidth");
realWidth = (Integer) mGetRawW.invoke(display);
realHeight = (Integer) mGetRawH.invoke(display);
} else{
realWidth = metrics. widthPixels;
realHeight = metrics. heightPixels;
}

} catch(Exception e) {
e.printStackTrace();
}
return newPoint(realWidth, realHeight);
}
最后一定要记得,调整预览方向为portrait,即竖屏。以下是竖屏调整代码:
// 根据横竖屏自动调节 preview 方向, Starting from API level 14, this method can be called when preview is active.
private static voidsetCameraDisplayOrientation(Activity activity, intcameraId, Camera camera) {
Camera.CameraInfo info = newCamera.CameraInfo();
Camera. getCameraInfo(cameraId, info);
introtation = activity.getWindowManager().getDefaultDisplay().getRotation();

//degrees the angle that the picture will be rotated clockwise. Valid values are 0, 90, 180, and 270.
//The starting position is 0 (landscape).
intdegrees = 0;
switch(rotation) {
caseSurface. ROTATION_0:
degrees = 0;
break;
caseSurface. ROTATION_90:
degrees = 90;
break;
caseSurface. ROTATION_180:
degrees = 180;
break;
caseSurface. ROTATION_270:
degrees = 270;
break;
}
intresult;
if(info. facing== Camera.CameraInfo. CAMERA_FACING_FRONT) {
result = (info. orientation+ degrees) % 360;
result = ( 360- result) % 360; // compensate the mirror
} else{
// back-facing
result = (info. orientation- degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}

实现方式二(推荐,因为简洁):
借助 CamcorderProfile类
public voidsurfaceChanged(SurfaceHolder surfaceHolder, intformat, intwidth, intheight) {
try{
// 设置手机前置摄像头预览的
CamcorderProfile profile= (CamcorderProfile. get( mCurrentCamIndex, CamcorderProfile. QUALITY_HIGH));
Camera.Parameters params = mCamera.getParameters();
params.setPreviewSize(profile. videoFrameWidth, profile. videoFrameHeight);
try{
mCamera.setParameters(params); // 把上面的设置 赋给摄像头
} catch(Exception e) {
e.printStackTrace();
}
mCamera.setPreviewDisplay(surfaceHolder); // 把摄像头获得画面显示在 SurfaceView 控件里面
mCamera.startPreview(); // 开始预览
previewing= true;
setCameraDisplayOrientation(CameraActivity. this, mCurrentCamIndex, mCamera); // 调节预览方向
} catch(Exception e) {
e.printStackTrace();
}
}





版权声明:本文为ds19920925原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。