Hinweis: Diese Seite bezieht sich auf das Camera2-Paket. Sofern Ihre App keine speziellen, Low-Level-Funktionen von Camera2 benötigt, empfehlen wir die Verwendung von CameraX. Sowohl CameraX als auch Camera2 unterstützen Android 5.0 (API-Level 21) und höher.
Kameras und Kameravorschauen haben auf Android-Geräten nicht immer dieselbe Ausrichtung.
Eine Kamera befindet sich an einer festen Position auf einem Gerät, unabhängig davon, ob es sich um ein Smartphone, ein Tablet oder einen Computer handelt. Wenn sich die Ausrichtung des Geräts ändert, ändert sich auch die Ausrichtung der Kamera.
Daher gehen Kamera-Apps im Allgemeinen von einer festen Beziehung zwischen der Ausrichtung des Geräts und dem Seitenverhältnis der Kameravorschau aus. Wenn sich ein Smartphone im Hochformat befindet, wird davon ausgegangen, dass die Kameravorschau höher als breit ist. Wenn das Smartphone (und die Kamera) ins Querformat gedreht werden, sollte die Kameravorschau breiter als hoch sein.
Diese Annahmen werden jedoch durch neue Formfaktoren wie faltbare Geräte und Anzeigemodi wie Multi-Window und Multi-Display infrage gestellt. Bei faltbaren Geräten ändern sich die Displaygröße und das Seitenverhältnis, ohne dass sich die Ausrichtung ändert. Im Mehrfenstermodus werden Kamera-Apps auf einen Teil des Bildschirms beschränkt und die Kameravorschau wird unabhängig von der Geräteausrichtung skaliert. Der Multidisplay-Modus ermöglicht die Verwendung von sekundären Displays, die möglicherweise nicht dieselbe Ausrichtung wie das primäre Display haben.
Kameraausrichtung
In der Android-Kompatibilitätsdefinition ist festgelegt, dass ein Bildsensor der Kamera „so ausgerichtet sein MUSS, dass die lange Seite der Kamera an der langen Seite des Bildschirms ausgerichtet ist. Das heißt, wenn das Gerät im Querformat gehalten wird, MÜSSEN die Kameras Bilder im Querformat aufnehmen. Dies gilt unabhängig von der natürlichen Ausrichtung des Geräts, d. h. sowohl für Geräte, die hauptsächlich im Querformat verwendet werden, als auch für Geräte, die hauptsächlich im Hochformat verwendet werden.“
Durch die Anordnung der Kamera zum Bildschirm wird der Anzeigebereich des Kamerasuchers in einer Kamera-App maximiert. Außerdem geben Bildsensoren ihre Daten in der Regel im Querformat aus, wobei 4:3 das häufigste Seitenverhältnis ist.
Die natürliche Ausrichtung des Kamerasensors ist Querformat. In Abbildung 1 ist der Sensor der Frontkamera (die Kamera, die in dieselbe Richtung wie das Display zeigt) um 270 Grad relativ zum Smartphone gedreht, um der Android-Kompatibilitätsdefinition zu entsprechen.
Damit die Sensorrotation für Apps verfügbar ist, enthält die camera2 API die Konstante SENSOR_ORIENTATION. Bei den meisten Smartphones und Tablets wird für die Frontkamera eine Sensorausrichtung von 270 Grad und für die Rückkamera eine Sensorausrichtung von 90 Grad (Blickwinkel von der Rückseite des Geräts) gemeldet. Dadurch wird die lange Kante des Sensors an der langen Kante des Geräts ausgerichtet. Laptopkameras melden in der Regel eine Sensorausrichtung von 0 oder 180 Grad.
Da die Bildsensoren der Kamera ihre Daten (einen Bildpuffer) in der natürlichen Ausrichtung des Sensors (Querformat) ausgeben, muss der Bildpuffer um die von SENSOR_ORIENTATION angegebene Gradzahl gedreht werden, damit die Kameravorschau in der natürlichen Ausrichtung des Geräts aufrecht angezeigt wird. Bei Frontkameras erfolgt die Drehung gegen den Uhrzeigersinn, bei Rückkameras im Uhrzeigersinn.
Bei der Frontkamera in Abbildung 1 sieht der vom Kamerasensor erzeugte Bildpuffer beispielsweise so aus:
Das Bild muss um 270 Grad gegen den Uhrzeigersinn gedreht werden, damit die Ausrichtung der Vorschau mit der Ausrichtung des Geräts übereinstimmt:
Eine Rückkamera würde einen Bildpuffer mit derselben Ausrichtung wie der vorherige Puffer erzeugen, aber SENSOR_ORIENTATION beträgt 90 Grad. Dadurch wird der Zwischenspeicher um 90 Grad im Uhrzeigersinn gedreht.
Bildschirm drehen
Die Geräterotation ist die Anzahl der Grad, um die ein Gerät von seiner natürlichen Ausrichtung gedreht wird. Ein Smartphone im Querformat hat beispielsweise eine Gerätedrehung von 90 oder 270 Grad, je nach Drehrichtung.
Ein Bildpuffer des Kamerasensors muss um dieselbe Anzahl von Grad wie die Gerätedrehung (zusätzlich zu den Grad der Sensorausrichtung) gedreht werden, damit die Kameravorschau aufrecht angezeigt wird.
Ausrichtungsberechnung
Bei der korrekten Ausrichtung der Kameravorschau werden die Sensorausrichtung und die Gerätedrehung berücksichtigt.
Die Gesamtrotation des Sensorbildpuffers kann mit der folgenden Formel berechnet werden:
rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360
Dabei ist sign für Frontkameras 1 und für Rückkameras -1.
Bei Frontkameras wird der Bildpuffer gegen den Uhrzeigersinn gedreht (ausgehend von der natürlichen Ausrichtung des Sensors). Bei Rückkameras wird der Sensorbildpuffer im Uhrzeigersinn gedreht.
Der Ausdruck deviceOrientationDegrees * sign + 360 wandelt die Gerätedrehung für Rückkameras von gegen den Uhrzeigersinn in im Uhrzeigersinn um (z. B. 270 Grad gegen den Uhrzeigersinn in 90 Grad im Uhrzeigersinn). Der Modulo-Vorgang skaliert das Ergebnis auf weniger als 360 Grad (z. B. wird eine Drehung um 540 Grad auf 180 Grad skaliert).
Verschiedene APIs melden die Gerätedrehung unterschiedlich:
Display#getRotation()gibt die Drehung des Geräts gegen den Uhrzeigersinn an (aus Sicht des Nutzers). Dieser Wert wird unverändert in die vorherige Formel eingefügt.OrientationEventListener#onOrientationChanged()gibt die Drehung des Geräts im Uhrzeigersinn (aus Sicht des Nutzers) zurück. Negieren Sie den Wert, um ihn in der vorherigen Formel zu verwenden.
Frontkameras
Hier ist der Bildpuffer, der vom Kamerasensor in Abbildung 4 erzeugt wird:
Der Zwischenspeicher muss um 270 Grad gegen den Uhrzeigersinn gedreht werden, um die Sensorausrichtung anzupassen (siehe Kameraausrichtung oben):
Anschließend wird der Zwischenspeicher um weitere 90 Grad gegen den Uhrzeigersinn gedreht, um die Drehung des Geräts zu berücksichtigen. Das Ergebnis ist die korrekte Ausrichtung der Kameravorschau in Abbildung 4:
Hier ist die Kamera nach rechts ins Querformat gedreht:
Hier ist der Bildpuffer:
Der Puffer muss um 270 Grad gegen den Uhrzeigersinn gedreht werden, um die Sensorausrichtung anzupassen:
Anschließend wird der Zwischenspeicher um weitere 270 Grad gegen den Uhrzeigersinn gedreht, um die Gerätedrehung zu berücksichtigen:
Rückkameras
Rückkameras haben in der Regel eine Sensorausrichtung von 90 Grad (von der Rückseite des Geräts aus gesehen). Bei der Ausrichtung der Kameravorschau wird der Sensorbildpuffer um den Betrag der Sensordrehung im Uhrzeigersinn gedreht (nicht gegen den Uhrzeigersinn wie bei Frontkameras). Anschließend wird der Bildpuffer um den Betrag der Gerätedrehung gegen den Uhrzeigersinn gedreht.
Hier ist der Bildpuffer des Kamerasensors aus Abbildung 12:
Der Zwischenspeicher muss um 90 Grad im Uhrzeigersinn gedreht werden, um die Sensorausrichtung anzupassen:
Anschließend wird der Zwischenspeicher um 270 Grad gegen den Uhrzeigersinn gedreht, um die Gerätedrehung zu berücksichtigen:
Seitenverhältnis
Das Seitenverhältnis des Displays ändert sich, wenn sich die Ausrichtung des Geräts ändert, aber auch, wenn faltbare Geräte zusammen- und aufgeklappt werden, wenn die Größe von Fenstern im Mehrfenstermodus geändert wird und wenn Apps auf sekundären Displays geöffnet werden.
Der Bildpuffer des Kamerasensors muss so ausgerichtet und skaliert sein, dass er der Ausrichtung und dem Seitenverhältnis des UI-Elements des Suchers entspricht, wenn sich die Ausrichtung der UI dynamisch ändert – unabhängig davon, ob sich die Ausrichtung des Geräts ändert oder nicht.
Wenn deine App bei neuen Formfaktoren oder in Umgebungen mit Mehrfenstermodus oder mehreren Displays davon ausgeht, dass die Kameravorschau dieselbe Ausrichtung wie das Gerät hat (Hoch- oder Querformat), ist die Vorschau möglicherweise falsch ausgerichtet, falsch skaliert oder beides.
In Abbildung 16 wurde in der Anwendung fälschlicherweise angenommen, dass das Gerät um 90 Grad gegen den Uhrzeigersinn gedreht wurde. Daher wurde die Vorschau in der App um denselben Betrag gedreht.
In Abbildung 17 hat die App das Seitenverhältnis des Bildpuffers nicht angepasst, damit er richtig auf die neuen Abmessungen des UI-Elements für die Kameravorschau skaliert werden kann.
Bei Kamera-Apps mit fester Ausrichtung treten auf faltbaren Smartphones und anderen Geräten mit großen Displays wie Laptops in der Regel Probleme auf:
In Abbildung 18 ist die Benutzeroberfläche der Kamera-App seitlich, da die Ausrichtung der App auf das Hochformat beschränkt ist. Das Sucherbild ist relativ zum Kamerasensor richtig ausgerichtet.
Porträtmodus im Bild-im-Bild-Modus
Kamera-Apps, die den Mehrfenstermodus nicht unterstützen (resizeableActivity="false") und ihre Ausrichtung einschränken (screenOrientation="portrait" oder screenOrientation="landscape"), können auf Geräten mit großen Displays im Porträtmodus mit Einblendung platziert werden, um die Kameravorschau richtig auszurichten.
Im Inset-Porträtmodus werden Apps, die nur im Hochformat angezeigt werden können, im Hochformat mit Letterbox-Effekt (Inset) angezeigt, obwohl das Seitenverhältnis des Displays im Querformat ist. Apps, die nur im Querformat angezeigt werden, werden im Querformat mit Letterboxing dargestellt, obwohl das Seitenverhältnis des Displays im Hochformat ist. Das Kamerabild wird so gedreht, dass es an die Benutzeroberfläche der App angepasst ist, auf das Seitenverhältnis der Kameravorschau zugeschnitten und dann so skaliert, dass es die Vorschau ausfüllt.
Der Inset-Porträtmodus wird ausgelöst, wenn das Seitenverhältnis des Bildsensors der Kamera und das Seitenverhältnis der primären Aktivität der Anwendung nicht übereinstimmen.
In Abbildung 19 wurde die Kamera-App, die nur im Hochformat angezeigt wird, gedreht, damit die Benutzeroberfläche auf dem Laptop-Display aufrecht angezeigt wird. Die App wird im Letterbox-Format angezeigt, da sich das Seitenverhältnis der App im Hochformat von dem des Displays im Querformat unterscheidet. Das Kameravorschaubild wurde gedreht, um die Drehung der Benutzeroberfläche der App (aufgrund des eingefügten Hochformats) auszugleichen. Außerdem wurde das Bild zugeschnitten und skaliert, um es an das Hochformat anzupassen, wodurch das Sichtfeld verkleinert wurde.
Drehen, zuschneiden, skalieren
Der Inset-Porträtmodus wird für eine Kamera-App, die nur im Hochformat funktioniert, auf einem Display mit einem Querformat-Seitenverhältnis aufgerufen:
Die App wird im Hochformat mit Letterboxing angezeigt:
Das Kamerabild wird um 90 Grad gedreht, um die Neuausrichtung der App zu berücksichtigen:
Das Bild wird auf das Seitenverhältnis der Kameravorschau zugeschnitten und dann so skaliert, dass es die Vorschau ausfüllt (das Sichtfeld wird verkleinert):
Bei faltbaren Geräten kann die Ausrichtung des Kamerasensors im Hochformat sein, während das Seitenverhältnis des Displays im Querformat ist:
Da die Kameravorschau an die Sensorausrichtung angepasst wird, ist das Bild im Sucher richtig ausgerichtet, die App, die nur im Hochformat funktioniert, wird jedoch seitlich angezeigt.
Im eingebetteten Porträtmodus muss die App nur im Hochformat mit Letterbox-Effekt angezeigt werden, damit die App und die Kameravorschau richtig ausgerichtet sind:
API
Ab Android 12 (API-Level 31) können Apps den Porträtmodus mit Einzug auch explizit über das Attribut SCALER_ROTATE_AND_CROP der Klasse CaptureRequest steuern.
Der Standardwert ist SCALER_ROTATE_AND_CROP_AUTO. Dadurch kann das System den Porträtmodus aufrufen.
SCALER_ROTATE_AND_CROP_90 ist das Verhalten des Inset-Porträtmodus, wie oben beschrieben.
Nicht alle Geräte unterstützen alle SCALER_ROTATE_AND_CROP-Werte. Eine Liste der unterstützten Werte finden Sie unter CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES.
CameraX
Mit der Jetpack CameraX-Bibliothek ist es ganz einfach, einen Kamerasucher zu erstellen, der die Sensorausrichtung und die Gerätedrehung berücksichtigt.
Das Layoutelement PreviewView erstellt eine Kameravorschau, die automatisch an die Sensorausrichtung, die Gerätedrehung und die Skalierung angepasst wird.
PreviewView behält das Seitenverhältnis des Kamerabilds bei, indem der Skalierungstyp FILL_CENTER angewendet wird. Dadurch wird das Bild zentriert, aber möglicherweise zugeschnitten, um den Abmessungen von PreviewView zu entsprechen. Wenn Sie das Kamerabild im Letterbox-Format anzeigen möchten, legen Sie den Skalierungstyp auf FIT_CENTER fest.
Hier erfahren Sie, wie Sie mit PreviewView eine Kameravorschau erstellen.
Eine vollständige Beispielimplementierung finden Sie im Repository CameraXBasic auf GitHub.
CameraViewfinder
Ähnlich wie beim Anwendungsfall Vorschau bietet die CameraViewfinder-Bibliothek eine Reihe von Tools, mit denen sich eine Kameravorschau einfacher erstellen lässt. Sie ist nicht von CameraX Core abhängig und kann daher nahtlos in Ihre vorhandene Camera2-Codebasis integriert werden.
Anstatt Surface direkt zu verwenden, kannst du das Widget CameraViewfinder verwenden, um den Kamerafeed für Camera2 anzuzeigen.
CameraViewfinder verwendet intern entweder eine TextureView oder eine SurfaceView, um den Kamerafeed anzuzeigen, und wendet die erforderlichen Transformationen darauf an, um den Sucher korrekt darzustellen. Dazu gehören die Korrektur des Seitenverhältnisses, der Skalierung und der Drehung.
Wenn du die Oberfläche vom CameraViewfinder-Objekt anfordern möchtest, musst du ein ViewfinderSurfaceRequest erstellen.
Diese Anfrage enthält Anforderungen an die Oberflächenauflösung und Informationen zum Kameragerät aus CameraCharacteristics.
Durch den Aufruf von requestSurfaceAsync() wird die Anfrage an den Surface-Anbieter gesendet, der entweder ein TextureView oder ein SurfaceView ist, und es wird ein ListenableFuture von Surface zurückgegeben.
Durch den Aufruf von markSurfaceSafeToRelease() wird der Surface-Anbieter darüber informiert, dass die Surface nicht benötigt wird und zugehörige Ressourcen freigegeben werden können.
fun startCamera() {
val previewResolution = Size(width, height)
val viewfinderSurfaceRequest =
ViewfinderSurfaceRequest(previewResolution, characteristics)
val surfaceListenableFuture =
cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)
Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> {
override fun onSuccess(surface: Surface) {
/* create a CaptureSession using this surface as usual */
}
override fun onFailure(t: Throwable) { /* something went wrong */ }
}, ContextCompat.getMainExecutor(context))
}
SurfaceView
SurfaceView ist ein unkomplizierter Ansatz zum Erstellen einer Kameravorschau, wenn die Vorschau nicht verarbeitet werden muss und nicht animiert ist.
SurfaceView dreht den Bildpuffer des Kamerasensors automatisch entsprechend der Displayausrichtung, wobei sowohl die Sensorausrichtung als auch die Gerätedrehung berücksichtigt werden.
Der Bildpuffer wird jedoch so skaliert, dass er in die SurfaceView-Abmessungen passt, ohne dass das Seitenverhältnis berücksichtigt wird.
Das Seitenverhältnis des Bildpuffers muss mit dem Seitenverhältnis des SurfaceView übereinstimmen. Dazu können Sie den Inhalt des SurfaceView in der Methode onMeasure() der Komponente skalieren:
(Der Quellcode für computeRelativeRotation() befindet sich unten im Abschnitt Relative Drehung.)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val width = MeasureSpec.getSize(widthMeasureSpec) val height = MeasureSpec.getSize(heightMeasureSpec) val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees) if (previewWidth > 0f && previewHeight > 0f) { /* Scale factor required to scale the preview to its original size on the x-axis. */ val scaleX = if (relativeRotation % 180 == 0) { width.toFloat() / previewWidth } else { width.toFloat() / previewHeight } /* Scale factor required to scale the preview to its original size on the y-axis. */ val scaleY = if (relativeRotation % 180 == 0) { height.toFloat() / previewHeight } else { height.toFloat() / previewWidth } /* Scale factor required to fit the preview to the SurfaceView size. */ val finalScale = min(scaleX, scaleY) setScaleX(1 / scaleX * finalScale) setScaleY(1 / scaleY * finalScale) } setMeasuredDimension(width, height) }
Weitere Informationen zur Implementierung von SurfaceView als Kameravorschau finden Sie unter Kameraausrichtungen.
TextureView
TextureView ist weniger leistungsfähig als SurfaceView und
erfordert mehr Arbeit, bietet aber TextureView maximale Kontrolle über die Kamera
vorschau.
TextureView dreht den Sensorbildpuffer basierend auf der Sensorausrichtung, verarbeitet aber nicht die Gerätedrehung oder die Skalierung der Vorschau.
Skalierung und Drehung können in einer Matrix-Transformation codiert werden.
Informationen zum korrekten Skalieren und Drehen eines TextureView findest du unter In der Kamera-App Oberflächen mit anpassbarer Größe unterstützen.
Relative Drehung
Die relative Drehung des Kamerasensors ist der Grad der Drehung, der erforderlich ist, um die Ausgabe des Kamerasensors an die Geräteausrichtung anzupassen.
Die relative Drehung wird von Komponenten wie SurfaceView und TextureView verwendet, um die x- und y-Skalierungsfaktoren für das Vorschaubild zu bestimmen. Sie wird auch verwendet, um die Drehung des Sensor-Bildpuffers anzugeben.
Mit den Klassen CameraCharacteristics und Surface kann die relative Drehung des Kamerasensors berechnet werden:
/** * Computes rotation required to transform the camera sensor output orientation to the * device's current orientation in degrees. * * @param characteristics The CameraCharacteristics to query for the sensor orientation. * @param surfaceRotationDegrees The current device orientation as a Surface constant. * @return Relative rotation of the camera sensor output. */ public fun computeRelativeRotation( characteristics: CameraCharacteristics, surfaceRotationDegrees: Int ): Int { val sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0 // Reverse device orientation for back-facing cameras. val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT ) 1 else -1 // Calculate desired orientation relative to camera orientation to make // the image upright relative to the device orientation. return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360 }
Fenstermesswerte
Die Bildschirmgröße sollte nicht zur Bestimmung der Abmessungen des Kamerasuchers verwendet werden. Die Kamera-App wird möglicherweise nur in einem Teil des Bildschirms ausgeführt, entweder im Mehrfenstermodus auf Mobilgeräten oder im Freiformmodus unter ChromeOS.
WindowManager#getCurrentWindowMetrics() (in API-Level 30 hinzugefügt) gibt die Größe des Anwendungsfensters und nicht die Größe des Bildschirms zurück. Die Jetpack WindowManager-Bibliotheksmethoden WindowMetricsCalculator#computeCurrentWindowMetrics() und WindowInfoTracker#currentWindowMetrics() bieten ähnliche Unterstützung mit Abwärtskompatibilität bis API-Level 14.
Drehung um 180 Grad
Eine Drehung des Geräts um 180 Grad (z. B. von der natürlichen Ausrichtung in die natürliche Ausrichtung auf dem Kopf) löst den Callback onConfigurationChanged() nicht aus. In diesem Fall wird die Kameravorschau möglicherweise verkehrt herum angezeigt.
Wenn du eine Drehung um 180 Grad erkennen möchtest, implementiere einen DisplayListener und prüfe die Gerätedrehung mit einem Aufruf von Display#getRotation() im Callback onDisplayChanged().
Exklusive Ressourcen
Vor Android 10 befand sich in einer Umgebung mit mehreren Fenstern nur die oberste sichtbare Aktivität im Status RESUMED. Das war für die Nutzenden verwirrend, da das System nicht anzeigte, welche Aktivität fortgesetzt wurde.
Mit Android 10 (API-Level 29) wurde Multi-Resume eingeführt, wobei sich alle sichtbaren Aktivitäten im Status RESUMED befinden. Sichtbare Aktivitäten können weiterhin in den Status PAUSED wechseln, wenn sich beispielsweise eine transparente Aktivität über der Aktivität befindet oder die Aktivität nicht fokussierbar ist, z. B. im Bild-im-Bild-Modus (siehe Unterstützung für Bild-im-Bild).
Eine Anwendung, die die Kamera, das Mikrofon oder eine exklusive oder Singleton-Ressource auf API-Level 29 oder höher verwendet, muss Multi-Resume unterstützen. Wenn beispielsweise drei fortgesetzte Aktivitäten die Kamera verwenden möchten, kann nur eine auf diese exklusive Ressource zugreifen. Jede Aktivität muss einen onDisconnected()-Callback implementieren, um über den präemptiven Zugriff auf die Kamera durch eine Aktivität mit höherer Priorität informiert zu werden.
Weitere Informationen finden Sie unter Multi-Resume.
Zusätzliche Ressourcen
- Ein Camera2-Beispiel findest du in der Camera2Basic-App auf GitHub.
- Informationen zum Anwendungsfall der CameraX-Vorschau finden Sie unter Vorschau implementieren.
- Eine Beispielimplementierung für die Kameravorschau mit CameraX finden Sie im CameraXBasic-Repository auf GitHub.
- Informationen zur Kameravorschau unter ChromeOS findest du unter Kameraausrichtungen.
- Informationen zur Entwicklung für faltbare Geräte findest du unter Informationen zu faltbaren Geräten.