OK, ich denke, es gibt keine ausreichende Antwort auf das allgemeine Problem mit der Dehnung der Kameravorschau. Zumindest habe ich keinen gefunden. Meine App litt auch unter diesem Stretching-Syndrom und es dauerte eine Weile, bis ich eine Lösung aus allen Benutzerantworten auf diesem Portal und im Internet gefunden hatte.
Ich habe die Lösung von @ Hesam ausprobiert, aber sie hat nicht funktioniert und meine Kamera-Vorschau stark verzerrt.
Zuerst zeige ich den Code meiner Lösung (die wichtigen Teile des Codes) und erkläre dann, warum ich diese Schritte unternommen habe. Es gibt Raum für Leistungsänderungen.
Hauptaktivität XML-Layout:
<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>
Kameravorschau:
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;
}
}
Hauptaktivität:
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;
}
}
Mein Kommentar:
Der Sinn all dessen ist, dass Sie, obwohl Sie die optimale Kameragröße in berechnen getOptimalPreviewSize()
, nur das nächstgelegene Verhältnis auswählen, das zu Ihrem Bildschirm passt. Wenn das Verhältnis nicht genau gleich ist, wird die Vorschau gedehnt.
Warum wird es sich dehnen? Weil Ihre FrameLayout Kameravorschau wird in gesetzt layout.xml zu match_parent in Breite und Höhe. Aus diesem Grund wird die Vorschau auf den Vollbildmodus ausgedehnt.
Sie müssen lediglich die Breite und Höhe des Kamera-Vorschau-Layouts so einstellen , dass sie dem gewählten Kameragrößenverhältnis entsprechen , damit die Vorschau ihr Seitenverhältnis beibehält und nicht verzerrt.
Ich habe versucht, mit der CameraPreview
Klasse alle Berechnungen und Layoutänderungen vorzunehmen, konnte es aber nicht herausfinden. Ich habe versucht, diese Lösung anzuwenden , SurfaceView
erkenne aber nicht getChildCount ()
oder getChildAt (int index)
. Ich denke, ich habe es irgendwann mit einem Verweis auf maLayoutPreview
zum Laufen gebracht, aber es hat sich schlecht benommen und das eingestellte Verhältnis auf meine gesamte App angewendet, und das tat es, nachdem das erste Bild aufgenommen wurde. Also ließ ich es los und verschob die Layoutänderungen auf die MainActivity
.
In CameraPreview
ich geändert prSupportedPreviewSizes
und getOptimalPreviewSize()
zu öffentlich , damit ich es in nutzen MainActivity
. Dann brauchte ich die Anzeigeabmessungen (abzüglich der Navigations- / Statusleiste, falls vorhanden) und wählte die optimale Kameragröße . Ich habe versucht, die RelativeLayout-Größe (oder FrameLayout-Größe) anstelle der Anzeigegröße zu ermitteln, aber es wurde ein Wert von Null zurückgegeben. Diese Lösung hat bei mir nicht funktioniert. Das Layout hat seinen Wert nach onWindowFocusChanged
(im Protokoll überprüft).
Ich habe also meine Methoden zur Berechnung der Layoutabmessungen, um sie an das Seitenverhältnis der ausgewählten Kameragröße anzupassen. Jetzt müssen Sie nur noch das LayoutParams
Layout Ihrer Kameravorschau festlegen . Ändern Sie die Breite, Höhe und zentrieren Sie sie im übergeordneten Element.
Es gibt zwei Möglichkeiten, die Vorschaudimensionen zu berechnen. Entweder möchten Sie, dass der Bildschirm mit schwarzen Balken (wenn windowBackground auf null gesetzt ist) an den Seiten oder oben / unten versehen wird. Oder Sie möchten, dass die Vorschau auf Vollbild vergrößert wird . Ich habe einen Kommentar mit weiteren Informationen in hinterlassen calcCamPrevDimensions()
.