Custom Camera Preview Issue (Stretch)

2019-07-06 21:13发布

问题:

I'm having a problem with my camera app. My app has:

1) a CameraActivity.class and

2) a CameraPreview.class.

CameraPreview implement a surfaceView where it's called from CameraActivity for the actual preview. (Also in CameraActivity I have the parameters)

Now the problem: When the preview is starting the preview is stretched. I tried a lot of things (so many that I cannot recall) I need someone to tell me what to write and where. Thanks in advance.

Here is the CameraActivity(Not all the code but the important I think)

    private PictureCallback mPicture = new PictureCallback() {

    public void onPictureTaken(byte[] data, Camera camera) {

        // Replacing the button after a photho was taken.
        flBtnContainer.setVisibility(View.GONE);
        ibRetake.setVisibility(View.VISIBLE);
        ibUse.setVisibility(View.VISIBLE);

        // File name of the image that we just took.
        fileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()).toString() + ".jpg";

        // Creating the directory where to save the image. Sadly in older
        // version of Android we can not get the Media catalog name
        File mkDir = new File(sdRoot, dir);
        mkDir.mkdirs();

        // Main file where to save the data that we recive from the camera
        File pictureFile = new File(sdRoot, dir + fileName);

        try {
            FileOutputStream purge = new FileOutputStream(pictureFile);
            purge.write(data);
            purge.close();
        } catch (FileNotFoundException e) {
            Log.d("DG_DEBUG", "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d("DG_DEBUG", "Error accessing file: " + e.getMessage());
        }

        // Adding Exif data for the orientation. For some strange reason the
        // ExifInterface class takes a string instead of a file.
        try {
            exif = new ExifInterface("/sdcard/" + dir + fileName);
            exif.setAttribute(ExifInterface.TAG_ORIENTATION, "" + orientation);
            exif.saveAttributes();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Intent intent = new Intent(CameraActivity.this, PicturePreview.class);
        Bundle extras = new Bundle();
        extras.putString("ImagePath", String.valueOf(pictureFile));
        intent.putExtras(extras);
        startActivity(intent);

        //SendBroadcasts let's us instantly update the SD card with our image
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://"+Environment.getExternalStorageDirectory())));

    }



};



private void createCamera() {
    // Create an instance of Camera
    mCamera = getCameraInstance();

//        Setting the right parameters in the camera
    Camera.Parameters params = mCamera.getParameters();
    params.setRotation(90);
    mCamera.setParameters(params);

    // Create our Preview view and set it as the content of our activity.
    mPreview = new CameraPreview(this, mCamera);
    FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
    preview.addView(mPreview, 0);
}


@Override
protected void onResume() {
    super.onResume();

    // Test if there is a camera on the device and if the SD card is
    // mounted.
    if (!checkCameraHardware(this)) {
        Intent i = new Intent(this, NoCamera.class);
        startActivity(i);
        finish();
    } else if (!checkSDCard()) {
        Intent i = new Intent(this, NoSDCard.class);
        startActivity(i);
        finish();
    }

    // Creating the camera
    createCamera();

    // Register this class as a listener for the accelerometer sensor
    ////sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
protected void onPause() {
    super.onPause();
    // release the camera immediately on pause event
    releaseCamera();

    // removing the inserted view - so when we come back to the app we
    // won't have the views on top of each other.
    FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
    preview.removeViewAt(0);
} 

And here is the CameraPreview.class

    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
boolean isPreviewRunning = true;




public CameraPreview(Context context, Camera camera) {
    super(context);
    mCamera = camera;

    // Install a SurfaceHolder.Callback so we get notified when the
    // underlying surface is created and destroyed.
    mHolder = getHolder();
    mHolder.addCallback(this);
    // deprecated setting, but required on Android versions prior to 3.0
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    mHolder.setFixedSize(100, 100);

}

public void surfaceCreated(SurfaceHolder holder) {
    // The Surface has been created, now tell the camera where to draw the
    // preview.
    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.setDisplayOrientation(90);
        mCamera.startPreview();
    } catch (IOException e) {
        Log.d("DG_DEBUG", "Error setting camera preview: " + e.getMessage());
    }

}

public void surfaceChanged(SurfaceHolder holder,
                           int format, int width, int height) {


    if (isPreviewRunning){
        return;
    }
    isPreviewRunning = true;

    // If your preview can change or rotate, take care of those events here.
    // Make sure to stop the preview before resizing or reformatting it.


    if (mHolder.getSurface() == null) {
        // preview surface does not exist
        return;
    }

    // stop preview before making changes
    try {
        mCamera.stopPreview();
    } catch (Exception e) {
        // ignore: tried to stop a non-existent preview
    }




    // make any resize, rotate or reformatting changes here

    // start preview with new settings
    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();


    } catch (Exception e) {
        Log.d("DG_DEBUG", "Error starting camera preview: " + e.getMessage());
    }

}



public void surfaceDestroyed(SurfaceHolder holder) {
    // empty. Take care of releasing the Camera preview in your activity.
}



}

Can someone tell what am I missing? If possible I can chat in Facebook or something for faster resolve of my problem..

Update: @LikeWhiteOnRice's solution.

Here is my original code

enter image description here

Here is with LikeWhiteOnRice's code:

enter image description here

Any thoughts?

回答1:

I added the code below to my camera preview class and it works for most devices. Just so you are aware, the camera library in Android is horrible and a huge pain to work with.

Put this function in your CameraPreview class:

private Camera.Size getOptimalSize(List<Camera.Size> sizes, int h, int w) {
    final double ASPECT_TOLERANCE = 0.05;
    double targetRatio = (double) w/h;

    if (sizes == null) {
        return null;
    }

    Camera.Size optimalSize = null;

    double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    for (Camera.Size size : sizes) {
        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
        if (Math.abs(size.height - targetHeight) < minDiff) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }

    if (optimalSize == null) {
        minDiff = Double.MAX_VALUE;
        for (Camera.Size size : sizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }

    return optimalSize;
}

In your surefaceCreated function, add this before you start your preview:

Camera.Parameters cameraParameters = mCamera.getParameters();
List<Camera.Size> previewSizes = cameraParameters.getSupportedPreviewSizes();
Camera.Size optimalPreviewSize = getOptimalSize(previewSizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
cameraParameters.setPreviewSize(optimalPreviewSize.width, optimalPreviewSize.height);
mCamera.setParameters(cameraParameters);

Edit: Also, I'm not sure if you want

mHolder.setFixedSize(100, 100);

in your constructor.