Vista previa de la cámara de Android estirada

He estado trabajando para hacer que mi cámara personalizada funcione en Android, pero al girar la cámara, la relación de aspecto de la vista de la superficie se arruina.

En mi oncreate para la actividad, establezco el framelayout que contiene la vista de superficie que muestra los parámetros de la cámara.

//FrameLayout that will hold the camera preview FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camerapreview); //Setting camera's preview size to the best preview size Size optimalSize = null; camera = getCameraInstance(); double aspectRatio = 0; if(camera != null){ //Setting the camera's aspect ratio Camera.Parameters parameters = camera.getParameters(); List sizes = parameters.getSupportedPreviewSizes(); optimalSize = CameraPreview.getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels); aspectRatio = (float)optimalSize.width/optimalSize.height; } if(optimalSize!= null){ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio)); previewHolder.setLayoutParams(params); LayoutParams surfaceParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio)); cameraPreview.setLayoutParams(surfaceParams); } cameraPreview.setCamera(camera); //Adding the preview to the holder previewHolder.addView(cameraPreview); 

Luego, en la vista de Superficie, configuro los parámetros de la cámara para que se muestren

 public void setCamera(Camera camera) { if (mCamera == camera) { return; } mCamera = camera; if (mCamera != null) { requestLayout(); try { mCamera.setPreviewDisplay(mHolder); } catch (IOException e) { e.printStackTrace(); } if(mCamera != null){ //Setting the camera's aspect ratio Camera.Parameters parameters = mCamera.getParameters(); List sizes = parameters.getSupportedPreviewSizes(); Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels); parameters.setPreviewSize(optimalSize.width, optimalSize.height); mCamera.setParameters(parameters); } /* Important: Call startPreview() to start updating the preview surface. Preview must be started before you can take a picture. */ mCamera.startPreview(); } } 

enter image description here

Puedes ver que el hombre LEGO se vuelve más alto y flaco cuando se gira el teléfono:

¿Cómo puedo asegurarme de que la relación de aspecto de mi cámara es correcta?

Estoy usando este método -> basado en demostraciones de API para obtener mi tamaño de vista previa:

 private Camera.Size getOptimalPreviewSize(List 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; } 

Como puede ver, debe ingresar el ancho y alto de su pantalla. Este método calculará la proporción de pantalla basada en esos valores y luego de la lista de supportedPreviewSizes elegirá la mejor para usted de entre las disponibles. Obtenga su lista dePreviewSize admitida en el lugar donde el objeto de la cámara no es nulo mediante el uso de

 mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); 

Y luego en onMeasure puedes obtener tu tamaño de vista previa óptimo así:

 @Override 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); } } 

Y luego (en mi código en el método surfaceChanged, como dije, estoy usando la estructura API Demos del código CameraActivity, puedes generarlo en Eclipse):

 Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); mCamera.setParameters(parameters); mCamera.startPreview(); 

Y una sugerencia para ti, porque hice casi la misma aplicación que tú. Una buena práctica para Camera Activity es ocultar StatusBar. Aplicaciones como Instagram lo están haciendo. Reduce el valor de altura de la pantalla y cambia el valor de la relación. Es posible obtener tamaños de vista previa extraños en algunos dispositivos (su SurfaceView se reducirá un poco)


Y para responder a su pregunta, ¿cómo verificar si la proporción de vista previa es la correcta? A continuación, obtenga la altura y el ancho de los parámetros que establece en:

 mCamera.setParameters(parameters); 

la relación establecida es igual a la altura / ancho. Si desea que la cámara se vea bien en su pantalla, la relación de altura / ancho de los parámetros que configure en la cámara debe ser igual a la relación de altura (barra de estado menos) / ancho de su pantalla.

La solución de F1Sher es buena, pero a veces no funciona. Particularmente, cuando su SurfaceView no cubre toda la pantalla. En este caso, debes anular el método de medición (). He copiado mi código aquí para su referencia.

