Wenn Ihre Android-App Kameras verwendet, müssen Sie bei der Verarbeitung von Ausrichtungen einige Besonderheiten beachten. In diesem Dokument wird davon ausgegangen, dass Sie mit den grundlegenden Konzepten der Android camera2 API vertraut sind. Eine Übersicht über camera2 finden Sie in unserem Blogpost oder in dieser Zusammenfassung. Wir empfehlen Ihnen außerdem, zuerst eine Kamera-App zu schreiben, bevor Sie dieses Dokument lesen.
Hintergrund
Die Verarbeitung von Ausrichtungen in Android-Kamera-Apps ist schwierig und erfordert die Berücksichtigung der folgenden Faktoren:
- Natürliche Ausrichtung: Die Ausrichtung des Displays, wenn sich das Gerät in der „normalen“ Position für das Gerätedesign befindet – in der Regel das Hochformat für Smartphones und das Querformat für Laptops.
- Sensorausrichtung: Die Ausrichtung des Sensors, der physisch am Gerät angebracht ist.
- Displayausrichtung: Gibt an, wie stark das Gerät physisch von der natürlichen Ausrichtung abweicht.
- Suchergröße: Die Größe des Suchers, der zum Anzeigen der Kameravorschau verwendet wird.
- Die von der Kamera ausgegebene Bildgröße.
Durch die Kombination dieser Faktoren ergibt sich eine Vielzahl möglicher UI- und Vorschaukonfigurationen für Kamera-Apps. In diesem Dokument wird gezeigt, wie Entwickler diese Probleme umgehen und Kameraausrichtungen in Android-Apps richtig verarbeiten können.
Der Einfachheit halber gehen wir davon aus, dass alle Beispiele eine nach hinten gerichtete Kamera betreffen, sofern nicht anders angegeben. Außerdem sind alle folgenden Fotos simuliert, um die Abbildungen visuell klarer zu gestalten.
Ausrichtungen
Natürliche Ausrichtung
Die natürliche Ausrichtung ist die Displayausrichtung, wenn sich das Gerät in der Position befindet, in der es normalerweise verwendet wird. Bei Smartphones ist die natürliche Ausrichtung oft das Hochformat. Mit anderen Worten: Smartphones sind schmaler und höher. Laptops sind von Natur aus im Querformat ausgerichtet, d. h., sie sind breiter als hoch. Bei Tablets ist es etwas komplizierter, da sie sowohl im Hoch- als auch im Querformat verwendet werden können.
Sensorausrichtung
Die Sensorausrichtung wird formal in Grad gemessen, um die ein Ausgabebild des Sensors im Uhrzeigersinn gedreht werden muss, damit es der natürlichen Ausrichtung des Geräts entspricht. Die Sensorausrichtung ist die Anzahl der Grad, um die ein Sensor gegen den Uhrzeigersinn gedreht wird, bevor er auf dem Gerät montiert wird. Wenn Sie auf das Display schauen, scheint die Drehung im Uhrzeigersinn zu erfolgen. Das liegt daran, dass der Sensor der Rückkamera auf der Rückseite des Geräts installiert ist.
Gemäß Android 10 Compatibility Definition 7.5.5 Camera Orientation MÜSSEN Front- und Rückkameras so ausgerichtet sein, dass die lange Seite der Kamera mit der langen Seite des Displays übereinstimmt.
Ausgabepuffer von Kameras sind im Querformat. Da die natürliche Ausrichtung von Smartphones in der Regel das Hochformat ist, beträgt die Sensorausrichtung normalerweise 90 oder 270 Grad gegenüber der natürlichen Ausrichtung, damit die lange Seite des Ausgabepuffers der langen Seite des Bildschirms entspricht. Die Sensorausrichtung ist bei Geräten, deren natürliche Ausrichtung das Querformat ist, z. B. bei Chromebooks, anders. Bei diesen Geräten sind die Bildsensoren wieder so platziert, dass die lange Seite des Ausgabepuffers mit der langen Seite des Displays übereinstimmt. Da beide Bilder im Querformat aufgenommen wurden, stimmen die Ausrichtungen überein und die Sensorausrichtung beträgt 0 oder 180 Grad.
Die folgenden Abbildungen zeigen, wie die Dinge aus der Sicht eines Beobachters aussehen, der auf das Gerätedisplay schaut:
Stellen Sie sich folgende Szene vor:
| Smartphone | Laptop |
|---|---|
![]() |
![]() |
Da die Sensorausrichtung auf Smartphones in der Regel 90 oder 270 Grad beträgt, würden die Bilder ohne Berücksichtigung der Sensorausrichtung so aussehen:
| Smartphone | Laptop |
|---|---|
![]() |
![]() |
Angenommen, die Ausrichtung des Sensors gegen den Uhrzeigersinn wird in der Variablen „sensorOrientation“ gespeichert. Um die Sensorausrichtung zu berücksichtigen, müssen Sie die Ausgabepuffer um `sensorOrientation` im Uhrzeigersinn drehen, um die Ausrichtung wieder an die natürliche Ausrichtung des Geräts anzupassen.
In Android können Apps TextureView oder SurfaceView verwenden, um die Kameravorschau anzuzeigen. Beide können die Sensorausrichtung verarbeiten, wenn Apps sie richtig verwenden. In den folgenden Abschnitten wird beschrieben, wie Sie die Sensorausrichtung berücksichtigen sollten.
Displayausrichtung
Die Displaydrehung wird formal durch die Drehung der gezeichneten Grafiken auf dem Bildschirm definiert. Sie erfolgt in die entgegengesetzte Richtung der physischen Drehung des Geräts aus seiner natürlichen Ausrichtung. In den folgenden Abschnitten wird davon ausgegangen, dass alle Displayrotationen Vielfache von 90 sind. Wenn Sie die Displaydrehung anhand der absoluten Gradzahl abrufen, runden Sie sie auf den nächstgelegenen Wert aus {0, 90, 180, 270} auf.
Mit „Bildschirmausrichtung“ ist in den folgenden Abschnitten gemeint, ob ein Gerät physisch im Quer- oder Hochformat gehalten wird. Dies ist nicht dasselbe wie „Bildschirmdrehung“.
Angenommen, Sie drehen die Geräte um 90 Grad gegen den Uhrzeigersinn, wie in der folgenden Abbildung dargestellt:
Angenommen, die Ausgabepuffer wurden bereits entsprechend der Sensorausrichtung gedreht. Dann haben Sie die folgenden Ausgabepuffer:
| Smartphone | Laptop |
|---|---|
![]() |
![]() |
Wenn die Displaydrehung in der Variablen „displayRotation“ gespeichert ist, sollten Sie die Ausgabepuffer um „displayRotation“ gegen den Uhrzeigersinn drehen, um das richtige Bild zu erhalten.
Bei Frontkameras wirkt sich die Displaydrehung in Bezug auf den Bildschirm in die entgegengesetzte Richtung auf die Bildpuffer aus. Wenn Sie es mit einer Frontkamera zu tun haben, sollten Sie die Puffer um displayRotation im Uhrzeigersinn drehen.
Einschränkungen
Die Displayausrichtung gibt die Drehung des Geräts gegen den Uhrzeigersinn an. Das gilt nicht für alle APIs für Ausrichtung/Drehung.
Beispiel:
-
Wenn Sie
Display#getRotation()verwenden, erhalten Sie die gegen den Uhrzeigersinn erfolgende Drehung, wie in diesem Dokument beschrieben. - Wenn Sie OrientationEventListener#onOrientationChanged(int) verwenden, erhalten Sie stattdessen die im Uhrzeigersinn erfolgende Drehung.
Wichtig ist hier, dass die Displaydrehung relativ zur natürlichen Ausrichtung erfolgt. Wenn Sie ein Smartphone beispielsweise um 90 oder 270 Grad drehen, wird das Display im Querformat angezeigt. Wenn Sie einen Laptop um denselben Betrag drehen, erhalten Sie einen Bildschirm im Hochformat. Apps sollten dies immer berücksichtigen und niemals Annahmen über die natürliche Ausrichtung eines Geräts treffen.
Beispiele
Anhand der vorherigen Abbildungen lässt sich veranschaulichen, was die Ausrichtungen und Drehungen sind.
| Smartphone | Laptop |
|---|---|
| Natürliche Ausrichtung = Hochformat | Natürliche Ausrichtung = Querformat |
| Sensorausrichtung = 90 | Sensor Orientation = 0 |
| Displayausrichtung = 0 | Displayausrichtung = 0 |
| Displayausrichtung = Hochformat | Displayausrichtung = Querformat |
| Smartphone | Laptop |
|---|---|
| Natürliche Ausrichtung = Hochformat | Natürliche Ausrichtung = Querformat |
| Sensorausrichtung = 90 | Sensor Orientation = 0 |
| Display Rotation = 90 | Display Rotation = 90 |
| Displayausrichtung = Querformat | Displayausrichtung = Hochformat |
Suchergröße
Apps sollten die Größe des Suchers immer an die Ausrichtung, Drehung und Bildschirmauflösung anpassen. Im Allgemeinen sollte die Ausrichtung des Suchers mit der aktuellen Displayausrichtung übereinstimmen. Das bedeutet, dass Apps die lange Kante des Suchers an der langen Kante des Displays ausrichten sollten.
Bildausgabegröße nach Kamera
Wählen Sie für die Bildausgabegröße der Vorschau nach Möglichkeit eine Größe aus, die der Größe des Suchers entspricht oder nur geringfügig größer ist. Ausgabepuffer sollten in der Regel nicht skaliert werden, da dies zu einer Verpixelung führen würde. Sie sollten auch keine zu große Größe auswählen, da dies die Leistung beeinträchtigen und den Akkuverbrauch erhöhen kann.
JPEG-Ausrichtung
Beginnen wir mit einer häufigen Situation: dem Aufnehmen eines JPEG-Fotos. In der camera2 API können Sie JPEG_ORIENTATION in der Aufnahmeanfrage übergeben, um anzugeben, um wie viel Grad Ihre ausgegebenen JPEGs im Uhrzeigersinn gedreht werden sollen.
Hier eine kurze Zusammenfassung der erwähnten Punkte:
-
Um die Sensorausrichtung zu berücksichtigen, müssen Sie den Bildpuffer um
sensorOrientationim Uhrzeigersinn drehen. -
Um die Displaydrehung zu verarbeiten, müssen Sie einen Puffer um
displayRotationgegen den Uhrzeigersinn für Rückkameras und im Uhrzeigersinn für Frontkameras drehen.
Wenn Sie die beiden Faktoren addieren, erhalten Sie den Betrag, um den Sie die Grafik im Uhrzeigersinn drehen möchten:
-
sensorOrientation - displayRotationfür Rückkameras. -
sensorOrientation + displayRotationfür Frontkameras.
Den Beispielcode für diese Logik finden Sie in der Dokumentation zu JPEG_ORIENTATION. Beachten Sie, dass im Beispielcode der Dokumentation deviceOrientation für die Drehung des Geräts im Uhrzeigersinn verwendet wird. Daher sind die Vorzeichen für die Displaydrehung umgekehrt.
Vorschau
Was ist mit der Kameravorschau? Es gibt zwei Hauptmethoden, mit denen eine App eine Kameravorschau anzeigen kann: SurfaceView und TextureView. Für jede ist ein anderer Ansatz erforderlich, um die Ausrichtung richtig zu verarbeiten.
SurfaceView
SurfaceView wird im Allgemeinen für Kameravorschauen empfohlen, sofern Sie die Vorschaupuffer nicht verarbeiten oder animieren müssen. Sie ist leistungsfähiger und weniger ressourcenintensiv als TextureView.
SurfaceView ist auch relativ einfach zu gestalten. Sie müssen sich nur um das Seitenverhältnis der SurfaceView kümmern, in der Sie die Kameravorschau anzeigen.
Quelle
Unterhalb von SurfaceView dreht die Android-Plattform Ausgabepuffer entsprechend der Bildschirmausrichtung des Geräts. Das bedeutet, dass sowohl die Sensorausrichtung als auch die Displayrotation berücksichtigt werden. Einfacher ausgedrückt: Wenn unser Display im Querformat ist, erhalten wir eine Vorschau im Querformat und umgekehrt.
Dies wird in der folgenden Tabelle veranschaulicht. Wichtig ist, dass die Ausrichtung der Quelle nicht nur durch die Displayrotation bestimmt wird.
| Displayausrichtung | Smartphone (natürliche Ausrichtung = Hochformat) | Laptop (natürliche Ausrichtung = Querformat) |
|---|---|---|
| 0 | ![]() |
![]() |
| 90 | ![]() |
![]() |
| 180 | ![]() |
![]() |
| 270 | ![]() |
![]() |
Layout
Wie Sie sehen, übernimmt SurfaceView bereits einige der schwierigen Dinge für uns. Jetzt müssen Sie jedoch die Größe des Suchers oder die Größe der Vorschau auf dem Bildschirm berücksichtigen. SurfaceView skaliert den Quellpuffer automatisch an seine Abmessungen an. Das Seitenverhältnis des Suchers muss mit dem des Quellpuffers übereinstimmen. Wenn Sie beispielsweise versuchen, eine Vorschau im Hochformat in eine SurfaceView im Querformat einzupassen, erhalten Sie ein verzerrtes Bild wie dieses:
Im Allgemeinen sollte das Seitenverhältnis (d.h. Breite/Höhe) des Suchers mit dem Seitenverhältnis der Quelle übereinstimmen. Wenn Sie das Bild im Sucher nicht zuschneiden möchten, um die Anzeige zu korrigieren, müssen Sie zwei Fälle berücksichtigen: wenn aspectRatioActivity größer als aspectRatioSource ist und wenn aspectRatioActivity kleiner oder gleich aspectRatioSource ist.
aspectRatioActivity > aspectRatioSource
Sie können sich den Fall als „breitere“ Aktivität vorstellen. Im Folgenden sehen Sie ein Beispiel mit einer Aktivität im Format 16:9 und einer Quelle im Format 4:3.
aspectRatioActivity = 16/9 ≈ 1.78 aspectRatioSource = 4/3 ≈ 1.33
Zuerst sollte auch dein Sucher ein Seitenverhältnis von 4:3 haben. Anschließend passen Sie die Quelle und den Sucher in die Aktivität ein:
In diesem Fall sollten Sie die Höhe des Suchers an die Höhe der Aktivität anpassen und das Seitenverhältnis des Suchers mit dem Seitenverhältnis der Quelle identisch machen. Der Pseudocode lautet so:
viewfinderHeight = activityHeight; viewfinderWidth = activityHeight * aspectRatioSource;
aspectRatioActivity ≤ aspectRatioSource
Der andere Fall ist, wenn die Aktivität „schmaler“ oder „höher“ ist. Wir können das vorherige Beispiel wiederverwenden. Im folgenden Beispiel drehen Sie das Gerät jedoch um 90 Grad, sodass das Seitenverhältnis der Aktivität 9:16 und das der Quelle 3:4 ist.
aspectRatioActivity = 9/16 = 0.5625 aspectRatioSource = 3/4 = 0.75
In diesem Fall sollten Sie die Quelle und den Sucher so in die Aktivität einpassen:
Die Breite des Suchers sollte der Breite der Aktivität entsprechen (im Gegensatz zur Höhe im vorherigen Fall). Das Seitenverhältnis des Suchers sollte mit dem Seitenverhältnis der Quelle identisch sein. Pseudo code:
viewfinderWidth = activityWidth; viewfinderHeight = activityWidth / aspectRatioSource;
Abschneidungen
AutoFitSurfaceView.kt (github) aus den Camera2-Beispielen überschreibt SurfaceView und verarbeitet nicht übereinstimmende Seitenverhältnisse, indem ein Bild verwendet wird, das in beiden Dimensionen gleich oder „nur größer“ als die Aktivität ist. Inhalte, die überlaufen, werden dann abgeschnitten. Das ist nützlich für Apps, bei denen die Vorschau die gesamte Aktivität abdecken oder eine Ansicht mit festen Abmessungen vollständig ausfüllen soll, ohne das Bild zu verzerren.
Caveat
Im vorherigen Beispiel wird versucht, den Bildschirmbereich zu maximieren, indem die Vorschau nur etwas größer als die Aktivität ist, sodass kein Platz leer bleibt. Das funktioniert, weil die überlaufenden Teile standardmäßig vom übergeordneten Layout (oder der ViewGroup) abgeschnitten werden. Das Verhalten entspricht RelativeLayout und LinearLayout, aber NICHT ConstraintLayout. Bei einem ConstraintLayout werden die untergeordneten Ansichten möglicherweise so angepasst, dass sie in das Layout passen. Dadurch wird der beabsichtigte „Center-Crop“-Effekt unterbrochen und es kommt zu gestreckten Vorschaubildern. Sie können sich diesen Commit als Referenz ansehen.
TextureView
TextureView bietet maximale Kontrolle über den Inhalt der Kameravorschau, hat aber einen Leistungsaufwand. Außerdem ist mehr Aufwand erforderlich, um die Kameravorschau richtig darzustellen.
Quelle
Unterhalb von TextureView dreht die Android-Plattform die Ausgabepuffer entsprechend der Sensorausrichtung, um der natürlichen Ausrichtung des Geräts zu entsprechen. TextureView verarbeitet zwar die Sensorausrichtung, aber nicht die Displayrotation. Die Ausgabepuffer werden an der natürlichen Ausrichtung des Geräts ausgerichtet. Das bedeutet, dass Sie die Bildschirmrotationen selbst verarbeiten müssen.
Dies wird in der folgenden Tabelle veranschaulicht. Wenn Sie die Zahlen um die entsprechende Displayausrichtung drehen, erhalten Sie in SurfaceView dieselben Zahlen.
| Displayausrichtung | Smartphone (natürliche Ausrichtung = Hochformat) | Laptop (natürliche Ausrichtung = Querformat) |
|---|---|---|
| 0 | ![]() |
![]() |
| 90 | ![]() |
![]() |
| 180 | ![]() |
![]() |
| 270 | ![]() |
![]() |
Layout
Das Layout ist bei TextureView etwas kompliziert. Bisher wurde empfohlen, eine Transformationsmatrix für TextureView zu verwenden, aber diese Methode funktioniert nicht auf allen Geräten. Wir empfehlen, stattdessen der Anleitung in diesem Artikel zu folgen.
So legst du Vorschauen in einem TextureView richtig an:
- Legen Sie die Größe der TextureView auf die ausgewählte Vorschaugröße fest.
- Skaliere die möglicherweise gestreckte TextureView zurück auf die ursprünglichen Abmessungen der Vorschau.
-
Drehe die TextureView um
displayRotationGrad gegen den Uhrzeigersinn.
Angenommen, Sie haben ein Smartphone, dessen Display um 90 Grad gedreht ist.
1. Legen Sie die Größe des TextureView auf die ausgewählte Vorschaugröße fest.
Angenommen, die von Ihnen ausgewählte Vorschaugröße ist previewWidth × previewHeight, wobei previewWidth > previewHeight (die Sensorausgabe ist von Natur aus querformatig). Beim Konfigurieren einer Aufnahmesitzung sollte SurfaceTexture#setDefaultBufferSize(int width, height) aufgerufen werden, um die Vorschaubildgröße (previewWidth × previewHeight) anzugeben.
Bevor Sie setDefaultBufferSize aufrufen, müssen Sie auch die Größe des TextureView auf `previewWidth × previewHeight` mit View#setLayoutParams(android.view.ViewGroup.LayoutParams) festlegen. Der Grund dafür ist, dass TextureView SurfaceTexture#setDefaultBufferSize(int width, height) mit der gemessenen Breite und Höhe aufruft. Wenn die Größe des TextureView nicht explizit festgelegt wird, kann dies zu einem Race Condition führen. Dies lässt sich vermeiden, indem Sie die Größe der TextureView explizit festlegen.
Die TextureView-Ansicht entspricht jetzt möglicherweise nicht den Abmessungen der Quelle. Bei Smartphones ist die Quelle hochformatig, die TextureView jedoch aufgrund der gerade festgelegten layoutParams querformatig. Das würde zu gestreckten Vorschauen führen, wie hier dargestellt:
2. Skaliere die möglicherweise gestreckte TextureView zurück auf die ursprünglichen Abmessungen der Vorschau.
Berücksichtigen Sie Folgendes, um die gestreckte Vorschau wieder auf die Abmessungen der Quelle zu skalieren.
Die Dimensionen der Quelle (sourceWidth × sourceHeight) sind:
-
previewHeight × previewWidth, wenn die natürliche Ausrichtung Hochformat oder umgekehrtes Hochformat ist (Sensorausrichtung ist 90 oder 270 Grad) -
previewWidth × previewHeight, wenn die natürliche Ausrichtung das Querformat oder das umgekehrte Querformat ist (die Sensorausrichtung ist 0 oder 180 Grad).
Verzerrungen mit View#setScaleX(float) und View#setScaleY(float) beheben
-
setScaleX(
sourceWidth / previewWidth) -
setScaleY(
sourceHeight / previewHeight)
3. Drehen Sie die Vorschau um `displayRotation` gegen den Uhrzeigersinn.
Wie bereits erwähnt, sollten Sie die Vorschau um displayRotation gegen den Uhrzeigersinn drehen, um die Displaydrehung auszugleichen.
Dazu können Sie View#setRotation(float) verwenden.
-
setRotation(
-displayRotation), da es sich um eine Drehung im Uhrzeigersinn handelt.
Beispiel
-
PreviewViewaus camerax in Jetpack verarbeitet das TextureView-Layout wie zuvor beschrieben. Die Transformation wird mit PreviewCorrector konfiguriert.
Hinweis:Wenn Sie die Transformationsmatrix für TextureView bereits in Ihrem Code verwendet haben, wird die Vorschau auf Geräten, die standardmäßig im Querformat ausgerichtet sind, z. B. Chromebooks, möglicherweise nicht richtig angezeigt. Wahrscheinlich wird in Ihrer Transformationsmatrix fälschlicherweise davon ausgegangen, dass die Sensorausrichtung 90 oder 270 Grad beträgt. Sie können sich diesen Commit auf GitHub ansehen, um eine Problemumgehung zu finden. Wir empfehlen jedoch dringend, Ihre App stattdessen auf die hier beschriebene Methode umzustellen.





















