Come creare un grafico in Android usando la libreria jjoe64/GraphView

In questo breve tutorial vedremo come realizzare un grafico in Android Studio per i nostri dispositivi Ltouch, utilizzando la libreria esterna jjoe64/GraphView in pochi e semplici passaggi. L’esempio da noi proposto andrà a realizzare un grafico avente come asse delle ascisse il tempo trascorso e come asse delle ordinate l’andamento della temperatura. Quest’ultima data da una sonda di temperatura PT100 collegata al dispositivo tramite ingresso analogico ( nulla vieta che la comunicazione possa avvenire in Modbus RTU ). I dati vengono letti e scritti su di una tabella ( temperature_table ) all’interno di un database locale ( DB_CHART.db ) ma in questa guida ci occuperemo solo della lettura quindi consiglio la visione del seguente articolo .
Nel caso in cui un passaggio dovesse essere poco chiaro oppure abbiate bisogno di maggiori informazioni ( per quanto riguarda la realizzazione del grafico ) tutto il materiale da cui attingere è reperibile al seguente indirizzo.
Detto ciò, il primo passaggio prevede di implementare la dipendenza nel build.gradle tramite il seguente comando:

implementation 'com.jjoe64:graphview:4.2.2'

Nel secondo passaggio invece si dovrà aprire il file xml dell’attività ( Es. layout/activity_main.xml ) e aggiungere il tag per creare il grafico vero e proprio ( disporlo all’interno a piacimento ):

Nel terzo passaggio:
-Per prima cosa inizializziamo le variabli…

LineGraphSeries dataseries = new LineGraphSeries<>(new DataPoint[0]);
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy\nHH:mm:ss");
//Nell'onCreate()
mioGrafico = (GraphView) findViewById(R.id.TempChart);

-Dopo di che dando per scontato che il database si già popolato, utilizzo il metodo getAllData() contenuto nella classe DatabaseHelper in questo modo:

//Ottengo i vari DataPoint ( valoreX, valoreY ) da applicare in seguito al grafico
dataseries.resetData(mioDBHelper.getAllData());

Il metodo getAllData() non fa altro che un SELECT di tutti i dati all’interno della tabella ( temperature_table ) e crea un vettore DataPoint ( che è quello che poi restituisce ). Quest’ultimo contiene i vari punti del grafico ovvero l’associazione ( asseX / asseY ) dove per asseX si intende prendere il valore della riga la cui corrispondente colonna è rappresentata da Time ( in Unix timestamp ) mentre per asseY si intende prendere il valore della riga per cui la corrispondente colonna è rappresentata da Temperature:

/**
* Metodo per ottenere tutti i dati presenti all'interno della tabella temperature_table
* ( anche se alla fine tengo conto solo delle colonne Time e Temperature
* @return : DataPoint[]
*/
public DataPoint[] getAllData(){
//Ottengo il DB in modalità lettura
SQLiteDatabase mioDB = this.getReadableDatabase();
//Creo un oggetto Cursor per selezionare tutti i dati all'interno della tabella
Cursor mioCursor = mioDB.rawQuery("SELECT * FROM "+TABLE_name,null);
//Istanzio DataPoint di dimensione pari a mioCursore.getCount() ( ovvero pari al numero di
// righe )
DataPoint[] mieiDataPoint = new DataPoint[mioCursor.getCount()];
//Con questo ciclo creo i vari datapoint (x,y) e prendo min e max di entrambi gli assi
for(int i = 0; i < mioCursor.getCount(); i++){
mioCursor.moveToNext();
if (i == 0){
MinX = mioCursor.getInt(mioCursor.getColumnIndex("Time"));
MinY = mioCursor.getDouble(mioCursor.getColumnIndex("Temperature"));
}
if(i == (mioCursor.getCount()-1)){
MaxX = mioCursor.getInt(mioCursor.getColumnIndex("Time"));
MaxY = mioCursor.getDouble(mioCursor.getColumnIndex("Temperature"));
}
mieiDataPoint[i] = new DataPoint(mioCursor.getInt(mioCursor.getColumnIndex("Time")),mioCursor.getDouble(mioCursor.getColumnIndex("Temperature")));
}
//Chiudo il db ( aperto in modalità lettura )
mioDB.close();
return mieiDataPoint;
}

A questo punto non rimane altro da fare che il seguente comando per caricare DataPoint[] all’interno del grafico

mioGrafico.addSeries(dataseries);

Nel caso in cui si voglia ottenere un qualcosa di più elaborato propongo di seguito un’altro esempio

//Imposto colore della linea del grafico
dataseries.setColor(Color.GREEN);
//Imposto se mettere o meno i punti ( in questo caso si )
dataseries.setDrawDataPoints(true);
//Imposto il raggio dei punti
dataseries.setDataPointsRadius(5);
//Imposto la grossezza della linea
dataseries.setThickness(3);
/*
Per settare maualmente l'asse y...
mioGrafico.getViewport().setYAxisBoundsManual(true);
mioGrafico.getViewport().setMinY(mioDBHelper.getMinY());
mioGrafico.getViewport().setMaxY(mioDBHelper.getMaxY())
*/
//Setto manualmente l'asse x
mioGrafico.getViewport().setXAxisBoundsManual(true);
//Specifico valore minimo asse x
mioGrafico.getViewport().setMinX(mioDBHelper.getMinX());
//Specifico valore massimo asse x
mioGrafico.getViewport().setMaxX(mioDBHelper.getMaxX());
//Do la possibilità di fare lo zoom e lo scroll
mioGrafico.getViewport().setScalable(true);
mioGrafico.getGridLabelRenderer().setHumanRounding(false); //In quanto uso data e ora come asse delle x
//Aggiungo al grafico i DataPoint
mioGrafico.addSeries(dataseries);

Il risultato ottenuto dovrebbe essere simile a quello in figura:

Come potrete notare manca solo un’ultimo passaggio ovvero la conversione Unix timestamp in data e ora comprensibili. La libreria ci viene in contro fornendoci gli strumenti per cambiare manualmente le label degli assi quindi basterà utilizzare quelli per raggiungere il nostro scopo.

mioGrafico.getGridLabelRenderer().setLabelFormatter(new DefaultLabelFormatter(){
@Override
public String formatLabel(double value, boolean isValueX) {
if(isValueX){
//Come mostrare i valori dell'asse x
//Ora legale europa centrale
sdf.setTimeZone(TimeZone.getTimeZone("GMT+2"));
return sdf.format(new Date((long) value * 1000L));
}else{
//Come mostrare i valori dell'asse y
return super.formatLabel(value,isValueX);
}
}
});

Da non dimenticare la creazione dell’oggetto sdf ( SimpleDataFormat ):

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy\nHH:mm:ss");


Con questa guida è tutto, se l’articolo vi è piaciuto condividete e restate sintonizzati per non perdervi alti tutorial sul mondo Android.