Questo argomento mostra come configurare i casi d'uso di CameraX all'interno della tua app per ottenere immagini con le informazioni sulla rotazione corrette, che provengano dal caso d'uso ImageAnalysis
o ImageCapture
. Così:
ImageAnalysis
del caso d'usoImageAnalysis
deve ricevere frame con la rotazione corretta.Analyzer
- Il caso d'uso
ImageCapture
dovrebbe scattare foto con la rotazione corretta.
Terminologia
Questo argomento utilizza la seguente terminologia, pertanto è importante comprendere il significato di ciascun termine:
- Orientamento del display
- Si riferisce al lato del dispositivo in posizione verticale e può essere uno di quattro valori: ritratto, orizzontale, ritratto inverso o orizzontale inverso.
- Rotazione del display
- Si tratta del valore restituito da
Display.getRotation()
e rappresenta i gradi di rotazione antioraria del dispositivo rispetto al suo orientamento naturale. - Rotazione target
- Rappresenta il numero di gradi di rotazione del dispositivo in senso orario per raggiungere il suo orientamento naturale.
Come determinare la rotazione target
Gli esempi riportati di seguito mostrano come determinare la rotazione target per un dispositivo in base al suo orientamento naturale.
Esempio 1: orientamento naturale verticale
Esempio di dispositivo: Pixel 3 XL | |
---|---|
Orientamento naturale = Ritratto Rotazione del display = 0 |
|
Orientamento naturale = Ritratto Rotazione del display = 90 |
Esempio 2: orientamento naturale orizzontale
Esempio di dispositivo: Pixel C | |
---|---|
Orientamento naturale = Orizzontale Rotazione del display = 0 |
|
Orientamento naturale = Orizzontale Rotazione del display = 270 |
Rotazione delle immagini
Quale parte è rivolta verso l\'alto? L'orientamento del sensore è definito in Android come valore costante, che rappresenta i gradi (0, 90, 180, 270) di rotazione del sensore rispetto alla parte superiore del dispositivo quando il dispositivo è in una posizione naturale. Per tutti i casi nei diagrammi, la rotazione dell'immagine descrive come devono essere ruotati i dati in senso orario per essere visualizzati in verticale.
Gli esempi seguenti mostrano quale deve essere la rotazione dell'immagine in base all'orientamento del sensore della fotocamera. Inoltre, presuppongono che la rotazione target sia impostata sulla rotazione del display.
Esempio 1: sensore ruotato di 90 gradi
Esempio di dispositivo: Pixel 3 XL | |
---|---|
Rotazione del display = 0 |
|
Rotazione del display = 90 |
Esempio 2: sensore ruotato di 270 gradi
Esempio di dispositivo: Nexus 5X | |
---|---|
Rotazione del display = 0 |
|
Rotazione del display = 90 |
Esempio 3: sensore ruotato di 0 gradi
Esempio di dispositivo: Pixel C (tablet) | |
---|---|
Rotazione del display = 0 |
|
Rotazione del display = 270 |
Calcolo della rotazione di un'immagine
ImageAnalysis
Il Analyzer
di ImageAnalysis
riceve le immagini dalla fotocamera sotto forma di
ImageProxy
. Ogni immagine contiene informazioni sulla rotazione, accessibili tramite:
val rotation = imageProxy.imageInfo.rotationDegrees
Questo valore rappresenta i gradi di rotazione dell'immagine in senso orario per allinearla alla rotazione target di ImageAnalysis
. Nel contesto di un'app Android, la rotazione target di ImageAnalysis
corrisponde in genere all'orientamento dello schermo.
ImageCapture
Un callback è associato a un'istanza ImageCapture
per segnalare quando un risultato di acquisizione è pronto. Il risultato può essere l'immagine acquisita o un errore.
Quando scatti una foto, il callback fornito può essere di uno dei seguenti tipi:
OnImageCapturedCallback
: riceve un'immagine con accesso in memoria sotto forma diImageProxy
.OnImageSavedCallback
: viene invocato quando l'immagine acquisita è stata memorizzata correttamente nella posizione specificata daImageCapture.OutputFileOptions
. Le opzioni possono specificare unFile
, unOutputStream
o una posizione inMediaStore
.
La rotazione dell'immagine acquisita, indipendentemente dal formato (ImageProxy
,
File
, OutputStream
, MediaStore Uri
), rappresenta i gradi di rotazione con
la quale l'immagine acquisita deve essere ruotata in senso orario per corrispondere alla
rotazione target di ImageCapture
, che, di nuovo, nel contesto di un'app per Android, tipicamente
corrisponde all'orientamento dello schermo.
Il recupero della rotazione dell'immagine acquisita può essere eseguito in uno dei seguenti modi:
ImageProxy
val rotation = imageProxy.imageInfo.rotationDegrees
File
val exif = Exif.createFromFile(file) val rotation = exif.rotation
OutputStream
val byteArray = outputStream.toByteArray() val exif = Exif.createFromInputStream(ByteArrayInputStream(byteArray)) val rotation = exif.rotation
MediaStore uri
val inputStream = contentResolver.openInputStream(outputFileResults.savedUri) val exif = Exif.createFromInputStream(inputStream) val rotation = exif.rotation
Verificare la rotazione di un'immagine
I casi d'uso ImageAnalysis
e ImageCapture
ricevono ImageProxy
dalla
videocamera dopo una richiesta di acquisizione andata a buon fine. Un ImageProxy
racchiude un'immagine e le informazioni relative, inclusa la rotazione. Queste informazioni sulla rotazione rappresentano i gradi di rotazione dell'immagine in modo che corrispondano alla rotazione target del caso d'uso.
Linee guida per la rotazione del target di ImageCapture/ImageAnalysis
Poiché molti dispositivi non ruotano in modalità Ritratto o Orizzontale capovolto per impostazione predefinita, alcune app per Android non supportano questi orientamenti. Il fatto che un'app supporti o meno questa funzionalità cambia il modo in cui può essere aggiornata la rotazione dei target dei casi d'uso.
Di seguito sono riportate due tabelle che definiscono come mantenere sincronizzata la rotazione target dei casi d'uso con la rotazione del display. Il primo mostra come farlo supportando tutti i quattro gli orientamenti, mentre il secondo gestisce solo gli orientamenti a cui il dispositivo ruota per impostazione predefinita.
Per scegliere quali linee guida seguire nella tua app:
Verifica se la videocamera
Activity
della tua app ha un orientamento bloccato, un orientamento sbloccato o se sostituisce le modifiche alla configurazione dell'orientamento.Decidi se la videocamera
Activity
della tua app deve gestire tutti e quattro gli orientamenti del dispositivo (verticale, verticale capovolto, orizzontale e orizzontale capovolto) o solo quelli supportati per impostazione predefinita dal dispositivo su cui è in esecuzione.
Supporta tutti e quattro gli orientamenti
Questa tabella riporta alcune linee guida da seguire nei casi in cui il dispositivo non ruoti in modalità Ritratto inverso. Lo stesso vale per i dispositivi che non ruotano in modalità Orizzontale inversa.
Scenario | Linee guida | Modalità finestra singola | Modalità schermo diviso multi-finestra |
---|---|---|---|
Orientamento sbloccato |
Configura i casi d'uso ogni volta che viene creato Activity , ad esempio nel callback onCreate() di Activity .
|
||
Utilizza onOrientationChanged() di OrientationEventListener .
All'interno del callback, aggiorna la rotazione del target dei casi d'uso. Questo gestisce i casi in cui il sistema non rielabora il Activity anche dopo una modifica dell'orientamento, ad esempio quando il dispositivo viene ruotato di 180 gradi.
|
Gestisce anche il caso in cui il display sia in orientamento verticale capovolto e il dispositivo non ruoti in verticale capovolto per impostazione predefinita. |
Gestisce anche i casi in cui Activity non viene nuovamente creato quando il dispositivo ruota (ad esempio di 90 gradi). Questo accade su dispositivi con fattore di forma ridotto quando l'app occupa metà dello schermo e su dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
|
(Facoltativo) Imposta la proprietà screenOrientation
di Activity su fullSensor nel file AndroidManifest .
|
In questo modo, l'interfaccia utente è verticale quando il dispositivo è in ritratto capovolto e consente al sistema di ricreare Activity ogni volta che il dispositivo viene ruotato di 90 gradi.
|
Non ha alcun effetto sui dispositivi che non ruotano in modalità Ritratto capovolto per impostazione predefinita. La modalità multi-finestra non è supportata quando il display è in orientamento ritratto opposto. | |
Orientamento bloccato |
Configura i casi d'uso una sola volta, quando viene creato per la prima volta il Activity , ad esempio nel callback onCreate() del Activity .
|
||
Utilizza onOrientationChanged() di OrientationEventListener .
All'interno del callback, aggiorna la rotazione target dei casi d'uso, ad eccezione di Anteprima.
|
Gestisce anche i casi in cui Activity non viene nuovamente creato quando il dispositivo ruota (ad esempio di 90 gradi). Questo accade su dispositivi con fattore di forma ridotto quando l'app occupa metà dello schermo e su dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
||
Orientamento configChanges sostituito |
Configura i casi d'uso una sola volta, quando viene creato per la prima volta il Activity , ad esempio nel callback onCreate() del Activity .
|
||
Utilizza onOrientationChanged() di OrientationEventListener .
All'interno del callback, aggiorna la rotazione del target dei casi d'uso.
|
Gestisce anche i casi in cui Activity non viene nuovamente creato quando il dispositivo ruota (ad esempio di 90 gradi). Questo accade su dispositivi con fattore di forma ridotto quando l'app occupa metà dello schermo e su dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
||
(Facoltativo) Imposta la proprietà screenOrientation dell'attività su fullSensor nel file AndroidManifest. | Consente all'interfaccia utente di essere in verticale quando il dispositivo è in modalità Ritratto inversa. | Non ha alcun effetto sui dispositivi che non ruotano in modalità Ritratto capovolto per impostazione predefinita. La modalità multi-finestra non è supportata quando il display è in orientamento verticale opposto. |
Supporta solo gli orientamenti supportati dal dispositivo
Supporta solo gli orientamenti supportati dal dispositivo per impostazione predefinita (che possono o meno includere verticale/orizzontale capovolto).
Scenario | Linee guida | Modalità schermo diviso multi-finestra |
---|---|---|
Orientamento sbloccato |
Configura i casi d'uso ogni volta che viene creato Activity , ad esempio nel callback onCreate() di Activity .
|
|
Utilizza onDisplayChanged() di DisplayListener . All'interno del callback, aggiorna la rotazione target dei casi d'uso, ad esempio quando il dispositivo viene ruotato di 180 gradi.
|
Gestisce anche i casi in cui Activity non viene nuovamente creato quando il dispositivo ruota (ad esempio di 90 gradi). Questo accade su dispositivi con fattore di forma ridotto quando l'app occupa metà dello schermo e su dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
|
Orientamento bloccato |
Configura i casi d'uso una sola volta, quando viene creato per la prima volta il Activity , ad esempio nel callback onCreate() del Activity .
|
|
Utilizza onOrientationChanged() di OrientationEventListener .
All'interno del callback, aggiorna la rotazione del target dei casi d'uso.
|
Gestisce anche i casi in cui Activity non viene nuovamente creato quando il dispositivo ruota (ad esempio di 90 gradi). Questo accade su dispositivi con fattore di forma ridotto quando l'app occupa metà dello schermo e su dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
|
Orientamento configChanges sostituito |
Configura i casi d'uso una sola volta, quando viene creato per la prima volta il Activity , ad esempio nel callback onCreate() del Activity .
|
|
Utilizza onDisplayChanged() di DisplayListener . All'interno del callback, aggiorna la rotazione target dei casi d'uso, ad esempio quando il dispositivo viene ruotato di 180 gradi.
|
Gestisce anche i casi in cui Activity non viene nuovamente creato quando il dispositivo ruota (ad esempio di 90 gradi). Questo accade su dispositivi con fattore di forma ridotto quando l'app occupa metà dello schermo e su dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
Orientamento sbloccato
Un Activity
ha un orientamento sbloccato quando l'orientamento del display (ad esempio verticale o orizzontale) corrisponde all'orientamento fisico del dispositivo, con l'eccezione dell'orientamento verticale/orizzontale inverso, che alcuni dispositivi non supportano per impostazione predefinita. Per forzare la rotazione del dispositivo in tutti e quattro gli orientamenti, imposta la proprietà screenOrientation
di Activity
su fullSensor
.
In modalità multi-finestra, un dispositivo che non supporta l'orientamento ritratto/paesaggio capovolto per impostazione predefinita non ruoterà in questo modo, anche se la proprietà screenOrientation
è impostata su fullSensor
.
<!-- The Activity has an unlocked orientation, but might not rotate to reverse portrait/landscape in single-window mode if the device doesn't support it by default. --> <activity android:name=".UnlockedOrientationActivity" /> <!-- The Activity has an unlocked orientation, and will rotate to all four orientations in single-window mode. --> <activity android:name=".UnlockedOrientationActivity" android:screenOrientation="fullSensor" />
Orientamento bloccato
Un display ha un orientamento bloccato quando rimane nello stesso orientamento (ad esempio verticale o orizzontale) indipendentemente dall'orientamento fisico del dispositivo. Per farlo, specifica la proprietà screenOrientation
di un Activity
all'interno della relativa dichiarazione nel file AndroidManifest.xml
.
Quando l'orientamento del display è bloccato, il sistema non distrugge e rielabora il Activity
quando il dispositivo viene ruotato.
<!-- The Activity keeps a portrait orientation even as the device rotates. --> <activity android:name=".LockedOrientationActivity" android:screenOrientation="portrait" />
Modifiche alla configurazione dell'orientamento sostituite
Quando un Activity
sostituisce le modifiche alla configurazione dell'orientamento, il sistema non le distrugge e non le ricrea quando cambia l'orientamento fisico del dispositivo.
Tuttavia, il sistema aggiorna l'interfaccia utente in base all'orientamento fisico del dispositivo.
<!-- The Activity's UI might not rotate in reverse portrait/landscape if the device doesn't support it by default. --> <activity android:name=".OrientationConfigChangesOverriddenActivity" android:configChanges="orientation|screenSize" /> <!-- The Activity's UI will rotate to all 4 orientations in single-window mode. --> <activity android:name=".OrientationConfigChangesOverriddenActivity" android:configChanges="orientation|screenSize" android:screenOrientation="fullSensor" />
Configurazione dei casi d'uso della videocamera
Negli scenari descritti sopra, i casi d'uso della videocamera possono essere configurati al momento della prima creazione di Activity
.
Nel caso di un Activity
con orientamento sbloccato, questa configurazione viene eseguita ogni volta che il dispositivo viene ruotato, poiché il sistema distrugge e ricrea il Activity
in base alle variazioni di orientamento. Di conseguenza, i casi d'uso impostano la rotazione target in modo che corrisponda all'orientamento del display per impostazione predefinita ogni volta.
Nel caso di un Activity
con un'opzione di blocco dell'orientamento o che sostituisce le modifiche alla configurazione dell'orientamento, questa configurazione viene eseguita una sola volta, quando viene creato il Activity
.
class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val cameraProcessFuture = ProcessCameraProvider.getInstance(this) cameraProcessFuture.addListener(Runnable { val cameraProvider = cameraProcessFuture.get() // By default, the use cases set their target rotation to match the // display’s rotation. val preview = buildPreview() val imageAnalysis = buildImageAnalysis() val imageCapture = buildImageCapture() cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageAnalysis, imageCapture) }, mainExecutor) } }
Configurazione di OrientationEventListener
L'utilizzo di un OrientationEventListener
ti consente di aggiornare continuamente la rotazione di destinazione dei casi d'uso della fotocamera man mano che l'orientamento del dispositivo cambia.
class CameraActivity : AppCompatActivity() { private val orientationEventListener by lazy { object : OrientationEventListener(this) { override fun onOrientationChanged(orientation: Int) { if (orientation == ORIENTATION_UNKNOWN) { return } val rotation = when (orientation) { in 45 until 135 -> Surface.ROTATION_270 in 135 until 225 -> Surface.ROTATION_180 in 225 until 315 -> Surface.ROTATION_90 else -> Surface.ROTATION_0 } imageAnalysis.targetRotation = rotation imageCapture.targetRotation = rotation } } } override fun onStart() { super.onStart() orientationEventListener.enable() } override fun onStop() { super.onStop() orientationEventListener.disable() } }
Configurazione di DisplayListener
L'utilizzo di un DisplayListener
ti consente di aggiornare la rotazione target dei casi d'uso della videocamera in determinate situazioni, ad esempio quando il sistema non distrugge e ricrea il Activity
dopo la rotazione del dispositivo di 180 gradi.
class CameraActivity : AppCompatActivity() { private val displayListener = object : DisplayManager.DisplayListener { override fun onDisplayChanged(displayId: Int) { if (rootView.display.displayId == displayId) { val rotation = rootView.display.rotation imageAnalysis.targetRotation = rotation imageCapture.targetRotation = rotation } } override fun onDisplayAdded(displayId: Int) { } override fun onDisplayRemoved(displayId: Int) { } } override fun onStart() { super.onStart() val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager displayManager.registerDisplayListener(displayListener, null) } override fun onStop() { super.onStop() val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager displayManager.unregisterDisplayListener(displayListener) } }