Dado que medí el SurfaceView basado en el ancho, entonces tengo un pequeño espacio blanco en el extremo de mi pantalla que llené por diseño. Puede solucionar este problema si mantiene la altura y aumenta el ancho multiplicándolo a razón. Sin embargo, aplastará levemente la superficie.

 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static final String TAG = "CameraPreview"; private Context mContext; private SurfaceHolder mHolder; private Camera mCamera; private List mSupportedPreviewSizes; private Camera.Size mPreviewSize; public CameraPreview(Context context, Camera camera) { super(context); mContext = context; mCamera = camera; // supported preview sizes mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); for(Camera.Size str: mSupportedPreviewSizes) Log.e(TAG, str.width + "/" + str.height); // 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); } public void surfaceCreated(SurfaceHolder holder) { // empty. surfaceChanged will take care of stuff } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + h); // 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 } // set preview size and make any resize, rotate or reformatting changes here // start preview with new settings try { Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); mCamera.setParameters(parameters); mCamera.setDisplayOrientation(90); mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); if (mSupportedPreviewSizes != null) { mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height); } if (mPreviewSize!=null) { float ratio; if(mPreviewSize.height >= mPreviewSize.width) ratio = (float) mPreviewSize.height / (float) mPreviewSize.width; else ratio = (float) mPreviewSize.width / (float) mPreviewSize.height; // One of these methods should be used, second method squishes preview slightly setMeasuredDimension(width, (int) (width * ratio)); // setMeasuredDimension((int) (width * ratio), height); } } private Camera.Size getOptimalPreviewSize(List 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.height / size.width; 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; } } 

NOTA: MI SOLUCIÓN ES UNA CONTINUACIÓN DE LA SOLUCIÓN DE HESAM: https://stackoverflow.com/a/22758359/1718734

Lo que me dirijo: Hesam dijo que hay un pequeño espacio en blanco que puede aparecer en algunos teléfonos, como este:

NOTA: aunque la relación de aspecto es correcta, la cámara no llena toda la pantalla.

Hesam sugirió una segunda solución, pero eso excluye la vista previa. Y en algunos dispositivos, se distorsiona fuertemente.

Entonces, ¿cómo solucionamos este problema? Es simple … multiplicando las proporciones de aspecto hasta que llena la pantalla. Me di cuenta de que varias aplicaciones populares como Snapchat, WhatsApp, etc. funcionan de la misma manera.

Todo lo que tiene que hacer es agregar esto al método onMeasure:

 float camHeight = (int) (width * ratio); float newCamHeight; float newHeightRatio; if (camHeight < height) { newHeightRatio = (float) height / (float) mPreviewSize.height; newCamHeight = (newHeightRatio * camHeight); Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " " + newCamHeight); setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight); Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - " + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - " + newCamHeight); } else { newCamHeight = camHeight; setMeasuredDimension(width, (int) newCamHeight); Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | A_width - " + (width) + " | A_height - " + newCamHeight); } 

Esto calculará la altura de la pantalla y obtendrá la relación entre la altura de la pantalla y la altura mPreviewSize. Luego multiplica la anchura y la altura de la cámara por la nueva relación de altura y establece la dimensión medida en consecuencia.

enter image description here

Y lo siguiente que sabes es que terminas con esto: D

enter image description here

Esto también funciona bien con la cámara frontal. Creo que esta es la mejor manera de hacerlo. Ahora, lo único que queda para mi aplicación es guardar la vista previa al hacer clic en "Capturar". Pero ya, esto es todo.

De acuerdo, creo que no hay suficiente respuesta para el problema general de estiramiento de la vista previa de la cámara. O al menos no encontré ninguno. Mi aplicación también sufrió este síndrome de estiramiento y me tomó un tiempo armar una solución de todas las respuestas de los usuarios en este portal e Internet.

Intenté la solución @ Hesam, pero no funcionó y mi vista previa de la cámara se distorsionó.

Primero muestro el código de mi solución (las partes importantes del código) y luego explico por qué tomé esos pasos. Hay espacio para modificaciones de rendimiento.

Actividad principal xml diseño:

    

Vista previa de la cámara:

 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder prHolder; private Camera prCamera; public List prSupportedPreviewSizes; private Camera.Size prPreviewSize; @SuppressWarnings("deprecation") public YoCameraPreview(Context context, Camera camera) { super(context); prCamera = camera; prSupportedPreviewSizes = prCamera.getParameters().getSupportedPreviewSizes(); prHolder = getHolder(); prHolder.addCallback(this); prHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { try { prCamera.setPreviewDisplay(holder); prCamera.startPreview(); } catch (IOException e) { Log.d("Yologram", "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (prHolder.getSurface() == null){ return; } try { prCamera.stopPreview(); } catch (Exception e){ } try { Camera.Parameters parameters = prCamera.getParameters(); List focusModes = parameters.getSupportedFocusModes(); if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); } parameters.setPreviewSize(prPreviewSize.width, prPreviewSize.height); prCamera.setParameters(parameters); prCamera.setPreviewDisplay(prHolder); prCamera.startPreview(); } catch (Exception e){ Log.d("Yologram", "Error starting camera preview: " + e.getMessage()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(width, height); if (prSupportedPreviewSizes != null) { prPreviewSize = getOptimalPreviewSize(prSupportedPreviewSizes, width, height); } } public Camera.Size getOptimalPreviewSize(List 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; } } 

Actividad principal:

 public class MainActivity extends Activity { ... @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); maCamera = getCameraInstance(); maLayoutPreview = (FrameLayout) findViewById(R.id.camera_preview); maPreview = new CameraPreview(this, maCamera); Point displayDim = getDisplayWH(); Point layoutPreviewDim = calcCamPrevDimensions(displayDim, maPreview.getOptimalPreviewSize(maPreview.prSupportedPreviewSizes, displayDim.x, displayDim.y)); if (layoutPreviewDim != null) { RelativeLayout.LayoutParams layoutPreviewParams = (RelativeLayout.LayoutParams) maLayoutPreview.getLayoutParams(); layoutPreviewParams.width = layoutPreviewDim.x; layoutPreviewParams.height = layoutPreviewDim.y; layoutPreviewParams.addRule(RelativeLayout.CENTER_IN_PARENT); maLayoutPreview.setLayoutParams(layoutPreviewParams); } maLayoutPreview.addView(maPreview); } @SuppressLint("NewApi") @SuppressWarnings("deprecation") private Point getDisplayWH() { Display display = this.getWindowManager().getDefaultDisplay(); Point displayWH = new Point(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { display.getSize(displayWH); return displayWH; } displayWH.set(display.getWidth(), display.getHeight()); return displayWH; } private Point calcCamPrevDimensions(Point disDim, Camera.Size camDim) { Point displayDim = disDim; Camera.Size cameraDim = camDim; double widthRatio = (double) displayDim.x / cameraDim.width; double heightRatio = (double) displayDim.y / cameraDim.height; // use ">" to zoom preview full screen if (widthRatio < heightRatio) { Point calcDimensions = new Point(); calcDimensions.x = displayDim.x; calcDimensions.y = (displayDim.x * cameraDim.height) / cameraDim.width; return calcDimensions; } // use "<" to zoom preview full screen if (widthRatio > heightRatio) { Point calcDimensions = new Point(); calcDimensions.x = (displayDim.y * cameraDim.width) / cameraDim.height; calcDimensions.y = displayDim.y; return calcDimensions; } return null; } } 

Mi comentario:

El objective de todo esto es que, aunque calcule el tamaño óptimo de la cámara en getOptimalPreviewSize() , solo elija la proporción más cercana que se ajuste a su pantalla. Entonces, a menos que la relación sea exactamente la misma, la vista previa se extenderá.

¿Por qué se estirará? Debido a que su vista previa de la cámara FrameLayout se establece en layout.xml para match_parent en ancho y alto. Por eso, la vista previa se extenderá a pantalla completa.

Lo que se debe hacer es configurar el ancho y el alto de la vista previa de la cámara para que coincida con la proporción de tamaño de la cámara elegida, de modo que la vista previa mantenga su relación de aspecto y no se distorsione.

Traté de usar la clase CameraPreview para hacer todos los cálculos y cambios de diseño, pero no pude resolverlo. Intenté aplicar esta solución , pero SurfaceView no reconoce getChildCount () ni getChildAt (int index) . Creo que finalmente maLayoutPreview con una referencia a maLayoutPreview , pero se estaba maLayoutPreview mal y apliqué la proporción establecida a toda mi aplicación, y así fue después de que se tomó la primera foto. Así que lo dejé pasar y moví las modificaciones de diseño a MainActivity .

En CameraPreview cambié prSupportedPreviewSizes y getOptimalPreviewSize() al público, así que puedo usarlo en MainActivity . Entonces necesité las dimensiones de la pantalla (menos la barra de navegación / estado si hay una) y escogí el tamaño óptimo de la cámara . Intenté obtener el tamaño RelativeLayout (o FrameLayout) en lugar del tamaño de visualización, pero devolvía el valor cero. Esta solución no funcionó para mí. El diseño obtuvo su valor después de onWindowFocusChanged (marcado en el registro).

Así que tengo mis métodos para calcular las dimensiones del diseño para que coincida con la relación de aspecto del tamaño de la cámara elegida. Ahora solo necesita establecer LayoutParams del diseño de vista previa de su cámara. Cambie el ancho, alto y centrelo en el elemento principal.

Hay dos opciones para calcular las dimensiones de vista previa. O desea que se ajuste a la pantalla con barras negras (si windowBackground está establecido en nulo) en los lados o arriba / abajo. O desea que la vista previa se amplíe a pantalla completa . Dejé el comentario con más información en calcCamPrevDimensions() .

Hola, el getOptimalPreview () que está aquí no funcionó para mí, así que quiero compartir mi versión:

 private Camera.Size getOptimalPreviewSize(List sizes, int w, int h) { if (sizes==null) return null; Camera.Size optimalSize = null; double ratio = (double)h/w; double minDiff = Double.MAX_VALUE; double newDiff; for (Camera.Size size : sizes) { newDiff = Math.abs((double)size.width/size.height - ratio); if (newDiff < minDiff) { optimalSize = size; minDiff = newDiff; } } return optimalSize; } 

Solo para hacer que este hilo sea más completo, agrego mi versión de respuesta:

Lo que quería lograr: la vista de la superficie no debería estirarse, y debería cubrir toda la pantalla. Además, solo había un modo horizontal en mi aplicación.

Solución:

La solución es una extensión extremadamente pequeña de la solución de F1sher:

=> El primer paso es integrar la solución de F1sher.

=> Ahora, podría surgir un escenario en la solución F1sher cuando la vista de superficie no cubre toda la pantalla. La solución es hacer que la superficie sea mayor que las dimensiones de la pantalla para que cubra toda la pantalla, para eso:

  size = getOptimalPreviewSize(mCamera.getParameters().getSupportedPreviewSizes(), screenWidth, screenHeight); Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(size.width, size.height); mCamera.setParameters(parameters); double screenRatio = (double) screenHeight / screenWidth; double previewRatio = (double) size.height / size.width; if (previewRatio > screenRatio) /*if preview ratio is greater than screen ratio then we will have to recalculate the surface height while keeping the surface width equal to the screen width*/ { RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(screenWidth, (int) (screenWidth * previewRatio)); params1.addRule(RelativeLayout.CENTER_IN_PARENT); flPreview.setLayoutParams(params1); flPreview.setClipChildren(false); LayoutParams surfaceParams = new LayoutParams(screenWidth, (int) (screenWidth * previewRatio)); surfaceParams.gravity = Gravity.CENTER; mPreview.setLayoutParams(surfaceParams); } else /*if preview ratio is smaller than screen ratio then we will have to recalculate the surface width while keeping the surface height equal to the screen height*/ { RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight); params1.addRule(RelativeLayout.CENTER_IN_PARENT); flPreview.setLayoutParams(params1); flPreview.setClipChildren(false); LayoutParams surfaceParams = new LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight); surfaceParams.gravity = Gravity.CENTER; mPreview.setLayoutParams(surfaceParams); } flPreview.addView(mPreview); /* The TopMost layout used is the RelativeLayout, flPreview is the FrameLayout in which Surface View is added, mPreview is an instance of a class which extends SurfaceView */ 

Intenté toda la solución anterior, pero ninguno de ellos funciona para mí. Finalmente lo resolví yo mismo, y descubrí que en realidad es bastante fácil. hay dos puntos que debes tener cuidado.

 parameters.setPreviewSize(cameraResolution.x, cameraResolution.y); 

este tamaño de vista previa debe ser una de las resoluciones admitidas por la cámara, que se puede obtener de la siguiente manera:

 List rawSupportedSizes = parameters.getSupportedPreviewSizes(); 

generalmente uno de los rawSupportedSize es igual a la resolución del dispositivo.

En segundo lugar, coloque su SurfaceView en un FrameLayout y configure la altura y el ancho del diseño de la superficie en el método surfaceChanged como se indicó anteriormente.

 FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams(); layoutParams.height = cameraResolution.x; layoutParams.width = cameraResolution.y; 

Ok, cosas hechas, espero que esto pueda ayudarte.

Debe establecer cameraView.getLayoutParams (). Height y cameraView.getLayoutParams (). Width de acuerdo con la relación de aspecto que desee.

Descubrí cuál es el problema, es con cambios de orientación . Si cambia la orientación de la cámara a 90 o 270 grados, deberá cambiar el ancho y la altura de los tamaños compatibles y todo estará bien.

También la vista de superficie debe estar en un diseño de marco y tener gravedad central.

Aquí hay un ejemplo de C # (Xamarin):

 public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height) { _camera.StopPreview(); // find best supported preview size var parameters = _camera.GetParameters(); var supportedSizes = parameters.SupportedPreviewSizes; var bestPreviewSize = supportedSizes .Select(x => new { Width = x.Height, Height = x.Width, Original = x }) // HACK swap height and width because of changed orientation to 90 degrees .OrderBy(x => Math.Pow(Math.Abs(x.Width - width), 3) + Math.Pow(Math.Abs(x.Height - height), 2)) .First(); if (height == bestPreviewSize.Height && width == bestPreviewSize.Width) { // start preview if best supported preview size equals current surface view size parameters.SetPreviewSize(bestPreviewSize.Original.Width, bestPreviewSize.Original.Height); _camera.SetParameters(parameters); _camera.StartPreview(); } else { // if not than change surface view size to best supported (SurfaceChanged will be called once again) var layoutParameters = _surfaceView.LayoutParameters; layoutParameters.Width = bestPreviewSize.Width; layoutParameters.Height = bestPreviewSize.Height; _surfaceView.LayoutParameters = layoutParameters; } } 

Preste atención a que los parámetros de la cámara deben establecerse como tamaño original (no intercambiado), y el tamaño de la vista de superficie debe intercambiarse.

Mis requisitos son que la vista previa de la cámara debe ser de pantalla completa y mantener la relación de aspecto. La solución de Hesam y Yoosuf fue genial, pero veo un gran problema de zoom por alguna razón.

La idea es la misma, tener el centro de contenedor de vista previa en el padre y boost el ancho o la altura dependerá de las relaciones de aspecto hasta que pueda cubrir toda la pantalla.

Una cosa a tener en cuenta es que el tamaño de vista previa está en el paisaje porque establecemos la orientación de la pantalla.

camera.setDisplayOrientation (90);

El contenedor al que agregaremos la vista SurfaceView:

  

Agregue la vista previa a su contenedor con el centro en el elemento principal de su actividad.

 this.cameraPreview = new CameraPreview(this, camera); cameraPreviewContainer.removeAllViews(); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); cameraPreviewContainer.addView(cameraPreview, 0, params); 

Dentro de la clase CameraPreview:

 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 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 (holder.getSurface() == null) { // preview surface does not exist return; } stopPreview(); // set preview size and make any resize, rotate or // reformatting changes here try { Camera.Size nativePictureSize = CameraUtils.getNativeCameraPictureSize(camera); Camera.Parameters parameters = camera.getParameters(); parameters.setPreviewSize(optimalSize.width, optimalSize.height); parameters.setPictureSize(nativePictureSize.width, nativePictureSize.height); camera.setParameters(parameters); camera.setDisplayOrientation(90); camera.setPreviewDisplay(holder); camera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec); final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec); if (supportedPreviewSizes != null && optimalSize == null) { optimalSize = CameraUtils.getOptimalSize(supportedPreviewSizes, width, height); Log.i(TAG, "optimal size: " + optimalSize.width + "w, " + optimalSize.height + "h"); } float previewRatio = (float) optimalSize.height / (float) optimalSize.width; // previewRatio is height/width because camera preview size are in landscape. float measuredSizeRatio = (float) width / (float) height; if (previewRatio >= measuredSizeRatio) { measuredHeight = height; measuredWidth = (int) ((float)height * previewRatio); } else { measuredWidth = width; measuredHeight = (int) ((float)width / previewRatio); } Log.i(TAG, "Preview size: " + width + "w, " + height + "h"); Log.i(TAG, "Preview size calculated: " + measuredWidth + "w, " + measuredHeight + "h"); setMeasuredDimension(measuredWidth, measuredHeight); } 

Renuncie a los cálculos y simplemente obtengo el tamaño de la vista donde quiero que se muestre la vista previa de la cámara y establezco el mismo tamaño de vista previa de la cámara (solo volteé anchura / altura debido a la rotación) en mi implementación personalizada de SurfaceView:

 @Override // CameraPreview extends SurfaceView implements SurfaceHolder.Callback { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Display display = ((WindowManager) getContext().getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay(); if (display.getRotation() == Surface.ROTATION_0) { final Camera.Parameters params = camera.getParameters(); // viewParams is from the view where the preview is displayed params.setPreviewSize(viewParams.height, viewParams.width); camera.setDisplayOrientation(90); requestLayout(); camera.setParameters(params); } // I do not enable rotation, so this can otherwise stay as is }