DevCorner: salviamo una View come immagine

Roberto Orgiu
Roberto Orgiu
DevCorner: salviamo una View come immagine

In via del tutto straordinaria, ci siamo visti costretti a rimandare il DevCorner di questa settimana per motivi logistici e ci scusiamo con i nostri lettori per l'inconveniente.
Apriamo quindi le danze saltando i preamboli e annunciando l'argomento di questo appuntamento: nelle prossime righe, andremo ad occuparci del salvataggio come immagine di una qualsiasi View del nostro layout, con tutto il suo contenuto.

Riprendiamo quindi il tutorial di una delle precedenti puntate, per l'esattezza quella in cui abbiamo imparato a disegnare sullo schermo ed aggiungiamo un bottone che ci permetta di salvare il contenuto della View che creammo nella parte alta del layout.

LEGGI ANCHE: Impariamo a disegnare sullo schermo.

Per prima cosa, abbiamo bisogno di un tipo di dato da poter passare all'AsyncTask che gestirà il salvataggio, in modo tale da poter reperire facilmente la View che intendiamo salvare, il nome del file che vogliamo usare per l'esportazione dei contenuti e il callback che vogliamo eseguire una volta che i dati sono stati scritti nello stream di destinazione.

A tal proposito abbiamo quindi creato una classe ViewHolder, il cui semplice sorgente può essere visto e scaricato dal repository GitHub.

Una volta completato questo semplice ma fondamentale step, possiamo creare il task asincrono, il cui metodo doInBackground accetti in ingresso un vettore di ViewHolder, il primo dei quali conterrà tutti i dati necessari all'esecuzione del compito. Ritrovata quindi la View dal ViewHolder in posizione 0, possiamo estrarne la Bitmap: vediamo in dettaglio gli step necessari.

In primis, dobbiamo calcolare le dimensioni in pixel della View bersaglio con una leggera approssimazione

int widthSpec = View.MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(), View.MeasureSpec.AT_MOST);
int heightSpec = View.MeasureSpec.makeMeasureSpec(view.getMeasuredHeight(), View.MeasureSpec.AT_MOST);
view.measure(widthSpec, heightSpec);

Dopodiché, si passa ad impostare i parametri per la cattura del contenuto, ricordando che tutte le misure partono da 0 e dobbiamo quindi tenere conto dell'eventuale padding assegnato durante la fase di design del componente più esterno della View.

view.layout(view.getLeft(), view.getTop(),
view.getMeasuredWidth() + view.getLeft(),
view.getMeasuredHeight() + view.getTop());

Una volta impostati i parametri, creiamo la Bitmap

Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);

Ne deriviamo un Canvas e lo posizioniamo correttamente

Canvas c = new Canvas(bitmap);
c.translate(-view.getScrollX(), -view.getScrollY());

ed infine copiamo il contenuto della View all'interno dell'oggetto Canvas appena creato

view.draw(c);

A questo punto, possiamo salvare i dati in un file PNG e terminare l'esecuzione del nostro task, avviando il callback relativo alla View sul thread principale dell'applicazione. Andiamo però con ordine e cominciamo con l'apertura di uno stream su cui scrivere i dati

final FileOutputStream fos = new FileOutputStream(imagePath);

Il passo seguente è invece la compressione dell'immagine nel formato PNG tramite i metodi della classe Bitmap

bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);

Una volta impostata l'immagine nello stream, possiamo forzare lo svuotamento del buffer e chiudere quindi il file.

fos.flush();
fos.close();

Anche per questa settimana siamo giunti alla fine della nostra rubrica dedicata all programmazione: per ogni evenienza, sul nostro forum potrete trovare i link ai sorgenti e alla nuova versione dell'APK insieme alle risorse necessarie al completamento dei precedenti tutorial.

Rinnovandovi le nostre scuse per il ritardo di questa settimana, vi diamo appuntamento al prossimo episodio di DevCorner. Buona programmazione a tutti!