mercoledì 30 novembre 2011

HowTo: usare la fotocamera su Android

Domanda: sto facendo una mia app per Android. Devo permettere all'utente di scattare una foto per farci poi alcune cose, come faccio?

Risposta: chiedi in prestito la camera ad Android.

Il concetto di base è quello di sfruttare il meccanismo degli Intent per chiedere al sistema di prestarci la sua gestione della fotocamera. Gli Intent sono una caratteristica veramente interessante di Android e chiunque decida di fare sul serio con questa piattaforma dovrebbe studiarseli per bene. Volendo comunque riassumere in due righe cosa sono e perché ci servono, possiamo dire che gli Intent sono lo strumento a disposizione delle applicazioni per esprimere la propria necessità di fare qualcosa (scattare una foto, mostrare una pagina web, vedere un video...), anche senza specificare il come farla. Di questo si delega il sistema stesso, che sceglie l'applicazione più consona a soddisfare l'esigenza.

La nostra esigenza, ovviamente, è scattare una foto e averla poi a disposizione. Una volta impostata la nostra Activity e l'azione che deve scatenare la richiesta, come pigiare un button, non ci resta che scrivere il poco codice che serve.

NB: prima di iniziare, preciso che tutto il codice qui indicato è esente da qualsiasi gestione delle eccezioni, anche da quelle comandate, in favore della leggibilità. Non è un grosso problema, dato che l'IDE (e Java) stesso vi obbligheranno a gestire le Exceptions necessarie.

private static final int CAMERA_REQUEST = 100; // un numero a nostro piacimento 
File tmpFotoFile = null;

private void launchCamera(){

   // Fase 1
   //

   tmpFotoFile = File.createTempFile("myappprefix", null);

   // Fase 2
   //

   Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
   cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tmpFotoFile));
   startActivityForResult(cameraIntent, CAMERA_REQUEST);
}

Come vedete il codice si compone di due fasi:
  1. dobbiamo creare un'area di memoria in cui salvare i dati della foto che andremo a scattare. Volendo potremmo anche creare un file fisico, ma per questo tutorial supponiamo che non vogliate (o possiate) scrivere sulla SDCard. Il nostro file temporaneo in memoria è gestito da tmpFotoFile.
  2. ora possiamo chiedere al sistema di lanciare la sua gestione della fotocamera. Per fare questo creiamo la giusta Intent, dicendogli che ci serve qualcosa per ottenere una ACTION_IMAGE_CAPTURE. Decoriamo ulteriormente la Intent con una informazione extra: l'output, caro Android, mettimelo per cortesia in tmpFotoFile. Come vedete è tutto estremamente generico, proprio perché adattabile anche ad altri contesti. Fatto questo, lanciamo la nuova Activity risultante dall'Intent specificata in modo da attenderne (e gestirne) il risultato.

A questo punto siamo già in grado di lanciare la gestione della fotocamera e scattare le foto. Alla chiusura dell'Activity della fotocamera, però, non abbiamo più il controllo sui dati della foto scattati. Per fare questo ci occorre gestire l'override di un metodo della classe base Activity, onActivityResult, intercettando il corretto codice di request. Ecco come:

byte[] bitmapdata;
ImageView preview;

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   
   if (requestCode == CAMERA_REQUEST) {
     Bitmap datifoto = android.provider.MediaStore.Images.Media.getBitmap(this.getContentResolver(), Uri.fromFile(tmpFotoFile));
     preview.setImageBitmap(datifoto);
    
     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     datifoto.compress(CompressFormat.JPEG, 70, bos); 
     bitmapdata = bos.toByteArray();
       
     tmpFotoFile.delete();
  }
}


Ci facciamo restituire la Bitmap leggendone i dati dal nostro file temporaneo. Con questa, volendo, andiamo ad impostare una ImageView che ci fornisce l'anteprima dell'immagine (la ImageView deve essere opportunamente presente nella nostra Activity e referenziata attraverso findViewById(...)).

La seconda cosa che vogliamo fare è ottenere una immagine jpeg compressa partendo dalla grossa bitmap ottenuta. Lo facciamo con le tre righe che seguono l'impostazione della preview, nelle quali specifichiamo il grado di compressione (70, nell'esempio) tenendo conto che 0 significa massima compressione e 100 massima qualità.

Infine, puliamo la memoria eliminando il nostro file temporaneo, che ci sembra sempre cosa buona e giusta.

Non ci resta altro da fare che aggiungere nel Manifest le uses-permission necessarie a far funzionare il tutto, ovvero:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
<uses-feature android:name="android.hardware.camera" />

Buone foto!

3 commenti:

  1. Ciao! ti ringrazio enormemente per questo tutorial, mi è stato utilissimo! Tuttavia ho una domanda da porti: se volessi avviare un'altra intent dopo aver scattato la foto, dove dovrei inserire il codice che crea il successivo intent? ti ringrazio molto!

    RispondiElimina
  2. Mmm, direi nella onActivityResult, a seguito della gestione della risposta, anche se non ho il codice sotto mano per provare.

    RispondiElimina
  3. Si, infatti è proprio così!Nella onActivityResult, dopo la gestione di tutto, basta dichiarare un oggetto Intent e gestirlo a proprio piacimento. Grazie ;)

    RispondiElimina