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 relative anteprime non sono sempre nello stesso orientamento sui dispositivi Android.
Una videocamera è in una posizione fissa su un dispositivo, anche se quest'ultimo sia un telefono, un tablet o un computer. Quando l'orientamento del dispositivo cambia, cambia anche l'orientamento della fotocamera.
Di conseguenza, le app della fotocamera in genere presuppongono una relazione fissa tra l'orientamento del dispositivo e il formato dell'anteprima della fotocamera. Quando lo smartphone è in orientamento verticale, si presume che l'anteprima della fotocamera sia più alta che larga. Quando lo smartphone e la fotocamera sono ruotati in orizzontale, il l'anteprima della fotocamera deve essere più larga dell'altezza.
Tuttavia, questi presupposti sono messi in discussione da nuovi fattori di forma, come foldable dispositivi mobili, e le modalità di visualizzazione, multi-finestra e multidisplay. I dispositivi pieghevoli cambiano le dimensioni del display e le proporzioni senza cambiare orientamento. La modalità a più finestre 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 di usare display secondari che potrebbero non nello stesso orientamento del display principale.
Orientamento della fotocamera
La Definizione di compatibilità Android specifica che il sensore fotografico di una fotocamera "DEVE essere orientato in modo tale che il della fotocamera si allinea alla dimensione lunga dello schermo. Ovvero, quando il dispositivo è tenuto in orientamento orizzontale, le videocamere DEVONO acquisire immagini in l'orientamento orizzontale. Questo vale indipendentemente dall'orientamento naturale del dispositivo, ovvero si applica ai dispositivi con orientamento principale orizzontale e verticale."
La disposizione della fotocamera sullo schermo ingrandisce l'area di visualizzazione della videocamera mirino in un'app Fotocamera. Inoltre, i sensori immagine in genere inviano i loro dati proporzioni orizzontali, 4:3 è il formato più comune.

L'orientamento naturale del sensore della fotocamera è orizzontale. Nella Figura 1, il sensore della fotocamera anteriore (quella che punta nella stessa direzione del display) sia ruotato di 270 gradi rispetto allo smartphone per rispettare le Definizione di compatibilità Android.
Per esporre la rotazione del sensore alle app,
L'API camera2 include una
SENSOR_ORIENTATION
costante. Per la maggior parte degli smartphone e dei tablet, il dispositivo segnala l'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, allineando il bordo lungo
sensore con il bordo lungo del dispositivo. Le fotocamere dei laptop di solito segnalano
orientato del sensore di 0 o 180 gradi.
Poiché i sensori di immagine della fotocamera emettono i dati (un buffer di immagini) nell'orientamento naturale del sensore (orizzontale), il buffer di immagini deve essere ruotato per il 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 in figura 1, il buffer di immagini prodotto dal sensore della fotocamera è il seguente:

L'immagine deve essere ruotata di 270 gradi in senso antiorario per visualizzare l'anteprima l'orientamento corrisponde a quello del dispositivo:

