javaandroidcameraandroid-cameraandroid-framelayout

Android full screen camera - while keeping the camera selected ratio


We are trying to build something similar to Instagram Camera screen. i.e allow the user taking square photos. While doing it out U.i must be able to let the user see the camera on fullScreen mode. We want to force the user to take an image in a portrait mode

Getting camera possible ratio's

We are calculating the best ratio available from camera by

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

    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;
}

Make it full screen.

public FrameLayout setCameraLayout(int width, int height) {
     float newProportion = (float) width / (float) height;
     // Get the width of the screen
     int screenWidth =     this.customCameraActivity.getWindowManager().getDefaultDisplay()
            .getWidth();
     int screenHeight =    this.customCameraActivity.getWindowManager().getDefaultDisplay()
            .getHeight();
      float screenProportion = (float) screenWidth / (float) screenHeight;
      // Get the SurfaceView layout parameters
      ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)preview.getLayoutParams();
    

     if (newProportion > screenProportion) {
        lp.width = screenWidth;
        lp.height = (int) ((float) screenWidth / newProportion);
     } else {
        lp.width = (int) (newProportion * (float) screenHeight);
        lp.height = screenHeight;
    }
   
    //calculate the amount that takes to make it full screen (in the `height` parameter)
    float propHeight = screenHeight / lp.height;
   
    //make it full screen(
    lp.width = (int)(lp.width * propHeight);
    lp.height = (int)(lp.height * propHeight);

    frameLayout.setLayoutParams(lp);
    return frameLayout;

}

setCameraLayout callers

OnCreate from the Activity and afterwards surfaceChanged

 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width,
                           int height) {
    if (getHolder().getSurface() == null) {
        return;
    }
    try {
        cameraManager.getCamera().stopPreview();
    } catch (Exception e) {
        // tried to stop a non-existent preview
    }

    try {
        Camera.Parameters cameraSettings = cameraManager.getCamera().getParameters();

        cameraSettings.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        this.cameraView.setCameraLayout(mPreviewSize.width, mPreviewSize.height);
        cameraManager.getCamera().setParameters(cameraSettings);
        cameraManager.getCamera().setPreviewDisplay(holder);

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

Goal

Fullscreen camera preview on phone.

The problem

Now we are getting the preview with no distortion which is GOOD! and it has the same height as the phone as well which is also GOOD!. But! the width of the preview is bigger than the phone width (of-course) so it turns out the the center of the camera is not on the center of the phone. Possible solutions we have thought about:

  1. move the layout left to negative position to make the preview center in the center of the screen.
  2. crop the layout and draw only the center of the new preview that should be visible to the phone screens

Any ideas how to overcome this issue?


Solution

  • I managed successfully center the preview by setting gravity of SurfaceView:

    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(wid, hei);
    lp.gravity = Gravity.CENTER;
    mSv.setLayoutParams(lp);
    

    The code snippet is taken from here, and you're welcome to use the code if you have the patience to decipher it. I think I was solving similar issues (preview rotation, distortion, fit-in/fill)

    Good Luck.