I'm trying to embed a camera preview in an activity. And it's only in portrait orientation. The problem is the preview gets stretched.
I've tried to pick the optimal size. But the problem is all supported preview sizes from getSupportedPreviewSizes()
returns sizes in landscape orientation. So picking the right size according to my code won't work I guess.
My layout XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_take_attendance"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:orientation="vertical"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.lab.rafael.smartattendance.TakeAttendanceActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/take_attendance_label"
android:id="@+id/take_attendance_label"
android:layout_marginBottom="@dimen/activity_vertical_margin"/>
<!-- camera preview container -->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/red"
android:id="@+id/take_attendance_scan_qr_frame"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/take_attendance_manual_text"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/take_attendance_manual_button"
android:id="@+id/take_attendance_manual_button"/>
</LinearLayout>
</LinearLayout>
Here's my CameraPreview
class:
package com.lab.rafael.smartattendance.camera;
import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
import java.util.List;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private Camera mCamera = null;
private SurfaceHolder mHolder = null;
private Camera.Size optimalSize = null;
public CameraPreview(Context context, Camera camera)
{
super(context);
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
Camera.Parameters params = mCamera.getParameters();
List<String> focusModes = params.getSupportedFocusModes();
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(holder);
if(focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
if(optimalSize != null) {
params.setPreviewSize(optimalSize.width, optimalSize.height);
}
mCamera.setParameters(params);
mCamera.startPreview();
} catch (IOException e)
{
Log.e("created_error", e.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if(mHolder.getSurface() == null) {
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
Log.e("changed_error", e.getMessage());
}
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e){
Log.e("error", e.getMessage());
}
}
@Override
public void onMeasure(int measureWidthSpec, int measureHeightSpec) {
optimalSize = getOptimalSize(MeasureSpec.getSize(measureWidthSpec), MeasureSpec.getSize(measureHeightSpec));
setMeasuredDimension(optimalSize.width, optimalSize.height);
}
protected Camera.Size getOptimalSize(int width, int height) {
List<Camera.Size> supportedSizes = mCamera.getParameters().getSupportedPreviewSizes();
double targetRatio = (double) width / height,
optimalRatio = 0.0,
acceptableRatioMargin = 0.1,
minDiff = Double.MAX_VALUE;
for(Camera.Size size : supportedSizes) {
optimalRatio = (double) size.width / size.height;
if(Math.abs(optimalRatio - targetRatio) < acceptableRatioMargin) {
if(Math.abs(height - size.height) < minDiff) {
minDiff = Math.abs(height - size.height);
optimalSize = size;
}
}
}
if(optimalSize == null) {
for(Camera.Size size : supportedSizes) {
if(Math.abs(height - size.height) <= minDiff) {
minDiff = Math.abs(height - size.height);
optimalSize = size;
}
}
}
return optimalSize;
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
The below images is resulting from the values:
Specified resolution from measureSpecWidth/Height = `984x1335`
Returned from getOptimalSize() = `1600x1200`.
Because provided supportedPreviewSizes
are for landscape not portrait.
Here's the result: