问题
I have an camera app which calculates the screen size and display the preview. My Preview class works fine for phone which as 720*1080 and 1080*1920 screen resolution when app starts initially. If I go to next activity and come back to camera activity. Preview works only for 720*1080 screen, but not for 1080*1920. It fails in SurfaceChanged method in setting the parameters.
// My CameraPreview Class
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
protected List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
private Camera mCamera;
public CameraPreview(Activity activity, Camera camera) {
super(activity);
mCamera = camera;
SurfaceHolder mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mHolder.setKeepScreenOn(true);
setCamera(mCamera);
}
public void setCamera(Camera camera) {
mCamera = camera;
if (mCamera != null) {
mCamera.setDisplayOrientation(getDisplayOrientation());
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
requestLayout();
Camera.Parameters parameters = mCamera.getParameters();
List<String> focusModes = parameters.getSupportedFocusModes();
if(focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)){
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(parameters);
}
Setting the exposure Compensation in a unique way.
parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
int exposureCompensation = parameters.getMinExposureCompensation();
int exposureCompensation2 = parameters.getMaxExposureCompensation();
int exposureCompensationFinal = (exposureCompensation + exposureCompensation2)/2;
parameters.setExposureCompensation(exposureCompensationFinal);
mCamera.setParameters(parameters);
}
}
public int getDisplayOrientation(){
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, info);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int rotation = display.getRotation();
int degree = 0;
switch(rotation){
case Surface.ROTATION_0: degree = 0; break;
case Surface.ROTATION_90: degree = 90; break;
case Surface.ROTATION_180: degree = 180; break;
case Surface.ROTATION_270: degree = 270; break;
}
int result;
if(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
result = (info.orientation + degree) % 360;
result = (360 - result) % 360;
}else{
result = (info.orientation - degree + 360) % 360;
}
return result;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException e) {
Log.d("DG_DEBUG", "Error setting camera preview in surfaceCreated: " + e.getMessage());
}
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height) {
Camera.Size optimalSize = null;
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) height / width;
double minDiff = Double.MAX_VALUE;
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 - height) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - height);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - height) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - height);
}
}
}
if (optimalSize == null) {
Log.d("getOptimalPreviewSize", "Optimal size not found");
} else {
Log.d("getOptimalPreviewSize",
String.format(
"getOptimalPreviewSize result: width = %d, height = %d for input width = %d, height = %d",
optimalSize.width, optimalSize.height, width,
height));
}
return optimalSize;
}
private Camera.Size getOptimalPictureSize() {
if (mCamera == null) {
return null;
}
List<Camera.Size> cameraSizes = mCamera.getParameters().getSupportedPictureSizes();
Camera.Size optimalSize = mCamera.new Size(0, 0);
double previewRatio = (double) mPreviewSize.width / mPreviewSize.height;
for (Camera.Size size : cameraSizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - previewRatio) > 0.01f)
continue;
if (size.height > optimalSize.height) {
optimalSize = size;
}
}
if (optimalSize.height == 0) {
for (Camera.Size size : cameraSizes) {
if (size.height > optimalSize.height) {
optimalSize = size;
}
}
}
return optimalSize;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mCamera != null) {
Camera.Size optimalSize = getOptimalPictureSize();
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
requestLayout();
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPictureSize(mPreviewSize.width, mPreviewSize.height);
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
// It fails at this step. It says Failed to set the parameters.I get this error if i'm using the Nexus 5 for testing app. But it works perfectly for 720*1080 resolution.
mCamera.setParameters(parameters);
mCamera.startPreview();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
}
} catch (Exception e) {
Log.d("SurfaceDestroyed", " Error handling surface destroy method");
}
}
}
Log Details
02-03 19:20:09.328 18839-18839/ com.android.example E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.android.example, PID: 18839
java.lang.RuntimeException: setParameters failed
at android.hardware.Camera.native_setParameters(Native Method)
at android.hardware.Camera.setParameters(Camera.java:1876)
at com.android.example.CameraPreview.surfaceChanged(CameraPreview.java:267)
at android.view.SurfaceView.updateWindow(SurfaceView.java:590)
at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:238)
at android.view.View.dispatchWindowVisibilityChanged(View.java:8697)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1269)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1269)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1269)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1269)
at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1269)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1311)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
at android.view.Choreographer.doCallbacks(Choreographer.java:580)
at android.view.Choreographer.doFrame(Choreographer.java:550)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
来源:https://stackoverflow.com/questions/28305367/make-camera-preview-better-for-all-the-phones