best - cámara androide vista previa relación de aspecto incorrecta
camera android (5)
He creado una aplicación de cámara basada en tutorial. La clase de vista previa que uso es de api-Demos "CameraPreview". Agregué una modificación desde here (la vista previa siempre se giró 90 °). Así es como configuro el tamaño de la vista previa:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_0) {
parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
mCamera.setDisplayOrientation(90);
}
if (display.getRotation() == Surface.ROTATION_90) {
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
}
if (display.getRotation() == Surface.ROTATION_180) {
parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);
}
if (display.getRotation() == Surface.ROTATION_270) {
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setDisplayOrientation(180);
}
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.startPreview();
}
Pero la vista previa se muestra con una relación de aspecto incorrecta. ¿Es por el código anterior o probablemente por el diseño que uso ?:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button_capture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="@string/capture" />
<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="100dp"
android:layout_height="match_parent"/>
Entonces, ¿cómo obtener la relación de aspecto correcta? Gracias por adelantado.
PD: leí la respuesta de: La vista previa de la cámara de Android parece extraña Pero esto no me funciona.
El problema está realmente en la forma en que usted diseña las cosas. hay sobrescrito onLayout
en la clase Preview
. La idea de su trabajo es establecer el tamaño de SurfaceView
acuerdo con el Size
óptimo encontrado. pero no tiene en cuenta la rotación, por lo que debe hacerlo usted mismo:
if (mPreviewSize != null) {
previewWidth = mPreviewSize.height;
previewHeight = mPreviewSize.width;
}
en lugar de
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
El truco es intercambiar el ancho y la altura, lo que se hace debido a la rotación de 90 grados lograda por
mCamera.setDisplayOrientation(90);
También puede considerar la opción de modificar el tamaño de la vista previa del niño según la orientación que establezca en
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
//...
}
(en el código proporcionado por mí, siempre es para una rotación de 90 grados, para 180 no tiene que hacer nada y cuando no establece ninguna rotación, no es necesario cambiar el ancho y la altura)
Otra cosa que vale la pena mencionar: cuando calcule getOptimalPreviewSize
para el caso en el que tenga rotación e intercambie el ancho y la altura del niño, también debe pasar el ancho y la altura del padre ( Preview
) intercambiado en onMeasure
:
if (mSupportedPreviewSizes != null) {
//noinspection SuspiciousNameCombination
final int previewWidth = height;
//noinspection SuspiciousNameCombination
final int previewHeight = width;
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, previewWidth, previewHeight);
}
El siguiente código modifica el ancho / alto del contenedor de vista previa de la cámara para que coincida con la relación de aspecto de la vista previa de la cámara.
Camera.Size size = camera.getParameters().getPreviewSize();
//landscape
float ratio = (float)size.width/size.height;
//portrait
//float ratio = (float)size.height/size.width;
preview = (FrameLayout) findViewById(R.id.camera_preview);
int new_width=0, new_height=0;
if(preview.getWidth()/preview.getHeight()<ratio){
new_width = Math.round(preview.getHeight()*ratio);
new_height = cameraPreview.getHeight();
}else{
new_width = preview.getWidth();
new_height = Math.round(preview.getWidth()/ratio);
}
preview.setLayoutParams(new FrameLayout.LayoutParams(new_width, new_height));
Intente cambiar los tamaños de vista previa agregando esta función:
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
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;
// Find size
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;
}
Y la configuración de los tamaños de estos valores optimizados:
List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
Camera.Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
Espero que esto funcione :)
Atentamente
Henric
La respuesta de Henric no me funcionó, así que he creado otro método que determina el tamaño de vista previa óptimo para cualquier cámara dado el ancho y la altura actuales de la vista de destino y también la orientación de la actividad:
public static Size getOptimalPreviewSize(List<Camera.Size> cameraPreviewSizes, int targetWidth, int targetHeight, boolean isActivityPortrait) {
if (CommonUtils.isEmpty(cameraPreviewSizes)) {
return null;
}
int optimalHeight = Integer.MIN_VALUE;
int optimalWidth = Integer.MIN_VALUE;
for (Camera.Size cameraPreviewSize : cameraPreviewSizes) {
boolean isCameraPreviewHeightBigger = cameraPreviewSize.height > cameraPreviewSize.width;
int actualCameraWidth = cameraPreviewSize.width;
int actualCameraHeight = cameraPreviewSize.height;
if (isActivityPortrait) {
if (!isCameraPreviewHeightBigger) {
int temp = cameraPreviewSize.width;
actualCameraWidth = cameraPreviewSize.height;
actualCameraHeight = temp;
}
} else {
if (isCameraPreviewHeightBigger) {
int temp = cameraPreviewSize.width;
actualCameraWidth = cameraPreviewSize.height;
actualCameraHeight = temp;
}
}
if (actualCameraWidth > targetWidth || actualCameraHeight > targetHeight) {
// finds only smaller preview sizes than target size
continue;
}
if (actualCameraWidth > optimalWidth && actualCameraHeight > optimalHeight) {
// finds only better sizes
optimalWidth = actualCameraWidth;
optimalHeight = actualCameraHeight;
}
}
Size optimalSize = null;
if (optimalHeight != Integer.MIN_VALUE && optimalWidth != Integer.MIN_VALUE) {
optimalSize = new Size(optimalWidth, optimalHeight);
}
return optimalSize;
}
Esto utiliza un objeto Tamaño personalizado, porque el Size de Android está disponible después de API 21.
public class Size {
private int width;
private int height;
public Size(int width, int height) {
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
}
Puede determinar el ancho y la altura de una vista escuchando sus cambios de diseño global y luego puede establecer las nuevas dimensiones. Esto también muestra cómo determinar la orientación de la actividad mediante programación:
cameraPreviewLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// gets called after layout has been done but before display.
cameraPreviewLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
boolean isActivityPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
Size optimalCameraPreviewSize = CustomUtils.getOptimalPreviewSize(cameraPreview.getCameraSizes(), cameraPreviewLayout.getWidth(), cameraPreviewLayout.getHeight(), isActivityPortrait);
if (optimalCameraPreviewSize != null) {
LinearLayout.LayoutParams cameraPreviewLayoutParams = new LinearLayout.LayoutParams(optimalCameraPreviewSize.getWidth(), optimalCameraPreviewSize.getHeight());
cameraPreviewLayout.setLayoutParams(cameraPreviewLayoutParams);
}
}
});
Usando la solución anterior, usando el método privado Size getOptimalPreviewSize (tamaños de lista, int w, int h). Funcionó bien! Estaba teniendo problemas con la relación de aspecto en la orientación de retrato: Aquí está mi solución usando. Mezclando con la documentación de android:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int 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
Camera.Parameters params = mCamera.getParameters();
params.set("orientation", "portrait");
Size optimalSize=getOptimalPreviewSize(params.getSupportedPreviewSizes(), getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
params.setPreviewSize(optimalSize.width, optimalSize.height);
mCamera.setParameters(params);
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}