2013-06-15 3 views
15

Ich versuche, Kameravorschauen im Hochformat richtig zu arbeiten, wo die Aktivität selbst die Ausrichtung normal ändern kann (d. H. Nicht auf Querformat gesperrt).Kamera setDisplayOrientation() im Hochformat-Modus bricht das Seitenverhältnis

Die Verwendung von setDisplayOrientation() bricht das Verhalten der Vorschau jedoch ernsthaft.

Dies kann von Googles eigenen ApiDemos demonstriert werden. Das erste Bild basiert auf der CameraPreview aus der android-17 Ausgabe des ApiDemos Beispielprojekts, wo die einzige Änderung, die ich vorgenommen habe, war, android:orientation="landscape" aus dem Eintrag dieser Aktivität im Manifest zu entfernen. Das folgende Bild ist ein Screenshot von dem, was erscheint auf dem Display eines Nexus 4, Android 4.2, mit der Kamera auf ein 8,5" Quadrat Papier zeigte:

Nexus 4 ApiDemos Preview, Portrait Mode

Was nicht offensichtlich aus dieser Vorschau ist dass es um 90 Grad gedreht ist. Die Socken, die Sie ganz rechts im Bild sehen, befanden sich tatsächlich unterhalb des Quadrats.

Die Lösung hierfür ist, zumindest im Querformat, setDisplayOrientation(). Wenn Sie die Preview innere Klasse von CameraPreview ändern, um mCamera.setDisplayOrientation(90); zu haben, erhalten Sie diese:

Nexus 4 ApiDemos Preview, Portrait Mode, with setDisplayOrientation(90)

Bemerkenswerterweise ist das Quadrat nicht mehr quadratisch.

Dies verwendet die gleiche SurfaceView Größe wie zuvor. Ich habe dieses Szenario mit einem anderen Code außerhalb von ApiDemos reproduziert, und ich bekomme das gleiche Verhalten mit TextureView in diesem Code.

Ich dachte kurz, dass das Problem sein könnte, dass getSupportedPreviewSizes() verschiedene Werte basierend auf setDisplayOrientation() zurückgeben kann, aber ein schneller Test schlägt vor, dass dies nicht der Fall ist.

Jeder hat eine Idee, wie man setDisplayOrientation() im Hochformat arbeiten kann, ohne das Seitenverhältnis der Bilder, die über die Vorschau geschoben werden, zu zerstören?

Danke!

+0

finde ich http://stackoverflow.com/questions/6710573/camera-display-doesnt-maintain-aspect-ratio -seems-skewed sag mir, wenn es so ist, werde ich eine vollständige Antwort schreiben – idan

+0

@idan: Sorry, aber es gibt keine Aktionsleiste auf "CameraPreview" in den "ApiDemos". Es verwendet bereits 'Window.FEATURE_NO_TITLE'. Trotzdem danke! – CommonsWare

+1

Ich habe vor kurzem selbst viel daran gearbeitet. Eine Sache, die Sie im Auge behalten sollten (und vielleicht sind Sie es bereits), ist, dass die Vorschaugrößen beim Drehen auf Hochformat nicht die Abmessungen vertauschen. Wenn die Standardausrichtung der Kamera auf Querformat eingestellt ist, entspricht die Breite der Breite des Telefons in Querformat, unabhängig davon, ob Sie tatsächlich * in * Querformat sind. Daher müssen Sie möglicherweise die Breite/Höhe ändern, wenn Sie nach dem suchen korrekte Vorschaugröße – kcoppock

Antwort

15

OK, ich denke, dass ich das herausgefunden habe. Es gab ein paar Stücke zum sprichwörtlichen Puzzle und ich danke @kcoppock für die Einsicht, die mir geholfen hat, etwas davon zu lösen.

Zuerst müssen Sie die Ausrichtung berücksichtigen, wenn Sie bestimmen, welche Vorschaugröße ausgewählt werden soll. Zum Beispiel ist die getOptimalPreviewSize() aus der CameraPreview von ApiDemos Orientierungslosigkeit, einfach weil ihre Version der App die Ausrichtung auf Landschaft gesperrt hat. Wenn Sie die Ausrichtung frei lassen möchten, müssen Sie das Zielseitenverhältnis umkehren, damit es übereinstimmt. Also, wo getOptimalPreviewSize() hat:

double targetRatio=(double)width/height; 

Sie auch benötigen:

if (displayOrientation == 90 || displayOrientation == 270) { 
    targetRatio=(double)height/width; 
} 

