Android 相机预览在全屏上被拉伸

Android Camera Preview is stretched on Full screen

我正在为我的 AR View 应用使用自定义相机。相机预览在全屏纵向模式下被拉伸。

我在 Whosebug 上尝试了很多解决方案,但它不适用于我的代码。 谁能告诉我如何解决这个问题?

我在下面添加了我的相机工作代码和屏幕截图,其中预览被拉伸。

这是我的自定义相机Class:

public class ARCamera extends ViewGroup implements SurfaceHolder.Callback {
    private final static float Z_NEAR = 0.5f;
    private final static float Z_FAR = 2000;
    private final String TAG = ARCamera.class.getSimpleName();
    SurfaceView surfaceView;
    SurfaceHolder surfaceHolder;
    Camera camera;
    Activity activity;
    float[] projectionMatrix = new float[16];
    int cameraWidth;
    int cameraHeight;
    int screenWidth, screenHeight;
    int rotation;

    public ARCamera(Context context, SurfaceView surfaceView) {
        super(context);

        this.surfaceView = surfaceView;
        this.activity = (Activity) context;
        surfaceHolder = this.surfaceView.getHolder();
        surfaceHolder.addCallback(this);

        DisplayMetrics displayMetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);

        rotation = windowManager.getDefaultDisplay()
                .getRotation();

        this.screenWidth = displayMetrics.widthPixels;
        this.screenHeight = displayMetrics.heightPixels;


    }

    public void setCamera(Camera camera) {
        this.camera = camera;

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
            if (camera != null) {

                Camera.CameraInfo info = new Camera.CameraInfo();
                Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info);

                int degrees = 0;
                switch (rotation) {
                    case Surface.ROTATION_0:
                        degrees = 0;
                        break;
                    case Surface.ROTATION_90:
                        degrees = 90;
                        break;
                    case Surface.ROTATION_180:
                        degrees = 180;
                        break;
                    case Surface.ROTATION_270:
                        degrees = 270;
                        break;
                }

                camera.setDisplayOrientation((info.orientation - degrees + 360) % 360);

                camera.setPreviewDisplay(holder);
            }
        } catch (IOException exception) {
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        if (camera != null) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.release();
            camera = null;
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (camera != null) {
            this.cameraWidth = width;
            this.cameraHeight = height;

            Camera.Parameters parameters = camera.getParameters();
            try {
                List<Camera.Size> supportedSizes = camera.getParameters().getSupportedPreviewSizes();
                for (Camera.Size element : supportedSizes) {
                    element.width -= cameraWidth;
                    element.height -= cameraHeight;
                }
                Collections.sort(supportedSizes, new ResolutionOrders());
                parameters.setPreviewSize(width + supportedSizes.get(supportedSizes.size() - 1).width, height + supportedSizes.get(supportedSizes.size() - 1).height);
            } catch (Exception ex) {
                parameters.setPreviewSize(screenWidth, screenHeight);
            }

            this.camera.setParameters(parameters);
            this.camera.startPreview();

            generateProjectionMatrix();
        }
    }

    private void generateProjectionMatrix() {
        float ratio = (float) this.screenWidth / this.screenHeight;
        final int OFFSET = 0;
        final float LEFT = -ratio;
        final float RIGHT = ratio;
        final float BOTTOM = -1;
        final float TOP = 1;
        Matrix.frustumM(projectionMatrix, OFFSET, LEFT, RIGHT, BOTTOM, TOP, Z_NEAR, Z_FAR);
    }

    public float[] getProjectionMatrix() {
        return projectionMatrix;
    }

    class ResolutionOrders implements java.util.Comparator<Camera.Size> {
        public int compare(Camera.Size left, Camera.Size right) {
            return Float.compare(left.width + left.height, right.width + right.height);
        }
    }

}

在表面改变块中,您必须根据您的设备以编程方式选择最佳尺寸。

List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();

这将为您提供支持的相机总尺寸。你必须从中选择合适的尺寸。我使用了名为“chooseOptimalSize”的函数。

Size aspectRatio = new Size(matrix.widthPixels, matrix.heightPixels);

Camera.Size previewSize = chooseOptimalSize(previewSizes, PREFERRED_PREVIEW_WIDTH, PREFERRED_PREVIEW_HEIGHT, aspectRatio, this);

这是函数“chooseOptimalSize”:-

