Nota: questa pagina fa riferimento al pacchetto Camera2. A meno che la tua app non richieda funzionalità specifiche di basso livello di Camera2, ti consigliamo di utilizzare CameraX. Sia CameraX che Camera2 supportano Android 5.0 (livello API 21) e versioni successive.
Le fotocamere e le anteprime della fotocamera non hanno sempre lo stesso orientamento sui dispositivi Android.
Una fotocamera si trova in una posizione fissa su un dispositivo, indipendentemente dal fatto che si tratti di uno smartphone, un tablet o un computer. Quando l'orientamento del dispositivo cambia, cambia anche l'orientamento della videocamera.
Di conseguenza, le app per fotocamera generalmente presuppongono una relazione fissa tra l'orientamento del dispositivo e le proporzioni dell'anteprima della fotocamera. Quando uno smartphone è in orientamento verticale, si presume che l'anteprima della fotocamera sia più alta che larga. Quando lo smartphone (e la fotocamera) vengono ruotati in modalità Orizzontale, l'anteprima della fotocamera dovrebbe essere più larga che alta.
Tuttavia, queste ipotesi vengono messe in discussione da nuovi fattori di forma, come i dispositivi pieghevoli, e da modalità di visualizzazione come multi-finestra e multi-display. I dispositivi pieghevoli cambiano le dimensioni e le proporzioni del display senza cambiare l'orientamento. La modalità multi-finestra limita le app della fotocamera a una parte dello schermo, ridimensionando l'anteprima della fotocamera indipendentemente dall'orientamento del dispositivo. La modalità multi-display consente l'utilizzo di display secondari che potrebbero non avere lo stesso orientamento del display principale.
Orientamento della fotocamera
La definizione di compatibilità Android specifica che un sensore di immagine della fotocamera "DEVE essere orientato in modo che la dimensione lunga della fotocamera sia allineata alla dimensione lunga dello schermo. In altre parole, quando il dispositivo viene tenuto in orientamento orizzontale, le fotocamere DEVONO acquisire le immagini in orientamento orizzontale. Questo vale indipendentemente dall'orientamento naturale del dispositivo, ovvero si applica sia ai dispositivi con orientamento principale orizzontale sia a quelli con orientamento principale verticale."
La disposizione della fotocamera rispetto allo schermo massimizza l'area di visualizzazione del mirino della fotocamera in un'app della fotocamera. Inoltre, i sensori di immagine in genere generano i dati in proporzioni orizzontali, di cui 4:3 è la più comune.
L'orientamento naturale del sensore della fotocamera è orizzontale. Nella figura 1, il sensore della fotocamera anteriore (la fotocamera rivolta nella stessa direzione del display) è ruotato di 270 gradi rispetto allo smartphone per rispettare la definizione di compatibilità Android.
Per esporre la rotazione del sensore alle app, l'API camera2 include una costante SENSOR_ORIENTATION. Per la maggior parte degli smartphone e dei tablet, il dispositivo segnala un'orientamento del sensore di 270 gradi per le fotocamere anteriori e di 90 gradi (punto di vista dal retro del dispositivo) per le fotocamere posteriori, che allinea il bordo lungo del sensore con il bordo lungo del dispositivo. Le fotocamere dei laptop in genere segnalano un'orientamento del sensore di 0 o 180 gradi.
Poiché i sensori di immagine della fotocamera generano i dati (un buffer di immagini) nell'orientamento naturale del sensore (orizzontale), il buffer di immagini deve essere ruotato del numero di gradi specificato da SENSOR_ORIENTATION affinché l'anteprima della fotocamera venga visualizzata in verticale nell'orientamento naturale del dispositivo. Per le fotocamere anteriori, la rotazione è in senso antiorario; per le fotocamere posteriori, in senso orario.
Ad esempio, per la fotocamera anteriore nella figura 1, il buffer di immagini prodotto dal sensore della fotocamera ha il seguente aspetto:
L'immagine deve essere ruotata di 270 gradi in senso antiorario in modo che l'orientamento dell'anteprima corrisponda a quello del dispositivo:
Una fotocamera posteriore produrrebbe un buffer di immagini con lo stesso orientamento del buffer precedente, ma SENSOR_ORIENTATION è di 90 gradi. Di conseguenza, il buffer viene ruotato di 90 gradi in senso orario.
Rotazione del dispositivo
La rotazione del dispositivo è il numero di gradi di rotazione di un dispositivo rispetto al suo orientamento naturale. Ad esempio, uno smartphone in orientamento orizzontale ha una rotazione del dispositivo di 90 o 270 gradi, a seconda della direzione di rotazione.
Un buffer di immagini del sensore della fotocamera deve essere ruotato dello stesso numero di gradi della rotazione del dispositivo (oltre ai gradi di orientamento del sensore) affinché l'anteprima della fotocamera venga visualizzata in verticale.
Calcolo dell'orientamento
L'orientamento corretto dell'anteprima della fotocamera tiene conto dell'orientamento del sensore e della rotazione del dispositivo.
La rotazione complessiva del buffer di immagini del sensore può essere calcolata utilizzando la seguente formula:
rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360
dove sign è 1 per le fotocamere anteriori, -1 per le fotocamere posteriori.
Per le fotocamere anteriori, il buffer delle immagini viene ruotato in senso antiorario (rispetto all'orientamento naturale del sensore). Per le fotocamere posteriori, il buffer di immagini del sensore viene ruotato in senso orario.
L'espressione deviceOrientationDegrees * sign + 360 converte la rotazione del dispositivo
da senso antiorario a senso orario per le fotocamere posteriori (ad esempio,
convertendo 270 gradi in senso antiorario in 90 gradi in senso orario). L'operazione modulo scala il risultato a meno di 360 gradi (ad esempio, scala 540 gradi di rotazione a 180).
API diverse segnalano la rotazione del dispositivo in modo diverso:
Display#getRotation()fornisce la rotazione in senso antiorario del dispositivo (dal punto di vista dell'utente). Questo valore viene inserito nella formula precedente così com'è.OrientationEventListener#onOrientationChanged()restituisce la rotazione in senso orario del dispositivo (dal punto di vista dell'utente). Nega il valore da utilizzare nella formula precedente.
Fotocamere anteriori
Ecco il buffer di immagini prodotto dal sensore della fotocamera nella figura 4:
Il buffer deve essere ruotato di 270 gradi in senso antiorario per adattarsi all'orientamento del sensore (vedi Orientamento della fotocamera sopra):
Poi il buffer viene ruotato di altri 90 gradi in senso antiorario per tenere conto della rotazione del dispositivo, ottenendo l'orientamento corretto dell'anteprima della fotocamera nella figura 4:
Ecco la fotocamera ruotata a destra in orientamento orizzontale:
Ecco il buffer dell'immagine:
Il buffer deve essere ruotato di 270 gradi in senso antiorario per adattarsi all'orientamento del sensore:
Il buffer viene quindi ruotato di altri 270 gradi in senso antiorario per tenere conto della rotazione del dispositivo:
Fotocamere posteriori
Le fotocamere posteriori in genere hanno un orientamento del sensore di 90 gradi (visto dal retro del dispositivo). Quando si orienta l'anteprima della fotocamera, il buffer dell'immagine del sensore viene ruotato in senso orario in base alla rotazione del sensore (anziché in senso antiorario come le fotocamere anteriori), quindi il buffer dell'immagine viene ruotato in senso antiorario in base alla rotazione del dispositivo.
Ecco il buffer di immagini del sensore della fotocamera nella figura 12:
Il buffer deve essere ruotato di 90 gradi in senso orario per adattarsi all'orientamento del sensore:
Poi il buffer viene ruotato di 270 gradi in senso antiorario per tenere conto della rotazione del dispositivo:
Proporzioni
Le proporzioni del display cambiano quando cambia l'orientamento del dispositivo, ma anche quando i pieghevoli vengono aperti e chiusi, quando le finestre vengono ridimensionate in ambienti multi-finestra e quando le app si aprono su display secondari.
Il buffer di immagini del sensore della fotocamera deve essere orientato e ridimensionato in modo da corrispondere all'orientamento e alle proporzioni dell'elemento UI del mirino, poiché l'UI cambia dinamicamente l'orientamento, con o senza la modifica dell'orientamento del dispositivo.
Sui nuovi fattori di forma o in ambienti multi-finestra o multi-display, se la tua app presuppone che l'anteprima della fotocamera abbia lo stesso orientamento del dispositivo (verticale o orizzontale), l'anteprima potrebbe essere orientata o ridimensionata in modo errato o entrambe le cose.
Nella figura 16, l'applicazione ha erroneamente presupposto che il dispositivo fosse ruotato di 90 gradi in senso antiorario, quindi l'app ha ruotato l'anteprima della stessa quantità.
Nella figura 17, l'app non ha regolato le proporzioni del buffer di immagini per consentire la scalabilità corretta in base alle nuove dimensioni dell'elemento UI di anteprima della fotocamera.
Le app per fotocamera con orientamento fisso in genere presentano problemi sui pieghevoli e su altri dispositivi con schermi di grandi dimensioni, come i laptop:
Nella figura 18, la UI dell'app della fotocamera è laterale perché l'orientamento dell'app è limitato solo alla modalità verticale. L'immagine del mirino è orientata correttamente rispetto al sensore della fotocamera.
Modalità Ritratto con riquadro
Le app della fotocamera che non supportano la modalità multi-finestra (resizeableActivity="false") e limitano l'orientamento (screenOrientation="portrait" o screenOrientation="landscape") possono essere posizionate in modalità Ritratto con riquadro sui dispositivi con schermi grandi per orientare correttamente l'anteprima della fotocamera.
La modalità Ritratto con inset inserisce le app solo in modalità Ritratto (inset) con orientamento verticale, anche se le proporzioni del display sono orizzontali. Le app solo orizzontali vengono visualizzate in formato letterbox con orientamento orizzontale, anche se il formato del display è verticale. L'immagine della fotocamera viene ruotata per allinearsi alla UI dell'app, ritagliata in modo da corrispondere alle proporzioni dell'anteprima della fotocamera e poi ridimensionata per riempire l'anteprima.
La modalità Ritratto con riquadro viene attivata quando le proporzioni del sensore di immagine della fotocamera e le proporzioni dell'attività principale dell'applicazione non corrispondono.
Nella figura 19, l'app della fotocamera solo in modalità Ritratto è stata ruotata per visualizzare la UI in verticale sul display del laptop. L'app è in formato letterbox a causa della differenza di proporzioni tra l'app verticale e il display orizzontale. L'immagine di anteprima della fotocamera è stata ruotata per compensare la rotazione della UI dell'app (a causa della modalità Ritratto con inset) ed è stata ritagliata e ridimensionata per adattarsi all'orientamento verticale, riducendo il campo visivo.
Ruota, ritaglia, ridimensiona
La modalità Ritratto inserito viene richiamata per un'app della fotocamera solo in modalità Ritratto su un display con proporzioni orizzontali:
L'app è in formato letterbox con orientamento verticale:
L'immagine della fotocamera viene ruotata di 90 gradi per adattarsi al nuovo orientamento dell'app:
L'immagine viene ritagliata in base alle proporzioni dell'anteprima della fotocamera, quindi ridimensionata per riempire l'anteprima (il campo visivo viene ridotto):
Sui dispositivi pieghevoli, l'orientamento del sensore della fotocamera può essere verticale, mentre le proporzioni del display sono orizzontali:
Poiché l'anteprima della fotocamera viene ruotata per adattarsi all'orientamento del sensore, l'immagine è orientata correttamente nel mirino, ma l'app solo verticale è laterale.
La modalità Ritratto con riquadro deve solo applicare il letterbox all'app con orientamento verticale per orientare correttamente l'app e l'anteprima della fotocamera:
API
A partire da Android 12 (livello API 31), le app possono anche controllare esplicitamente la modalità Ritratto con inset tramite la proprietà SCALER_ROTATE_AND_CROP della classe CaptureRequest.
Il valore predefinito è SCALER_ROTATE_AND_CROP_AUTO, che consente al sistema di richiamare la modalità Ritratto con riquadro.
SCALER_ROTATE_AND_CROP_90 è il comportamento della modalità Ritratto con riquadro descritto in precedenza.
Non tutti i dispositivi supportano tutti i valori SCALER_ROTATE_AND_CROP. Per ottenere un elenco dei valori supportati, consulta CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES.
CameraX
La libreria Jetpack CameraX semplifica la creazione di un mirino della fotocamera che si adatta all'orientamento del sensore e alla rotazione del dispositivo.
L'elemento di layout PreviewView crea un'anteprima della fotocamera, regolando automaticamente l'orientamento del sensore, la rotazione del dispositivo e la scalabilità.
PreviewView mantiene le proporzioni dell'immagine della fotocamera applicando il tipo di scala FILL_CENTER, che centra l'immagine, ma potrebbe ritagliarla in modo che corrisponda alle dimensioni di PreviewView. Per applicare il letterbox all'immagine della videocamera, imposta il tipo di scala su FIT_CENTER.
Per scoprire le nozioni di base sulla creazione di un'anteprima della fotocamera con PreviewView, consulta Implementa un'anteprima.
Per un esempio completo di implementazione, consulta il repository CameraXBasic su GitHub.
CameraViewfinder
Analogamente al caso d'uso Anteprima, la libreria CameraViewfinder fornisce un insieme di strumenti per semplificare la creazione di un'anteprima della fotocamera. Non dipende da CameraX Core, quindi puoi integrarla facilmente nel tuo codebase Camera2 esistente.
Anziché utilizzare direttamente Surface, puoi utilizzare il widget CameraViewfinder per visualizzare il feed videocamera per Camera2.
CameraViewfinder utilizza internamente un TextureView o un SurfaceView per visualizzare il feed videocamera e applica le trasformazioni necessarie per visualizzare correttamente il mirino. Ciò comporta la correzione delle proporzioni,
della scala e della rotazione.
Per richiedere la superficie dall'oggetto CameraViewfinder, devi creare un
ViewfinderSurfaceRequest.
Questa richiesta contiene i requisiti per la risoluzione della superficie e le informazioni sul dispositivo della videocamera di CameraCharacteristics.
La chiamata a requestSurfaceAsync() invia la richiesta al provider della piattaforma, che è TextureView o SurfaceView, e riceve un ListenableFuture di Surface.
La chiamata a markSurfaceSafeToRelease() comunica al provider della superficie che quest'ultima non è necessaria e che le risorse correlate possono essere rilasciate.
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 è un approccio semplice per creare un'anteprima della fotocamera se l'anteprima non richiede l'elaborazione e non è animata.
SurfaceView ruota automaticamente il buffer di immagini del sensore della fotocamera in modo che corrisponda all'orientamento del display, tenendo conto sia dell'orientamento del sensore sia della rotazione del dispositivo.
Tuttavia, il buffer dell'immagine viene ridimensionato per adattarsi alle dimensioni SurfaceView senza
considerare le proporzioni.
Devi assicurarti che le proporzioni del buffer dell'immagine corrispondano alle proporzioni di SurfaceView, il che può essere ottenuto scalando i contenuti di SurfaceView nel metodo onMeasure() del componente:
(Il codice sorgente computeRelativeRotation() si trova in Rotazione relativa di seguito.)
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) }
Per maggiori dettagli sull'implementazione di SurfaceView come anteprima della fotocamera, consulta
Orientamenti della fotocamera.
TextureView
TextureView è meno efficiente di SurfaceView e richiede più lavoro, ma TextureView ti offre il massimo controllo dell'anteprima della fotocamera.
TextureView ruota il buffer dell'immagine del sensore in base all'orientamento del sensore, ma non gestisce la rotazione del dispositivo o la scalabilità dell'anteprima.
La scalabilità e la rotazione possono essere codificate in una trasformazione Matrix.
Per scoprire come scalare e ruotare correttamente un TextureView, consulta Supporta le superfici ridimensionabili nell'app Fotocamera.
Rotazione relativa
La rotazione relativa del sensore della fotocamera è la quantità di rotazione necessaria per allineare l'output del sensore della fotocamera all'orientamento del dispositivo.
La rotazione relativa viene utilizzata da componenti come SurfaceView e TextureView
per determinare i fattori di scalabilità x e y per l'immagine di anteprima. Viene utilizzato anche per specificare la rotazione del buffer dell'immagine del sensore.
Le classi CameraCharacteristics e Surface consentono di calcolare la rotazione relativa del sensore della fotocamera:
/** * 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 }
Metriche della finestra
Le dimensioni dello schermo non devono essere utilizzate per determinare le dimensioni del mirino della fotocamera; l'app della fotocamera potrebbe essere in esecuzione in una parte dello schermo, in modalità multi-finestra sui dispositivi mobili o in modalità libera su ChromeOS.
WindowManager#getCurrentWindowMetrics()
(aggiunto nel livello API 30) restituisce le dimensioni della finestra dell'applicazione anziché
quelle dello schermo. I metodi della libreria Jetpack WindowManager
WindowMetricsCalculator#computeCurrentWindowMetrics()
e WindowInfoTracker#currentWindowMetrics()
forniscono un supporto simile con compatibilità con le versioni precedenti fino al livello API 14.
Rotazione: 180°
Una rotazione di 180 gradi di un dispositivo (ad esempio, dall'orientamento naturale all'orientamento naturale capovolto) non attiva il callback onConfigurationChanged(). Di conseguenza, l'anteprima della videocamera potrebbe essere capovolta.
Per rilevare una rotazione di 180 gradi, implementa un
DisplayListener e controlla la rotazione del dispositivo con una chiamata
a Display#getRotation() nel
callback onDisplayChanged().
Risorse esclusive
Prima di Android 10, solo l'attività visibile in primo piano in un ambiente multi-finestra era nello stato RESUMED. Questo creava confusione negli utenti perché il sistema non forniva alcuna indicazione su quale attività fosse stata ripresa.
Android 10 (livello API 29) ha introdotto la ripresa multipla, in cui tutte le attività visibili sono nello stato RESUMED. Le attività visibili possono comunque entrare nello stato PAUSED
se, ad esempio, un'attività trasparente si trova sopra l'attività o
l'attività non è attivabile, ad esempio in modalità Picture in picture (consulta
Supporto di Picture in picture).
Un'applicazione che utilizza la fotocamera, il microfono o qualsiasi risorsa esclusiva o singleton
con livello API 29 o superiore deve supportare la ripresa multipla. Ad esempio, se tre attività riprese vogliono utilizzare la fotocamera, solo una può accedere a questa risorsa esclusiva. Ogni attività deve implementare un callback onDisconnected() per rimanere informata sull'accesso preventivo alla fotocamera da parte di un'attività con priorità più alta.
Per ulteriori informazioni, consulta la sezione Ripresa multipla.
Risorse aggiuntive
- Per un esempio di Camera2, consulta l'app Camera2Basic su GitHub.
- Per scoprire di più sul caso d'uso dell'anteprima di CameraX, consulta Implementa un'anteprima di CameraX.
- Per un'implementazione di esempio dell'anteprima della fotocamera CameraX, consulta il repository CameraXBasic su GitHub.
- Per informazioni sull'anteprima della fotocamera su ChromeOS, consulta Orientamenti della fotocamera.
- Per informazioni sullo sviluppo per dispositivi pieghevoli, consulta Scopri di più sui pieghevoli.