Questo argomento illustra come configurare i casi d'uso di CameraX all'interno della tua app per recuperare immagini con le informazioni sulla rotazione corrette, che si tratti del caso d'uso di ImageAnalysis
o di ImageCapture
. Così:
- Il
Analyzer
del caso d'usoImageAnalysis
dovrebbe ricevere frame con la rotazione corretta. - Il caso d'uso di
ImageCapture
dovrebbe scattare foto con la rotazione corretta.
Terminologia
Questo argomento utilizza la seguente terminologia, pertanto è importante comprendere il significato di ogni termine:
- Orientamento del display
- Indica quale lato del dispositivo si trova nella posizione più alta e può avere uno dei quattro valori seguenti: verticale, orizzontale, verticale o orizzontale invertito.
- Rotazione del display
- Si tratta del valore restituito da
Display.getRotation()
e rappresenta i gradi di rotazione del dispositivo in senso antiorario rispetto al suo orientamento naturale. - Rotazione target
- Rappresenta il numero di gradi attraverso i quali ruotare il dispositivo in senso orario per raggiungere il suo orientamento naturale.
Come determinare la rotazione target
I seguenti esempi mostrano come determinare la rotazione target di un dispositivo in base al suo orientamento naturale.
Esempio 1: orientamento naturale verticale
Esempio di dispositivo: Pixel 3 XL | |
---|---|
Orientamento naturale = verticale Rotazione della visualizzazione = 0 |
|
Orientamento naturale = verticale Rotazione della Rete Display = 90 |
Esempio 2: orientamento naturale orizzontale
Esempio di dispositivo: Pixel C | |
---|---|
Orientamento naturale = orizzontale Rotazione della visualizzazione = 0 |
|
Orientamento naturale = orizzontale Rotazione della Rete Display = 270 |
Rotazione dell'immagine
Quale estremità è? In Android, l'orientamento del sensore è definito come un valore costante, che rappresenta i gradi (0, 90, 180, 270) di rotazione del sensore dalla parte superiore del dispositivo quando quest'ultimo si trova in una posizione naturale. Per tutti i casi nei diagrammi, la rotazione delle immagini descrive come ruotare i dati in senso orario per apparire in posizione verticale.
I seguenti esempi mostrano quale dovrebbe essere la rotazione dell'immagine in base all'orientamento del sensore della fotocamera. Presuppongono inoltre che la rotazione target sia impostata sulla rotazione del display.
Esempio 1: il sensore è ruotato di 90 gradi
Esempio di dispositivo: Pixel 3 XL | |
---|---|
Rotazione del display = 0 |
|
Rotazione del display = 90 |
Esempio 2: il sensore è ruotato di 270 gradi
Esempio di dispositivo: Nexus 5X | |
---|---|
Rotazione del display = 0 |
|
Rotazione del display = 90 |
Esempio 3: il 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
Analisi di immagini
Il dispositivo Analyzer
di ImageAnalysis
riceve 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 adattarsi alla rotazione target di ImageAnalysis
. Nel contesto di un'app per Android, la rotazione target di ImageAnalysis
corrisponde in genere all'orientamento dello schermo.
Acquisizione immagine
Un callback è collegato 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
: richiamato quando l'immagine acquisita è stata archiviata correttamente nella località specificata daImageCapture.OutputFileOptions
. Le opzioni possono specificare unFile
, unOutputStream
o una località inMediaStore
.
La rotazione dell'immagine acquisita, indipendentemente dal formato (ImageProxy
, File
, OutputStream
, MediaStore Uri
), rappresenta i gradi di rotazione in base ai quali l'immagine acquisita deve essere ruotata in senso orario per soddisfare la rotazione target di ImageCapture
. Anche in questo caso, nel contesto di un'app per Android corrisponde in genere all'orientamento dello schermo.
È possibile recuperare la rotazione dell'immagine acquisita 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
aggrega un'immagine e le relative informazioni, inclusa la sua rotazione. Queste informazioni sulla rotazione rappresentano i gradi in cui l'immagine deve essere ruotata per corrispondere alla rotazione target del caso d'uso.
Linee guida relative alla rotazione target per Image Capture/ImageAnalysis
Poiché per impostazione predefinita molti dispositivi non ruotano in senso verticale o orizzontale invertito, alcune app per Android non supportano questi orientamenti. Il fatto che un'app la supporti o meno, cambia il modo in cui è possibile aggiornare la rotazione 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. La prima mostra come farlo supportando tutti e quattro gli orientamenti; la seconda gestisce solo gli orientamenti su cui ruota il dispositivo per impostazione predefinita.
Per scegliere quali linee guida seguire nella tua app:
Verifica se la videocamera della tua app
Activity
ha un orientamento bloccato, sbloccato o se sostituisce le modifiche alla configurazione dell'orientamento.Decidi se la fotocamera della tua app
Activity
deve gestire tutti e quattro gli orientamenti del dispositivo (verticale, verticale, orizzontale e orizzontale invertito) o se deve gestire solo gli orientamenti supportati dal dispositivo su cui è in esecuzione per impostazione predefinita.
Supporta tutti e quattro gli orientamenti
In questa tabella sono riportate alcune linee guida da seguire nei casi in cui il dispositivo non ruota per ruotare in verticale. Lo stesso può essere applicato ai dispositivi che non ruotano in orizzontale.
Scenario | Linee guida | Modalità finestra singola | Modalità schermo diviso multi-finestra |
---|---|---|---|
Orientamento sbloccato |
Configura i casi d'uso ogni
volta che viene creato il Activity , ad esempio nel callback
onCreate() di Activity .
|
||
Usa onOrientationChanged() di OrientationEventListener .
All'interno del callback, aggiorna la rotazione target dei casi d'uso. In questo modo vengono gestiti i casi in cui il sistema non ricrea l'elemento Activity anche dopo una modifica dell'orientamento, ad esempio quando il dispositivo viene ruotato di 180 gradi.
|
Consente anche di gestire il display con orientamento verticale inverso e, per impostazione predefinita, il dispositivo non ruota per ruotare in verticale. |
Gestisce anche i casi in cui Activity non viene ricreato quando il dispositivo ruota (ad esempio 90 gradi). Questo accade sui dispositivi con fattori di forma ridotti quando l'app occupa metà dello schermo e sui 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 è dritta quando il dispositivo è in modalità verticale inversa e il sistema può ricreare Activity ogni volta che il dispositivo viene ruotato di 90 gradi.
|
Non ha effetto sui dispositivi che non ruotano in modo da invertire l'orientamento verticale per impostazione predefinita. La modalità multi-finestra non è supportata quando il display è con l'orientamento verticale inverso. | |
Orientamento bloccato |
Configura i casi d'uso solo una volta, quando
Activity viene creato per la prima volta, ad esempio nel callback
onCreate() di Activity .
|
||
Usa onOrientationChanged() di OrientationEventListener .
All'interno del callback, aggiorna la rotazione target dei casi d'uso, ad eccezione dell'anteprima.
|
Gestisce anche i casi in cui Activity non viene ricreato quando il dispositivo ruota (ad esempio 90 gradi). Questo accade sui dispositivi con fattori di forma ridotti quando l'app occupa metà dello schermo e sui dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
||
Modifiche alla configurazione di orientamento sostituite |
Configura i casi d'uso solo una volta, quando
Activity viene creato per la prima volta, ad esempio nel callback
onCreate() di Activity .
|
||
Usa onOrientationChanged() di OrientationEventListener .
All'interno del callback, aggiorna la rotazione target dei casi d'uso.
|
Gestisce anche i casi in cui Activity non viene ricreato quando il dispositivo ruota (ad esempio 90 gradi). Questo accade sui dispositivi con fattori di forma ridotti quando l'app occupa metà dello schermo e sui dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
||
(Facoltativo) Imposta la proprietà screenOrientation dell'attività su fullSensor nel file AndroidManifest. | Consente di tenere l'interfaccia utente in posizione verticale quando il dispositivo è in verticale al contrario. | Non ha effetto sui dispositivi che non ruotano in modo da invertire l'orientamento verticale per impostazione predefinita. La modalità multi-finestra non è supportata quando il display ha un orientamento verticale inverso. |
Supporta solo gli orientamenti supportati dal dispositivo
Sono supportati solo gli orientamenti supportati dal dispositivo per impostazione predefinita (che possono o meno includere l'orientamento verticale/orizzontale inverso).
Scenario | Linee guida | Modalità schermo diviso multi-finestra |
---|---|---|
Orientamento sbloccato |
Configura i casi d'uso ogni
volta che viene creato il Activity , ad esempio nel callback
onCreate() di Activity .
|
|
Usa 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 ricreato quando il dispositivo ruota (ad esempio 90 gradi). Questo accade sui dispositivi con fattori di forma ridotti quando l'app occupa metà dello schermo e sui dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
|
Orientamento bloccato |
Configura i casi d'uso solo una volta, quando
Activity viene creato per la prima volta, ad esempio nel callback
onCreate() di Activity .
|
|
Usa onOrientationChanged() di OrientationEventListener .
All'interno del callback, aggiorna la rotazione target dei casi d'uso.
|
Gestisce anche i casi in cui Activity non viene ricreato quando il dispositivo ruota (ad esempio 90 gradi). Questo accade sui dispositivi con fattori di forma ridotti quando l'app occupa metà dello schermo e sui dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
|
Modifiche alla configurazione di orientamento sostituite |
Configura i casi d'uso solo una volta, quando
Activity viene creato per la prima volta, ad esempio nel callback
onCreate() di Activity .
|
|
Usa 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 ricreato quando il dispositivo ruota (ad esempio 90 gradi). Questo accade sui dispositivi con fattori di forma ridotti quando l'app occupa metà dello schermo e sui dispositivi più grandi quando l'app occupa due terzi dello schermo.
|
Orientamento sbloccato
Un Activity
è sbloccato quando il suo orientamento del display (ad esempio verticale o orizzontale) corrisponde all'orientamento fisico del dispositivo, ad eccezione del formato verticale/orizzontale invertito, 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 per impostazione predefinita la modalità Verticale/orizzontale invertita non ruoterà in modo da ruotare correttamente l'orientamento verticale/orizzontale, 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. A questo scopo, specifica una proprietà screenOrientation
di Activity
nella relativa dichiarazione nel file AndroidManifest.xml
.
Se l'orientamento del display è bloccato, il sistema non elimina né ricrea
Activity
durante la rotazione del dispositivo.
<!-- The Activity keeps a portrait orientation even as the device rotates. --> <activity android:name=".LockedOrientationActivity" android:screenOrientation="portrait" />
Modifiche alla configurazione di orientamento sostituite
Quando un Activity
sostituisce le modifiche alla configurazione dell'orientamento, il sistema non lo elimina né lo 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 delle videocamere possono essere configurati subito dopo la creazione di Activity
.
Nel caso di un oggetto Activity
con orientamento sbloccato, questa configurazione viene eseguita ogni volta che il dispositivo viene ruotato, poiché il sistema distrugge e ricrea il Activity
quando cambia l'orientamento. In questo modo, i casi d'uso impostano ogni volta la rotazione target in modo che corrisponda all'orientamento del display per impostazione predefinita.
Nel caso di un oggetto Activity
con orientamento bloccato o che sostituisce le modifiche alla configurazione dell'orientamento, questa configurazione viene eseguita una sola volta, quando viene creata per la prima volta l'elemento 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
target dei casi d'uso della fotocamera quando cambia l'orientamento del dispositivo.
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 elimina e ricrea il Activity
dopo che il dispositivo ruota 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) } }