开发安卓摄像头程序时,比如扫描二维码的程序,大家会使用SurfaceView进行实时图像的预览。很多同学发现,界面上呈现的图像是拉长的,显得比较瘦。有人在网上发布了 getOptimalPreviewSize方法,但是经过实测,我手头的四个手机,有两个依然是拉伸的。所以,新的图像大小方法的探索就比较重要了。
1,为什么预览图像扭曲了?
我们将surfaceView.getHoder().getSurface()传递给了cameraCaptureSession,但是我们却无法得知实际上cameraManager选择了什么尺寸的摄像头画面,而系统只是将他从摄像头获取的某个尺寸的图像通过非比例缩放的方式画在了我们给的surfaceView画布上。非比例缩放,大致了图像的扭曲,因为surfaceView的尺寸比例和实际摄像头画面的尺寸比例不相符,才会变形拉伸。
2,我们的目标
我们只有在摄像头实际在surfaceView上绘制图像前,将画布的尺寸比例设置为摄像头所得图像的尺寸比例,就可以保证摄像头使用比例缩放来绘制图像在界面上。由于摄像头不告知我们实际所用图像大小,我们只有臆想来选择一个自认为他可能在用的尺寸,作为设置画布的依据。
3,摄像头预览图像尺寸的确定
通过以下方法:
- Size supported_image_sizes[] = streamConfigurationMap.getOutputSizes(ImageFormat.PRIVATE);
-
- preferredImageSizeForPreview = Arrays.stream( supported_image_sizes )
- .map(size->new OutputSize(size, Math.abs(size.getHeight()-display_height),Math.abs(size.getWidth()*1f/size.getHeight())))
- .sorted((a,b)->a.value
1 : 1) : 1) - .findFirst()
- .get().size
- ;
其中用到一个小帮助类OutputSize,用以表征Stream良好运行,如下:
- private static class OutputSize{
- Size size;
- int value;
- float ratio;
- OutputSize(Size s, int a, float r){ size=s; value =a; ratio = r;}
- }
根据得到的尺寸 preferredImageSizeForPreview,依比例设置surfaceView画布大小即可。
- private void changeSurfaceViewSizeToMatchImageSize( SurfaceView surfaceView, Size prefer ) {
- DisplayMetrics display = getResources().getDisplayMetrics();
- int width = getResources().getConfiguration().orientation== Configuration.ORIENTATION_LANDSCAPE
- ? display.heightPixels*prefer.getWidth()/prefer.getHeight()
- : display.heightPixels*prefer.getHeight()/prefer.getWidth()
- ;
- ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams)surfaceView.getLayoutParams();
- params.width = width;
- surfaceView.setLayoutParams( params );
- surfaceView.getHolder().setFixedSize(width,display.heightPixels);
- }
同时,建议这个设置画布大小的方法在activity.onCreate()方法中调用,避免多次surfaceChanged方法回调。
4,实测效果
在手头能及的手机上测试后,结果正常,均无图像拉伸的现象。
5,说明
当然,各个厂商可能会更改摄像头画面尺寸选择的算法,那就需要针对不同厂家的机型进行更为详细的适配了;但上述方法的适应性,依然比较高。