public static Camera.Size chooseOptimalSize(List<Camera.Size> choices, int width, int height, Size aspectRatio, Context c) {
        // Collect the supported resolutions that are at least as big as the preview Surface
        List<Size> bigEnough = new ArrayList<>();
        int w = aspectRatio.getWidth();
        int h = aspectRatio.getHeight();
        double ratio = (double) h / w;
        int loopCounter=0;
        for (Camera.Size size : choices) {
            int orientation = c.getResources().getConfiguration().orientation;
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                //if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=720) {
                //if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=3840 ) {
                //if((size.getWidth()/16) == (size.getHeight()/9) && size.getWidth() <=5120 ) {//Retina 5K
                if((size.width/16) == (size.height/9) && size.width <=7680 ) {//8K UHDTV Super Hi-Vision
                    Log.e(TAG, "chooseOptimalSize:"+size+"--LoopPosition---==>"+loopCounter);
                    return size;
                }
            } else {
                Log.e(TAG, "chooseOptimalSize:--given--"+size);
                if((size.width/16) == (size.height/9) && ((size.width <=1280)||(size.height<=1920))) {
                    //if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=4320 ) ) {//8K UHDTV Super Hi-Vision
                    //if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=2880 ) ) {//Retina 5K
                    //if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=2160 ) ) {
                    //if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=1280 ) ) {
                    //if((size.getWidth()/16) == (size.getHeight()/9) && (size.getWidth() <=4480 && size.getWidth() >=1280) ) {
                    Log.e(TAG, "chooseOptimalSize:"+size+"-16:9"+"--LoopPosition---==>"+loopCounter);
                    return size;
                }else if((size.width/18) == (size.height/9) && ((size.width <=3840)||(size.height<=2160))) {
                    Log.e(TAG, "chooseOptimalSize:"+size+"-18:9"+"--LoopPosition---==>"+loopCounter);
                    return size;
                }else if((size.width/18.5) == (size.height/9) && ((size.width <=3840)||(size.height<=2160))) {
                    Log.e(TAG, "chooseOptimalSize:"+size+"-18.5:9"+"--LoopPosition---==>"+loopCounter);
                    return size;
                }else if((width/19) == (height/9) && ((width <=3840)||(height<=2160))) {
                    /*if((size.getWidth()/19) == (size.getHeight()/9) && ((size.getWidth() <=3840)||(size.getHeight()<=2160))) {*/
                    Log.e(TAG, "chooseOptimalSize:"+size+"-19:9"+"--LoopPosition---==>"+loopCounter);
                    return size;
                }else if((size.width/19.5) == (size.height/9) && ((size.width<=3840)||(size.height<=2160))) {
                    Log.e(TAG, "chooseOptimalSize:"+size+"-19.5:9"+"--LoopPosition---==>"+loopCounter);
                    return size;
                }else{
                    Log.e(TAG, "chooseOptimalSize"+" not proper aspect resolution");
                }
                //2340
            }

//            if(screenWidth==size.getWidth()){
//                Log.e(TAG1, loopCounter+".choose:width Matched:"+screenWidth+"="+size.getWidth());
//            }else{
//                Log.e(TAG1, loopCounter+".choose:width Not Matched:"+screenWidth+"="+size.getWidth());
//            }
//
//            if(screenHeight==size.getHeight()){
//                Log.e(TAG1, loopCounter+".choose:height Matched:"+screenHeight+"="+size.getHeight());
//            }else{
//                Log.e(TAG1, loopCounter+".choose:height Not Matched:"+screenHeight+"="+size.getHeight());
//            }
            loopCounter++;
        }
        // Pick the smallest of those, assuming we found any
//        if (bigEnough.size() > 0) {
//            return Collections.min(bigEnough, new CompareSizesByArea());
//        } else {
//            Log.e(TAG, "Couldn't find any suitable preview size");
            return choices.get(0);
//        }
    }


    /*
     * Compares two {@code Size}s based on their areas.
     */
    static class CompareSizesByArea implements Comparator<Size> {
        @Override
        public int compare(Size lhs, Size rhs) {
            // We cast here to ensure the multiplications won't overflow
            return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
                    (long) rhs.getWidth() * rhs.getHeight());
        }
    }

如果您愿意,可以取消注释代码,在我的情况下不需要该代码。

此功能将为您提供设备相机的最佳尺寸。您可以使用 :

进行设置
parameters.setPreviewSize(previewSize.width,previewSize.height);

大功告成!!!