In questa lezione, spieghiamo come controllare direttamente l'hardware della fotocamera utilizzando le API del framework.
Nota: questa pagina fa riferimento alla classe Camera, che è stata ritirata. Ti consigliamo di utilizzare CameraX o, per casi d'uso specifici, Camera2. Sia CameraX che Camera2 supportano Android 5.0
(livello API 21) e versioni successive.
Per scoprire come eseguire la migrazione da Camera1 a CameraX, consulta
Esegui la migrazione da Camera1 a CameraX.
In alternativa, puoi utilizzare la
competenza agentica per la migrazione da Camera1 a CameraX.
Il controllo diretto della fotocamera di un dispositivo richiede molto più codice rispetto alla richiesta di foto o video da applicazioni della fotocamera esistenti. Tuttavia, se vuoi creare un'applicazione per fotocamera specializzata o qualcosa di completamente integrato nell'interfaccia utente della tua app, questa lezione ti mostra come fare.
Consulta le seguenti risorse correlate:
Apri l'oggetto Camera
Il primo passaggio per controllare direttamente la fotocamera è ottenere un'istanza dell'oggetto Camera. Come avviene per l'applicazione Fotocamera di Android, il modo consigliato per accedere alla fotocamera è aprire Camera su un thread separato avviato da onCreate(). Questo approccio è una buona idea, poiché l'operazione può richiedere del tempo e potrebbe rallentare il thread dell'interfaccia utente. In un'implementazione più semplice, l'apertura della fotocamera può essere posticipata al metodo onResume() per facilitare il riutilizzo del codice e mantenere semplice il flusso di controllo.
La chiamata a Camera.open() genera un'eccezione se la fotocamera è già in uso da un'altra applicazione, quindi la inseriamo in un blocco try.
Kotlin
private fun safeCameraOpen(id: Int): Boolean { return try { releaseCameraAndPreview() mCamera = Camera.open(id) true } catch (e: Exception) { Log.e(getString(R.string.app_name), "failed to open Camera") e.printStackTrace() false } } private fun releaseCameraAndPreview() { preview?.setCamera(null) mCamera?.also { camera -> camera.release() mCamera = null } }
Java
private boolean safeCameraOpen(int id) { boolean qOpened = false; try { releaseCameraAndPreview(); camera = Camera.open(id); qOpened = (camera != null); } catch (Exception e) { Log.e(getString(R.string.app_name), "failed to open Camera"); e.printStackTrace(); } return qOpened; } private void releaseCameraAndPreview() { preview.setCamera(null); if (camera != null) { camera.release(); camera = null; } }
A partire dal livello API 9, il framework della fotocamera supporta più fotocamere. Se utilizzi l'API legacy e chiami open() senza un argomento, ottieni la prima fotocamera posteriore.
Crea l'anteprima della fotocamera
Per scattare una foto, in genere gli utenti devono visualizzare un'anteprima del soggetto prima di fare clic
sull'otturatore. A questo scopo, puoi utilizzare un SurfaceView per disegnare anteprime di ciò che il sensore della fotocamera sta rilevando.
Classe di anteprima
Per iniziare a visualizzare un'anteprima, devi utilizzare la classe Preview. L'anteprima richiede un'implementazione dell'interfaccia android.view.SurfaceHolder.Callback, che viene utilizzata per trasmettere i dati delle immagini dall'hardware della fotocamera all'applicazione.
Kotlin
class Preview( context: Context, val surfaceView: SurfaceView = SurfaceView(context) ) : ViewGroup(context), SurfaceHolder.Callback { var mHolder: SurfaceHolder = surfaceView.holder.apply { addCallback(this@Preview) setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } ... }
Java
class Preview extends ViewGroup implements SurfaceHolder.Callback { SurfaceView surfaceView; SurfaceHolder holder; Preview(Context context) { super(context); surfaceView = new SurfaceView(context); addView(surfaceView); // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. holder = surfaceView.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } ... }
La classe di anteprima deve essere passata all'oggetto Camera prima che l'anteprima dell'immagine in tempo reale possa essere avviata, come mostrato nella sezione successiva.
Imposta e avvia l'anteprima
Un'istanza della fotocamera e la relativa anteprima devono essere create in un ordine specifico, con l'oggetto della fotocamera per primo. Nello snippet seguente, il processo di inizializzazione della fotocamera è incapsulato in modo che Camera.startPreview() venga chiamato dal metodo setCamera() ogni volta che l'utente esegue un'azione per cambiare la fotocamera. L'anteprima deve essere riavviata anche nel metodo di callback surfaceChanged() della classe di anteprima.
Kotlin
fun setCamera(camera: Camera?) { if (mCamera == camera) { return } stopPreviewAndFreeCamera() mCamera = camera mCamera?.apply { mSupportedPreviewSizes = parameters.supportedPreviewSizes requestLayout() try { setPreviewDisplay(holder) } catch (e: IOException) { e.printStackTrace() } // Important: Call startPreview() to start updating the preview // surface. Preview must be started before you can take a picture. startPreview() } }
Java
public void setCamera(Camera camera) { if (mCamera == camera) { return; } stopPreviewAndFreeCamera(); mCamera = camera; if (mCamera != null) { List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes(); supportedPreviewSizes = localSizes; requestLayout(); try { mCamera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); } // Important: Call startPreview() to start updating the preview // surface. Preview must be started before you can take a picture. mCamera.startPreview(); } }
Modifica impostazioni fotocamera
Le impostazioni della fotocamera modificano il modo in cui la fotocamera scatta le foto, dal livello di zoom alla compensazione dell'esposizione. Questo esempio modifica solo le dimensioni dell'anteprima; per saperne di più, consulta il codice sorgente dell'applicazione Fotocamera.
Kotlin
override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { mCamera?.apply { // Now that the size is known, set up the camera parameters and begin // the preview. parameters?.also { params -> params.setPreviewSize(previewSize.width, previewSize.height) requestLayout() parameters = params } // Important: Call startPreview() to start updating the preview surface. // Preview must be started before you can take a picture. startPreview() } }
Java
@Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Now that the size is known, set up the camera parameters and begin // the preview. Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(previewSize.width, previewSize.height); requestLayout(); mCamera.setParameters(parameters); // Important: Call startPreview() to start updating the preview surface. // Preview must be started before you can take a picture. mCamera.startPreview(); }
Imposta l'orientamento dell'anteprima
La maggior parte delle applicazioni della fotocamera blocca il display in modalità Orizzontale perché è l'orientamento naturale del sensore della fotocamera. Questa impostazione non ti impedisce di scattare foto in modalità Ritratto, perché l'orientamento del dispositivo viene registrato nell'intestazione EXIF. Il metodo setCameraDisplayOrientation() ti consente di modificare
la modalità di visualizzazione dell'anteprima senza influire sulla modalità di registrazione dell'immagine. Tuttavia, in Android prima del livello API 14, devi interrompere l'anteprima prima di modificare l'orientamento e poi riavviarla.
Scatta una foto
Utilizza il metodo Camera.takePicture() per scattare una foto dopo l'avvio dell'anteprima. Puoi creare oggetti Camera.PictureCallback e Camera.ShutterCallback e passarli a Camera.takePicture().
Se vuoi acquisire immagini continuamente, puoi creare un Camera.PreviewCallback che implementa onPreviewFrame(). Per una via di mezzo, puoi acquisire solo i frame di anteprima selezionati o configurare un'azione ritardata per chiamare takePicture().
Riavvia l'anteprima
Dopo aver scattato una foto, devi riavviare l'anteprima prima che l'utente possa scattare un'altra foto. In questo esempio, il riavvio viene eseguito sovraccaricando il pulsante di scatto.
Kotlin
fun onClick(v: View) { previewState = if (previewState == K_STATE_FROZEN) { camera?.startPreview() K_STATE_PREVIEW } else { camera?.takePicture(null, rawCallback, null) K_STATE_BUSY } shutterBtnConfig() }
Java
@Override public void onClick(View v) { switch(previewState) { case K_STATE_FROZEN: camera.startPreview(); previewState = K_STATE_PREVIEW; break; default: camera.takePicture( null, rawCallback, null); previewState = K_STATE_BUSY; } // switch shutterBtnConfig(); }
Interrompi l'anteprima e rilascia la fotocamera
Una volta che l'applicazione ha finito di utilizzare la fotocamera, è il momento di eseguire la pulizia. In particolare, devi rilasciare l'oggetto Camera, altrimenti rischi di causare l'arresto anomalo di altre applicazioni, incluse le nuove istanze della tua applicazione.
Quando devi interrompere l'anteprima e rilasciare la fotocamera? La distruzione della superficie di anteprima è un buon indizio del fatto che è il momento di interrompere l'anteprima e rilasciare la fotocamera, come mostrato in questi metodi della classe Preview.
Kotlin
override fun surfaceDestroyed(holder: SurfaceHolder) { // Surface will be destroyed when we return, so stop the preview. // Call stopPreview() to stop updating the preview surface. mCamera?.stopPreview() } /** * When this function returns, mCamera will be null. */ private fun stopPreviewAndFreeCamera() { mCamera?.apply { // Call stopPreview() to stop updating the preview surface. stopPreview() // Important: Call release() to release the camera for use by other // applications. Applications should release the camera immediately // during onPause() and re-open() it during onResume()). release() mCamera = null } }
Java
@Override public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return, so stop the preview. if (mCamera != null) { // Call stopPreview() to stop updating the preview surface. mCamera.stopPreview(); } } /** * When this function returns, mCamera will be null. */ private void stopPreviewAndFreeCamera() { if (mCamera != null) { // Call stopPreview() to stop updating the preview surface. mCamera.stopPreview(); // Important: Call release() to release the camera for use by other // applications. Applications should release the camera immediately // during onPause() and re-open() it during onResume()). mCamera.release(); mCamera = null; } }
In precedenza nella lezione, questa procedura faceva parte anche del metodo setCamera(), quindi l'inizializzazione di una fotocamera inizia sempre con l'interruzione dell'anteprima.