wo displayOrientation ist ein Wert von 0 bis 360, dass ich von etwa 100 Zeilen einig ernsthaft hässlich Code bin Bestimmung, Deshalb verpacke ich all das in eine wiederverwendbare Komponente, die ich in Kürze veröffentlichen werde.Dieser Code basiert auf the setDisplayOrientation() JavaDocs und this StackOverflow answer.

(BTW, wenn Sie diese nach dem 1. Juli 2013 lesen, und Sie nicht über einen Link in dieser Antwort auf diese Komponente sehen, schreiben Sie einen Kommentar, mich zu erinnern, wie ich vergessen)

Zweitens Sie Sie müssen diese Anzeigeausrichtung berücksichtigen, wenn Sie das Seitenverhältnis des von Ihnen verwendeten Bildformats SurfaceView/TextureView steuern. Die CameraPreview Aktivität von ApiDemos hat seine eigene PreviewViewGroup, die das Seitenverhältnis behandelt, und es müssen Sie das Seitenverhältnis für den Einsatz im Hochformat umgekehrt:

if (displayOrientation == 90 
     || displayOrientation == 270) { 
     previewWidth=mPreviewSize.height; 
     previewHeight=mPreviewSize.width; 
    } 
    else { 
     previewWidth=mPreviewSize.width; 
     previewHeight=mPreviewSize.height; 
    } 

wo displayOrientation ist, dass gleiche Wert (90 und 270 sein Porträt und Reverse-Portrait, und beachten Sie, dass ich nicht versucht habe, Reverse-Portrait oder Reverse-Landscape zu arbeiten, so dass mehr Feinabstimmung erforderlich ist).

Drittens - und eine, die ich ärgerlich finde - müssen Sie die Vorschau vor dem Aufruf setPictureSize() auf dem Camera.Parameters starten. Ansonsten ist es so, als ob das Bildseitenverhältnis des Bildes auf die Vorschaubilder angewendet wird, was die Dinge versaut.

So habe ich Code zu haben, ähnelt die folgenden:

Camera.Parameters parameters=camera.getParameters(); 
Camera.Size pictureSize=getHost().getPictureSize(parameters); 

parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); 
parameters.setPictureSize(pictureSize.width, pictureSize.height); 
parameters.setPictureFormat(ImageFormat.JPEG); 

camera.setParameters(parameters); 
camera.startPreview(); 

und das ist falsch. Was Sie wirklich brauchen, ist:

Camera.Parameters parameters=camera.getParameters(); 
Camera.Size pictureSize=getHost().getPictureSize(parameters); 

parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); 

camera.setParameters(parameters); 
camera.startPreview(); 

parameters=camera.getParameters(); 
parameters.setPictureSize(pictureSize.width, pictureSize.height); 
parameters.setPictureFormat(ImageFormat.JPEG); 
camera.setParameters(parameters); 

Ohne diese Änderung würde ich ein gestrecktes Seitenverhältnis erhalten, auch in der Landschaft. Dies war kein Problem für CameraPreview von ApiDemos, da sie keine Bilder machten, und so riefen sie nie setPictureSize().

Aber bis jetzt, auf einem Nexus 4, habe ich jetzt quadratische Seitenverhältnisse für Hoch-und Querformat, mit einer schwebenden Ausrichtung (d. H. Nicht auf Querformat gesperrt).

Ich werde versuchen, sich daran zu erinnern, diese Frage zu ändern, wenn ich in andere Hacks von Angehörigen anderer Geräte erforderlich ist, plus meine CameraView/CameraFragment Komponenten zu verbinden, wenn sie freigegeben werden.


UPDATE # 1

Nun, natürlich, kann es nicht möglicherweise durch fast diese einfach. :-)

Die Lösung für das Problem, wo setPictureSize() verschraubt das Seitenverhältnis funktioniert ... bis Sie ein Bild machen. Zu diesem Zeitpunkt wechselt die Vorschau in das falsche Seitenverhältnis.

Eine Möglichkeit wäre, Bilder auf Bilder mit demselben (oder sehr nahen) Seitenverhältnis zur Vorschau zu beschränken, so dass der Schluckauf nicht existiert. Ich mag diese Antwort nicht, da der Benutzer in der Lage sein sollte, die Bildgröße zu erreichen, die die Kamera bietet.

Sie den Umfang des Schadens begrenzen durch:

  • Nur die Bildgröße zu aktualisieren, usw.die Camera.Parameters kurz vor dem Bild
  • Zurückkehren zu der Pre-Bildaufnahme Camera.Parameters nach der Einnahme der Bild

Dieses noch stellt das falsche Seitenverhältnis während der Momente, während das Bild aufgenommen wird, nehmen. Die langfristige Lösung dafür - denke ich - besteht darin, die Kameravorschau während der Aufnahme vorübergehend durch ein Standbild (das letzte Vorschaubild) zu ersetzen. Ich werde es irgendwann versuchen.


