DevCorner: generiamo un QR Code... offline!

Roberto Orgiu
Roberto Orgiu
DevCorner: generiamo un QR Code... offline!

Abbiamo visto in precedenza come creare un QR code tramite le Chart API di Google e caricare il risultato in maniera asincrona in una ImageView ma sappiamo bene che, per quanto diffusa, la copertura della rete internet è tutt'altro che perfetta e continua: solamente il cambio di protocollo da H a 3G può generare diversi problemi alle richieste di rete più lunghe. Possiamo quindi evitare di incappare in questi problemi generando offline il contenuto di cui abbiamo bisogno?

La risposta, come potete immaginare, è positiva ed eccoci quindi pronti a generare un QR Code tramite le librerie ZXing, le stesse su cui si basano la maggior parte degli scanner presenti sul Play Store.

LEGGI ANCHE: Generare un QR Code tramite le Chart API di Google.

Innanzitutto, abbiamo bisogno di aggiungere la libreria al progetto, e lo facciamo aggiungendo la riga seguente al file build.gradle del modulo relativo all'app che stiamo scrivendo

compile 'com.google.zxing:core:3.0.1'

Sincronizzato il file di Gradle, possiamo utilizzare le librerie ed ottenere un'applicazione simile alla seguente.

QR-codeGenerator

Evitiamo quindi tutta la procedura relativa all'immissione del testo, che i nostri più fedeli lettori sapranno come implementare, e concentriamoci sulla generazione del QR Code. Purtroppo non ci sono metodi che ci ritornino un'immagine già pronta ma, attraverso semplici operazioni matematiche, potremo trasporre il risultato in un oggetto di tipo Bitmap che potremo poi disegnare sull'ImageView finale.

Iniziamo quindi definendo la dimensione massima del QRCode

final int qrDimension = 800;

e dichiarando l'oggetto principale della codifica

QRCodeWriter qrCodeWriter = new QRCodeWriter();

A questo punto, possiamo tentare di generare la matrice che conterrà il codice, estraendone, in caso di successo, il numero di righe e di colonne

final BitMatrix bitMatrix = qrCodeWriter.encode(message, BarcodeFormat.QR_CODE, qrDimension, qrDimension);
final int matrixHeight = bitMatrix.getHeight();
final int matrixWidth = bitMatrix.getWidth();

Dobbiamo quindi occuparci della trasposizione della matrice in vettore e lo faremo usando una tecnica simile a quella usata dal linguaggio C per accedere agli elementi della struttura dati: supponiamo di avere una matrice 3x2.

In memoria, tale matrice non conserverà la sua forma, ma alla prima colonna verrà accodata la seconda (e così via), in modo da avere un vettore, le cui colonne vengono divise grazie all'offset derivato dall'altezza stessa della matrice.

Prendiamo ad esempio la seguente matrice:

a1  a3
a2  a4

In memoria, vedremo un solo vettore con la seguente forma

a1
a2
a3
a4

Allo stesso modo convertiremo la matrice ottenuta, inserendo nel vettore il colore che vogliamo a seconda che si tratti di un bit da colorare (nominato BLACK, anche se nella demo è azzurro) o da lasciare bianco (WHITE):

int[] pixels = new int[matrixWidth * matrixHeight];
for (int y = 0; y < matrixHeight; y++) {
  final int offset = y * matrixWidth;
  for (int x = 0; x < matrixWidth; x++) {
    pixels[offset + x] = bitMatrix.get(x, y) ? BLACK : WHITE;
  }
 }

Ora che in pixels abbiamo il QR Code, possiamo creare una Bitmap delle dimensioni giuste (ovvero quelle della vecchia matrice)

Bitmap bitmapQRCode = Bitmap.createBitmap(matrixWidth, matrixHeight, Bitmap.Config.ARGB_8888);

Come ultimo passo, possiamo impostare all'interno della Bitmap i pixel che abbiamo "colorato" all'interno del vettore pixels e disegnarla all'interno dell'ImageView

bitmapQRCode.setPixels(pixels, 0, matrixWidth, 0, 0, matrixWidth, matrixHeight);
imgQR.setImageBitmap(bitmapQRCode);

Eccoci quindi al risultato di questo DevCorner: come ogni settimana, potete trovare sul nostro forum tutte le risorse necessarie allo svolgimento di questo episodio. Buona programmazione a tutti!