android Camera预览界面拉伸问题解决

问题现象

项目中的扫一扫界面打开以后,扫描二维码的界面显示的二维码被拉伸,图片如下:
在这里插入图片描述

问题原因

通常,拍照预览页面的视图拉伸主要与下面两个因素有关:

  • Surfaceview的大小
  • Camera中的Preview的大小

如果手机surfaceview大小为比例为16:9,而预览尺寸大小为比例为4:3,从上面的二维码可以看到明显的拉伸。正因为surfaceview的宽高比例跟camera preview的宽高比例不一样才会产生这样的效果。如果surfaceview尺寸比例跟预览尺寸比例相同,那便不会产生变形。由此可以得出结论:

  • 1.手机surfaceview与默认的camera preview的宽高比不一致
  • 2.camera preview的宽高比设置不正确

解决方法

然后道长翻找ZXing的代码(具体版本可能非常老了),找到如下一段代码:

  /**
     * 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();
        }
    }

其中有两个方法调用,一个是初始化相机参数的initFromCameraParameters()和设置相机参数的setDesiredCameraParameters()。然后我发现在设置相机参数的方法中已经设置对应的parameters,setDesiredCameraParameters()代码如下:

    /**
     * Sets the camera up to take preview images which are used for both preview and decoding.
     * We detect the preview format here so that buildLuminanceSource() can build an appropriate
     * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
     * and the planar Y can be used for barcode scanning without a copy in some cases.
     */
    void setDesiredCameraParameters(Camera camera) {
        Camera.Parameters parameters = camera.getParameters();
        Log.d(TAG, "Setting preview size: " + cameraResolution);
        parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
        setFlash(parameters);
        setZoom(parameters);
        //setSharpness(parameters);
        //modify here
        camera.setDisplayOrientation(90);
        camera.setParameters(parameters);
    }

在上面的代码中,这一行代码便是设置相机预览界面的分辨率的。

parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);

然后在初始化相机参数的方法initFromCameraParameters()中获取了cameraResolution这个参数的值,代码如下:

    /**
     * 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);
    }

然后把上面代码中getCameraResolution()方法改为了网上以为老哥的方法,代码如下:

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<Camera.Size> 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;
    }
    /**
     * 通过对比得到与宽高比最接近的预览尺寸(如果有相同尺寸,优先选择)
     *
     * @param isPortrait 是否竖屏
     * @param surfaceWidth 需要被进行对比的原宽
     * @param surfaceHeight 需要被进行对比的原高
     * @param preSizeList 需要对比的预览尺寸列表
     * @return 得到与原宽高比例最接近的尺寸
     */
    public static Point getOptimalPreviewSize(boolean isPortrait, List<Camera.Size> 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);
    }

到此,道长已经解决了Camera预览界面拉伸的问题,希望这篇博客对你有所帮助。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值