OK, donc je pense qu'il n'y a pas de réponse suffisante pour le problème général d'étirement de l'aperçu de la caméra. Ou du moins je n'en ai pas trouvé. Mon application a également souffert de ce syndrome d'étirement et il m'a fallu un certain temps pour trouver une solution à partir de toutes les réponses des utilisateurs sur ce portail et sur Internet.
J'ai essayé la solution de @ Hesam mais cela n'a pas fonctionné et j'ai laissé l'aperçu de ma caméra très déformé.
Je montre d'abord le code de ma solution (les parties importantes du code) puis j'explique pourquoi j'ai pris ces mesures. Il y a place pour des modifications de performance.
Mise en page XML de l'activité principale:
<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>
Aperçu de la caméra:
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;
}
}
Activité principale:
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;
}
}
Mon commentaire:
Le point de tout cela est que, même si vous calculez la taille de la caméra optimale en getOptimalPreviewSize()
vous ne choisissez le rapport le plus proche de correspondre à votre écran. Ainsi, à moins que le rapport ne soit exactement le même, l'aperçu s'étirera.
Pourquoi cela va-t-il s'étirer? Parce que l'aperçu de votre caméra FrameLayout est défini dans layout.xml sur match_parent en largeur et en hauteur. C'est pourquoi l'aperçu s'étendra en plein écran.
Ce qu'il faut faire est de définir la largeur et la hauteur de la mise en page de l'aperçu de la caméra pour qu'elles correspondent au rapport de taille de caméra choisi , afin que l'aperçu conserve son rapport hauteur / largeur et ne se déforme pas.
J'ai essayé d'utiliser la CameraPreview
classe pour faire tous les calculs et les changements de mise en page, mais je ne pouvais pas le comprendre. J'ai essayé d'appliquer cette solution , mais je SurfaceView
ne reconnais pas getChildCount ()
ou getChildAt (int index)
. Je pense que je l'ai finalement fait fonctionner avec une référence à maLayoutPreview
, mais il se comportait mal et a appliqué le rapport défini à l'ensemble de mon application et cela a été fait après la première photo. J'ai donc laissé tomber et déplacé les modifications de mise en page vers le MainActivity
.
Dans CameraPreview
j'ai changé prSupportedPreviewSizes
et getOptimalPreviewSize()
en public afin que je puisse l'utiliser dans MainActivity
. Ensuite, j'avais besoin des dimensions de l' écran (moins la barre de navigation / d'état s'il y en a une) et choisi la taille optimale de la caméra . J'ai essayé d'obtenir la taille RelativeLayout (ou FrameLayout) au lieu de la taille d'affichage, mais cela retournait une valeur nulle. Cette solution n'a pas fonctionné pour moi. La mise en page a sa valeur après onWindowFocusChanged
(vérifiée dans le journal).
J'ai donc mes méthodes pour calculer les dimensions de la mise en page pour qu'elles correspondent au rapport hauteur / largeur de la taille de caméra choisie. Il vous suffit maintenant de définir LayoutParams
la disposition d'aperçu de votre appareil photo. Modifiez la largeur, la hauteur et centrez-la dans le parent.
Il existe deux choix pour calculer les dimensions de l'aperçu. Soit vous voulez qu'il s'adapte à l'écran avec des barres noires (si windowBackground est défini sur null) sur les côtés ou en haut / en bas. Ou vous voulez que l'aperçu soit agrandi en plein écran . J'ai laissé un commentaire avec plus d'informations calcCamPrevDimensions()
.