De acuerdo, creo que no hay una respuesta suficiente para el problema general de estiramiento de la vista previa de la cámara. O al menos no encontré uno. Mi aplicación también sufrió este síndrome de estiramiento y me tomó un tiempo encontrar una solución de todas las respuestas de los usuarios en este portal e Internet.
Intenté la solución de @ Hesam pero no funcionó y dejé la vista previa de mi cámara muy distorsionada.
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.
Diseño xml de actividad principal:
<RelativeLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout
android:id="@+id/camera_preview"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
Vista previa de la cámara:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder prHolder;
private Camera prCamera;
public List<Camera.Size> 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<String> 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<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;
}
}
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 punto de todo esto es, que si bien se calcula el tamaño de la cámara óptima en getOptimalPreviewSize()
sólo recoger la relación más cercana para adaptarse a su pantalla. Entonces, a menos que la relación sea exactamente la misma, la vista previa se extenderá.
¿Por qué se estirará? Porque la vista previa de su cámara FrameLayout está configurada en layout.xml para emparejar_parent en ancho y alto. Por eso, la vista previa se extenderá a pantalla completa.
Lo que debe hacerse es establecer el ancho y la altura del diseño de la vista previa de la cámara para que coincida con la relació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 CameraPreview
clase para hacer todos los cálculos y cambios de diseño, pero no pude resolverlo. Traté de aplicar esta solución , pero SurfaceView
no reconoce getChildCount ()
o getChildAt (int index)
. Creo que finalmente lo hice funcionar con una referencia a maLayoutPreview
, pero se estaba comportando mal y apliqué la proporción establecida a toda mi aplicación y lo hizo después de tomar la primera fotografía. Así que lo dejé pasar y moví las modificaciones de diseño al MainActivity
.
En CameraPreview
Cambié prSupportedPreviewSizes
y getOptimalPreviewSize()
en público para poder usarlo MainActivity
. Luego necesitaba las dimensiones de la pantalla (menos la barra de navegación / estado si hay una) y elegí 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 estaba devolviendo un valor cero. Esta solución no me funcionó. El diseño obtuvo su valor después onWindowFocusChanged
(marcado en el registro).
Así que tengo mis métodos para calcular las dimensiones del diseño para que coincidan con la relación de aspecto del tamaño de cámara elegido. Ahora solo necesita configurar LayoutParams
el diseño de vista previa de su cámara. Cambie el ancho, la altura y céntrelo en padre.
Hay dos opciones para calcular las dimensiones de la vista previa. O desea que se ajuste a la pantalla con barras negras (si windowBackground está configurado como nulo) en los lados o arriba / abajo. O desea que la vista previa se amplíe a pantalla completa . Dejé un comentario con más información en calcCamPrevDimensions()
.