UPDATE # 2: v0.0.1 von my CWAC-Camera library ist jetzt verfügbar, die oben genannte Code enthält.

+1

Wow, ja entweder wir missverstehen sie, oder die Kamera APIs sind wirklich, wirklich schlecht. Die Tatsache, dass die Dokumentation für ['setDisplayOrientation()'] (http://developer.android.com/reference/android/hardware/Camera.html) einen Code enthält, um den richtigen Wert zu ermitteln (warum nicht zum aktuellen Android hinzufügen) Quelle, anstatt es in einem Javadoc zu lassen?) bestätigt irgendwie, dass es ein Durcheinander ist. Danke für die Information. :) – kcoppock

+0

Hallo. Gute Arbeit! Ich frage mich, ob Ihre App auch mein Problem lösen kann: http://stackoverflow.com/questions/17348614/make-a-surfaceview-larger-than-the-screen-fitting-a-camera-preview-to-a- Oberfläche/. Ich kann das jetzt nicht mit deinem Code testen :( – Paul

+1

@Paul: Ja, ich habe deine Frage dort gesehen, und ich habe keine Ahnung, sorry. – CommonsWare

1

Ok Jungs, Ich war wirklich verrückt die letzten zwei Wochen über diese f * * * Problem. so in Ordnung, einige Dinge zu wissen:

  • Sie manchmal nur bekommen schwarze Ränder, wenn ActionBar oder Soft-Tasten sichtbar sind, nur möglich bei einigen Geräten, mein Rat: * do not care * oder Sie erhalten verrückt, oder Vorschauen geschnitten, die beide nicht gut ...

Änderungen von @CommonsWare einfach perfekt sind, kann aber nicht direkt an die CameraPreview von API Ebene 8 Proben (Link unten angewendet werden), also hier ist th e diff:

für im Portrait richtigen Seitenverhältnis zu tun:
Methoden die gleichen sind, gelten nur

public void setCamera(Camera camera, boolean portrait) { 
    mCamera = camera; 
    this.portrait = portrait; 
    if (mCamera != null) { 
     mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); 
     requestLayout(); 
    } 
} 

public void switchCamera(Camera camera, boolean portrait) throws IOException { 
    setCamera(camera, portrait); 
    camera.setPreviewDisplay(mHolder);  
    Camera.Parameters parameters = camera.getParameters(); 
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); 
    requestLayout(); 

    camera.setParameters(parameters); 
} 

in diff:

@Override 
protected void onLayout(boolean changed, int l, int t, int r, int b) {…} 

Änderung:

int previewWidth = width; 
int previewHeight = height; 
if (mPreviewSize != null) { 
     previewWidth = mPreviewSize.width; 
     previewHeight = mPreviewSize.height; 
} 

zu:

if (portrait && mPreviewSize != null) { 
     previewWidth = mPreviewSize.height; 
     previewHeight = mPreviewSize.width; 
} 
else if (mPreviewSize != null){ 
     previewWidth = mPreviewSize.width; 
     previewHeight = mPreviewSize.height; 
} 

in:

private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {…} 

Änderung:

double targetRatio = (double) w/h; 

zu:

double targetRatio = 1; 
if (portrait) { 
    targetRatio= (double) w/h; 
} 

endlich beginnen Kamera für Portrait-Modus mit so etwas wie:

try { 
    mCamera = openFrontFacingCameraGingerbread(); 
    if (portraitMode) { 
     mCamera.setDisplayOrientation(90); 
    } 
    preview.setCamera(mCamera, portraitMode); 
} catch (Exception e) { 
    e.printStackTrace(); 
    handleCameraUnavailable(); 
} 

WUUHUUUU! Für mich endlich noch Probleme in der Vorschau, und keine Probleme beim Speichern von Bildern. Da mein Aktivitätsort gesperrt ist, erhalte ich die Anzeigeausrichtung über OrientationChangedListener zum Speichern von Bildern mit der richtigen Ausrichtung.

mit so etwas wie:
http://www.androidzeitgeist.com/2013/01/fixing-rotation-camera-picture.html

Aber sie richtig angesehen und gespeichert werden.

Ich hoffe, dass ich viele Menschen dabei helfen kann, gibt es den Original-Code von API Ebene 8:
https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java , wo die Änderungen angewandt wurden.

-1

Bitte ändern Sie die Ausrichtung der Kamera auf den Code, und Sie sollten setDisplayorientation() nicht getDisplayOrientation()

if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) 
{ 
    camera.setDisplayOrientation(0); 

}