Una fotocamera posteriore produrrebbe un buffer di immagine con lo stesso orientamento
come il buffer sopra, ma SENSOR_ORIENTATION
è 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 in cui un dispositivo viene ruotato rispetto alla sua naturalezza orientamento. Ad esempio, uno smartphone in orientamento orizzontale ha una rotazione del dispositivo di 90 o 270 gradi, a seconda del senso di rotazione.
Un buffer d'immagine del sensore della fotocamera deve essere ruotato dello stesso numero di gradi del la rotazione del dispositivo (oltre ai gradi di orientamento del sensore) per dell'anteprima della fotocamera in verticale.
Calcolo dell'orientamento
Il corretto orientamento dell'anteprima della fotocamera prende in considerazione il sensore l'orientamento e la rotazione del dispositivo.
La rotazione complessiva del buffer dell'immagine del sensore può essere calcolata utilizzando la seguente formula:
rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360
dove sign
è 1
per le fotocamere anteriori e -1
per le fotocamere posteriori.
Per le fotocamere anteriori, il buffer di immagine viene ruotato in senso antiorario (da l'orientamento naturale del sensore). Per le fotocamere posteriori, il sensore buffer di immagine ruotato in senso orario.
L'espressione deviceOrientationDegrees * sign + 360
converte la rotazione dei dispositivi
da antiorario a senso orario per le fotocamere posteriori (ad esempio,
conversione di 270 gradi in senso antiorario a 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 registrano la rotazione dei dispositivi in modo diverso:
Display#getRotation()
consente la rotazione in senso antiorario del dispositivo (dal punto di vista dell'utente). Questo valore si collega così com'è alla formula precedente.OrientationEventListener#onOrientationChanged()
restituisce la rotazione in senso orario del dispositivo (dal punto di vista dell'utente). Annulla il valore da utilizzare nella formula riportata sopra.
Fotocamere anteriori

Ecco il buffer di immagine prodotto dal sensore della fotocamera nella Figura 2:

Il buffer deve essere ruotato di 270 gradi in senso antiorario per regolare l'orientamento del sensore (vedi Orientamento della videocamera sopra):

Quindi il tampone viene ruotato di altri 90 gradi in senso antiorario per tiene conto della rotazione del dispositivo, determinando il corretto orientamento anteprima della fotocamera nella figura 2:

Ecco la fotocamera girata a destra per l'orientamento orizzontale:

Ecco il buffer dell'immagine:

Il buffer deve essere ruotato di 270 gradi in senso antiorario per regolare l'orientamento del sensore:

Quindi il buffer viene ruotato di altri 270 gradi in senso antiorario per tenere conto la rotazione del dispositivo:

Fotocamere posteriori
Le fotocamere posteriori in genere hanno un orientamento del sensore di 90 gradi (come visto dal retro del dispositivo). Quando orienta l'anteprima della fotocamera, il valore il buffer di immagine del sensore viene ruotato in senso orario della quantità di rotazione del sensore (anziché in senso antiorario come le fotocamere anteriori), quindi l'immagine il buffer viene ruotato in senso antiorario per lo stesso numero di rotazione del dispositivo.

Ecco il buffer di immagini del sensore della fotocamera nella figura 4:

Il buffer deve essere ruotato di 90 gradi in senso orario per regolare l'orientamento del sensore:

Il buffer viene quindi ruotato di 270 gradi in senso antiorario per tenere conto della rotazione del dispositivo:

Proporzioni
Le proporzioni del display cambiano anche quando cambia l'orientamento del dispositivo i pieghevoli si piegano e si aprono quando le finestre vengono ridimensionate in modalità multi-finestra ambienti e quando le app si aprono su display secondari.
Il buffer d'immagine del sensore della fotocamera deve essere orientato e scalato in modo da corrispondere orientamento e proporzioni dell'elemento UI del mirino come UI cambia l'orientamento in modo dinamico, con o senza cambio di dispositivo orientamento.
Su nuovi fattori di forma o in ambienti con più finestre o più display, se la tua app presuppone che l'anteprima della videocamera abbia lo stesso orientamento del dispositivo (ritratto o paesaggio), l'anteprima potrebbe essere orientata in modo errato, avere una scala errata o entrambe le cose.

Nella figura 5, l'applicazione ha ipotizzato erroneamente che il dispositivo fosse ruotato di 90 gradi gradi in senso antiorario; per cui l'app ha ruotato l'anteprima dello stesso numero.

Nella Figura 6, l'app non ha regolato le proporzioni del buffer di immagine per consente di ridimensionarla in modo adeguato alle nuove dimensioni dell'interfaccia utente di anteprima della fotocamera .
Le app della fotocamera con orientamento fisso di solito riscontrano problemi sui pieghevoli e Altri dispositivi con schermi di grandi dimensioni, come i laptop:

Nella figura 7, l'interfaccia utente dell'app della fotocamera è in verticale perché l'orientamento dell'app è limitato solo al formato verticale. L'immagine del mirino è orientata correttamente rispetto al sensore della fotocamera.
Inset in modalità Ritratto
App fotocamera che non supportano la modalità multi-finestra
(resizeableActivity="false"
)
e limitarne l'orientamento
(screenOrientation="portrait"
)
o screenOrientation="landscape"
)
può essere attivata in modalità Ritratto sui dispositivi con schermi grandi per orientarsi correttamente
l'anteprima della fotocamera.
Inserisci le app solo verticali in modalità Ritratto con bordi neri (inserizioni) in orientamento verticale anche se le proporzioni del display sono orizzontali. Le app solo in orizzontale hanno un formato letterbox con orientamento orizzontale anche se le proporzioni del display sono verticali. L'immagine della fotocamera viene ruotata per allinearla con l'interfaccia utente dell'app, ritagliata per adattarla alle proporzioni dell'anteprima della fotocamera e e ridimensionato per riempire l'anteprima.
La modalità Ritratto in primo piano viene attivata quando le proporzioni del sensore di immagine della fotocamera e le proporzioni dell'attività principale dell'applicazione non corrispondono.

Nella figura 8, l'app Fotocamera solo ritratto è stata ruotata per visualizzare l'interfaccia utente in verticale sul display del laptop. L'app è in formato letterbox a causa della differenza di proporzioni tra l'app verticale e la visualizzazione orizzontale. La fotocamera l'immagine di anteprima è stata ruotata per compensare la rotazione dell'interfaccia utente dell'app (a causa inserisci la modalità Ritratto) e l'immagine è stata ritagliata e ridimensionata per adattarsi verticale, per ridurre il campo visivo.
Ruota, ritaglia, ridimensiona
La modalità Ritratto inserita viene attivata per un'app della fotocamera solo verticale su un display con proporzioni orizzontali:

L'app è in letterbox con orientamento verticale:

L'immagine della fotocamera viene ruotata di 90 gradi per regolare il riorientamento della dell'app:

L'immagine viene ritagliata in base alle proporzioni dell'anteprima della fotocamera, quindi ridimensionata a riempi l'anteprima (il campo visivo è 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 regolare l'orientamento del sensore, l'immagine è orientata correttamente nel mirino, mentre l'app solo Ritratto è ruotata.
La modalità Ritratto inserita richiede l'inserimento dell'app in formato Lettera solo con orientamento verticale per orientare correttamente l'anteprima dell'app e della fotocamera:

API
A partire da Android 12 (livello API 31), le app possono anche controllare esplicitamente la modalità Ritratto con incasso 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à verticale integrata.
SCALER_ROTATE_AND_CROP_90
è il comportamento della modalità Ritratto in primo piano descritto sopra.
Non tutti i dispositivi supportano tutti i valori SCALER_ROTATE_AND_CROP
. Per ottenere un elenco
dei valori supportati,
CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES
FotocameraX
La raccolta Jetpack CameraX consente di creare un mirino della fotocamera che si adatti all'orientamento del sensore la rotazione dei dispositivi è un'attività semplice.
L'elemento di layout PreviewView
crea un'anteprima della fotocamera, regolando
automaticamente l'orientamento del sensore,
la rotazione e il ridimensionamento dei dispositivi. 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 modificare l'immagine della fotocamera in modalità letterbox, imposta il tipo di scala su
FIT_CENTER
Per conoscere le nozioni di base sulla creazione di un'anteprima della fotocamera con PreviewView
, consulta
Implementare un'anteprima.
Per un'implementazione di esempio completa, consulta il repository
CameraXBasic
su GitHub.
CameraViewfinder
Analogamente al caso d'uso dell'Anteprima, il CameraViewfinder offre una serie di strumenti per semplificare la creazione di un'anteprima della fotocamera. Non dipende da CameraX Core, quindi puoi integrarlo perfettamente nel tuo il codebase Camera2 esistente.
Invece di utilizzare
Surface
puoi utilizzare
CameraViewfinder
per visualizzare il feed videocamera per Camera2.
CameraViewfinder
utilizza internamente un TextureView
o un SurfaceView
per visualizzare il feed della 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 relativi alla risoluzione della superficie e al dispositivo della fotocamera
informazioni da CameraCharacteristics
.
La chiamata a requestSurfaceAsync()
invia la richiesta al fornitore della piattaforma, che può essere TextureView
o
SurfaceView
, e riceve un ListenableFuture
di Surface
.
Chiamata a markSurfaceSafeToRelease()
in corso...
comunica al fornitore della piattaforma che la piattaforma non è necessaria e correlata
possono essere svincolate.
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)) }
void startCamera(){ Size previewResolution = new Size(width, height); ViewfinderSurfaceRequest viewfinderSurfaceRequest = new ViewfinderSurfaceRequest(previewResolution, characteristics); ListenableFuture<Surface> surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest); Futures.addCallback(surfaceListenableFuture, new FutureCallback<Surface>() { @Override public void onSuccess(Surface result) { /* create a CaptureSession using this surface as usual */ } @Override public void onFailure(Throwable t) { /* something went wrong */} }, ContextCompat.getMainExecutor(context)); }
Visualizzazione di superficie
SurfaceView
è un
approccio diretto alla creazione dell'anteprima della fotocamera se quest'ultima non
richiedono l'elaborazione e non sono animati.
SurfaceView
ruota automaticamente il buffer d'immagine del sensore della fotocamera per adattarlo
l'orientamento del display, tenendo conto sia dell'orientamento del sensore che del dispositivo
la rotazione. Tuttavia, il buffer dell'immagine viene ridimensionato in base alle dimensioni SurfaceView
senza tenere conto delle proporzioni.
Devi assicurarti che le proporzioni del buffer immagine corrispondano alle proporzioni del SurfaceView
, il che puoi fare ridimensionando i contenuti del SurfaceView
nel metodo onMeasure()
del componente:
(il codice sorgente di computeRelativeRotation()
è 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) }
@Override void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees); if (previewWidth > 0f && previewHeight > 0f) { /* Scale factor required to scale the preview to its original size on the x-axis. */ float scaleX = (relativeRotation % 180 == 0) ? (float) width / previewWidth : (float) width / previewHeight; /* Scale factor required to scale the preview to its original size on the y-axis. */ float scaleY = (relativeRotation % 180 == 0) ? (float) height / previewHeight : (float) height / previewWidth; /* Scale factor required to fit the preview to the SurfaceView size. */ float finalScale = Math.min(scaleX, scaleY); setScaleX(1 / scaleX * finalScale); setScaleY(1 / scaleY * finalScale); } setMeasuredDimension(width, height); }
Per ulteriori dettagli sull'implementazione di SurfaceView
come anteprima della fotocamera, consulta
Orientamenti fotocamera.
TextureView
TextureView
ha un rendimento inferiore rispetto a SurfaceView
e richiede più lavoro, ma offre il massimo controllo sull'anteprima della fotocamera.
TextureView
ruota il buffer d'immagine del sensore in base all'orientamento, ma
non gestisce la rotazione del dispositivo o il ridimensionamento dell'anteprima.
La scalatura e la rotazione possono essere codificate in una trasformazione Matrix. Per scoprire come
scalare e ruotare correttamente un TextureView
, consulta
Supportare le superfici ridimensionabili nell'app Fotocamera
Rotazione relativa
La rotazione relativa del sensore della videocamera è il tempo di rotazione necessario per allinea 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 scala x e y per l'immagine di anteprima. È utilizzata anche per
specificare la rotazione del buffer immagine del sensore.
La
CameraCharacteristics
e
Le classi Surface
consentono il calcolo
Rotazione relativa del sensore della videocamera:
/** * 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)!! // 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 }
/** * 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 int computeRelativeRotation( CameraCharacteristics characteristics, int surfaceRotationDegrees ){ Integer sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); // Reverse device orientation for back-facing cameras. int sign = characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT ? 1 : -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 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 la dimensione della finestra dell'applicazione anziché
le dimensioni dello schermo. Metodi della libreria Jetpack WindowManager
WindowMetricsCalculator#computeCurrentWindowMetrics()
e
WindowInfoTracker#currentWindowMetrics()
offrono un supporto simile, con compatibilità con le versioni precedenti al livello API 14.
Rotazione di 180 gradi
Una rotazione di 180 gradi di un dispositivo (ad esempio, dall'orientamento naturale a
l'orientamento naturale al contrario) non attiva
onConfigurationChanged()
di Google. Di conseguenza, l'anteprima della fotocamera potrebbe essere capovolta.
Per rilevare una rotazione di 180 gradi, implementa una
DisplayListener
e controlla la rotazione del dispositivo con una chiamata
Display#getRotation()
nel
onDisplayChanged()
di Google.
Risorse esclusive
Prima di Android 10, solo l'attività visibile più in alto in un ambiente con più finestre era nello stato RESUMED
. Questo non era chiaro per gli utenti perché
il sistema non ha fornito alcuna indicazione di quale attività è 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 accedere all'PAUSED
ad esempio se, ad esempio, un'attività trasparente si trova sopra l'attività o
non sia possibile mettere a fuoco l'attività, ad esempio in modalità Picture in picture (vedi
supporto di Picture in picture).
Un'applicazione che utilizza la fotocamera, il microfono o qualsiasi altra funzione
la risorsa singleton con livello API 29 o successivo deve supportare la ripresa multipla. Per
Ad esempio, se vuoi usare la fotocamera per tre attività riprese, solo una potrà
per accedere a questa risorsa esclusiva. Ogni attività deve implementare un callback
onDisconnected()
per essere consapevole dell'accesso preventivo alla videocamera da parte di un'attività prioritaria superiore.
Per ulteriori informazioni, consulta Ripristino multiplo.
Risorse aggiuntive
- Per un esempio di Camera2, vedi l'app Camera2Basic su GitHub.
- Per informazioni sul caso d'uso dell'anteprima di CameraX, consulta CameraX Implementare un'anteprima.
- Per un'implementazione di esempio dell'anteprima della videocamera CameraX, consulta il repository CameraXBasic su GitHub.
- Per informazioni sull'anteprima della fotocamera su ChromeOS, consulta Orientamenti della fotocamera.
- Per informazioni sullo sviluppo per i dispositivi pieghevoli, consulta Scopri di più sui dispositivi pieghevoli.