Android相机预览页面被压缩和拉伸问题

最近公司要求在原有的项目中添加一个扫码登录的功能,在调试好相机之后,发现相机返回到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);
    }

你可能感兴趣的:(android,java)