GraphLib: una libreria Android open source per grafici

Grafici e diagrammi di dati sono strumenti meravigliosi per illustrare le relazioni, rappresentare le tendenze dei dati e monitorare gli obiettivi nelle applicazioni Android. L'ho visto di persona diversi anni fa, quando un mio ex studente ha vinto il primo posto in un concorso per app mobile per studenti sponsorizzato dalla Charleston Defense Contractors Association. Una caratteristica fondamentale dell'app vincitrice, "Diabetes and Me", era la capacità di rappresentare graficamente i livelli giornalieri di zucchero.

Come altro esempio, si consideri un'applicazione per il monitoraggio del peso che traccia i progressi rispetto a un obiettivo di peso. La figura 1 illustra come potrebbe apparire un'applicazione di questo tipo su un telefono Android. La figura utilizza un grafico a linee rosse per mostrare i pesi mensili medi per l'anno 2017. Mostra il peso obiettivo come una linea retta verde vicino al fondo. (Sebbene i valori dei dati mostrati nel grafico a linee siano ipotetici, sono realistici e appartengono all'autore di questo articolo.)

John I. Moore

In questo articolo userò la mia libreria open source, GraphLib, per dimostrare le basi della rappresentazione grafica delle funzioni matematiche in Android. Non è la stessa libreria di grafici che il mio studente ha usato per la sua applicazione. In effetti, è molto più semplice e facile da usare.

scarica Scarica GraphLib Ottieni il codice sorgente per la libreria grafica Android open source introdotta in questo articolo. Creato da John I. Moore.

Panoramica di GraphLib

GraphLibconsiste di un'interfaccia e otto classi. Tre di queste classi sono interne alla libreria e hanno solo accesso ai pacchetti, quindi non sarà necessario comprenderle per utilizzare GraphLib. Due delle classi rimanenti hanno funzionalità molto semplici e le restanti non sono difficili da raccogliere.

Di seguito descriverò l'interfaccia GraphLib e ciascuna delle sue otto classi. Si noti che ho utilizzato le funzionalità di Java 8 come le interfacce funzionali e le espressioni lambda per sviluppare e testare la libreria, ma è relativamente semplice modificare queste funzionalità per le versioni precedenti di Java.

L'interfaccia funzionale di GraphLib

Come mostrato nel Listato 1, l'interfaccia Functionha un solo metodo astratto ed è, quindi, un'interfaccia funzionale. Notare che questa interfaccia è più o meno equivalente a Java 8 DoubleUnaryOperator, che si trova nel pacchetto java.util.function. La differenza è che Functionnon utilizza alcuna funzionalità di Java 8 diversa dall'annotazione @FunctionalInterface. La rimozione di questa annotazione è l'unica modifica necessaria per rendere l' Functioninterfaccia compatibile con le versioni precedenti di Java.

Listato 1. Funzione interfaccia

 package com.softmoore.android.graphlib; @FunctionalInterface public interface Function { public double apply(double x); } 

Imparare a conoscere le espressioni lambda

Le espressioni lambda, note anche come chiusure, letterali di funzione o semplicemente lambda, descrivono un insieme di funzionalità definite in JSR (Java Specification Request) 335. In una sezione dell'ultima versione del Java Tutorial vengono fornite introduzioni meno formali alle espressioni lambda; nell'articolo JavaWorld "Programmazione Java con espressioni lambda" e in un paio di articoli di Brian Goetz, "State of the lambda" e "State of the lambda: Libraries edition".

Classi GraphLib

Classi Pointe Labelsono relativamente semplici: Pointincapsula una coppia di valori double che rappresentano un punto nel piano x, y , e Labelincapsula un valore double e una stringa, dove il valore double rappresenta un punto su un asse e la stringa viene utilizzata per etichettarlo punto. L'esempio nella Figura 1 utilizza i punti per descrivere il grafico a linee e le etichette per l'asse in basso, che mostrano le abbreviazioni di una lettera per i mesi. Fornirò più esempi che illustrano l'uso di queste classi più avanti nell'articolo.

Le classi GraphFunction, GraphPointse ScreenPointnon sono solo molto semplici, sono anche interne alla libreria e hanno solo accesso ai pacchetti. Non hai davvero bisogno di capire queste classi per usare la libreria, quindi le descriverò solo brevemente qui:

  • GraphFunctionincapsula una funzione (cioè una classe che implementa l'interfaccia Function) e un colore usato per disegnare quella funzione.
  • GraphPointsincapsula un elenco di punti insieme a un colore utilizzato per tracciarli. Questa classe viene utilizzata internamente sia per tracciare punti che per disegnare grafici a linee.
  • ScreenPointincapsula una coppia di valori interi che rappresentano le coordinate dei pixel sullo schermo di un dispositivo Android. Questa classe è simile ma più semplice della classe Android Pointnel pacchetto android.graphics.

Ho fornito il codice sorgente per queste classi nel caso foste interessati ai dettagli.

Le tre classi rimanenti nella libreria GraphLib sono Graph, Graph.Buildere GraphView. È importante capire il ruolo che ognuno di loro svolge in un'applicazione Android.

La classe Graphcontiene informazioni su colori, punti, etichette, grafici, ecc. Da disegnare, ma è essenzialmente indipendente dai dettagli grafici di Android. Anche se Graphha molti campi, tutti hanno valori predefiniti e quindi ha senso utilizzare il modello Builder per creare istanze di questa classe. La classe Graphcontiene una sottoclasse statica annidata denominata Builder, che viene utilizzata per creare Graphoggetti.

Le due classi Graphe Graph.Buildervanno insieme, dal punto di vista di uno sviluppatore, e devono essere intesi, in sostanza, come uno. In verità, devi solo capire come utilizzare la classe annidata Builderper creare un Graphoggetto. Gli sviluppatori non fanno nulla direttamente con un Graphoggetto dopo che è stato creato se non passarlo a un GraphViewoggetto, che esegue il lavoro di visualizzare tutto su un dispositivo Android.

Il Listato 2 riassume i metodi disponibili in classe Graph.Builder. Gli esempi successivi illustreranno come utilizzare il pattern Builder per creare Graphoggetti. Per ora, è sufficiente notare che, a parte il costruttore predefinito (prima riga nel Listato 2) e il build()metodo (ultima riga nel Listato 2), tutti gli altri metodi restituiscono l' Builderoggetto. Ciò rende possibile concatenare chiamate ai metodi del builder.

Listato 2. Riepilogo dei metodi in classe Graph.Builder

 public Builder() public Builder addFunction(Function function, int graphColor) public Builder addFunction(Function function) public Builder addPoints(Point[] points, int pointColor) public Builder addPoints(List points, int pointColor) public Builder addPoints(Point[] points) public Builder addPoints(List points) public Builder addLineGraph(Point[] points, int lineGraphColor) public Builder addLineGraph(List points, int lineGraphColor) public Builder addLineGraph(Point[] points) public Builder addLineGraph(List points) public Builder setBackgroundColor(int bgColor) public Builder setAxesColor(int axesColor) public Builder setFunctionColor(int functColor) public Builder setPointColor(int pointColor) public Builder setWorldCoordinates(double xMin, double xMax, double yMin, double yMax) public Builder setAxes(double axisX, double axisY) public Builder setXTicks(double[] xTicks) public Builder setXTicks(List xTicks) public Builder setYTicks(double[] yTicks) public Builder setYTicks(List yTicks) public Builder setXLabels(Label[] xLabels) public Builder setXLabels(List xLabels) public Builder setYLabels(Label[] yLabels) public Builder setYLabels(List yLabels) public Graph build() 

Noterai nel Listato 2 che molti dei metodi sono sovraccaricati per accettare array di oggetti o elenchi di oggetti. Dò la preferenza agli array rispetto agli elenchi per gli esempi in questo articolo, semplicemente perché è molto più facile inizializzare gli array, ma GraphLibsupporta entrambi. Tuttavia, Java 9 conterrà metodi di fabbrica di convenienza per le raccolte, rimuovendo così questo piccolo vantaggio per gli array. Se Java 9 fosse ampiamente utilizzato al momento di questo articolo, avrei preferito gli elenchi agli array in entrambi GraphLibe negli esempi successivi.

Il modello Builder

Per saperne di più sul pattern Builder, vedere la seconda edizione di Effective Java di Joshua Bloch o l'articolo di JavaWorld "Troppi parametri nei metodi Java, Parte 3: Pattern Builder" di Dustin Marx.

Le classi dell'interfaccia utente in Android sono chiamate visualizzazioni e la classe Viewnel pacchetto android.viewè l'elemento di base per i componenti dell'interfaccia utente. Una vista occupa un'area rettangolare sullo schermo ed è responsabile del disegno e della gestione degli eventi. Dal punto di vista dell'ereditarietà, class Viewè una classe antenata non solo dei controlli dell'interfaccia utente (pulsanti, campi di testo, ecc.) Ma anche dei layout, che sono gruppi di viste invisibili che sono i principali responsabili della disposizione dei loro componenti figli.

La classe GraphViewestende la classe Viewed è responsabile della visualizzazione delle informazioni incapsulate in un Graphsullo schermo di un dispositivo Android. Quindi, la classe GraphViewè dove si svolge tutto il disegno.

Utilizzando GraphLib

Esistono due approcci alla creazione di interfacce utente per Android: un approccio procedurale (all'interno del codice sorgente Java) o un approccio dichiarativo (in un file XML). Uno dei due è valido, ma il consenso è quello di utilizzare l'approccio dichiarativo il più possibile. Ho usato un approccio dichiarativo per i miei esempi.

Ci sono cinque passaggi di base per utilizzare la GraphLiblibreria. Prima di iniziare, scarica il codice sorgente Java compilato per la libreria GraphLib. 

scarica Scarica GraphLib.jar Ottieni il codice sorgente Java compilato per GraphLib. Creato da John I. Moore.

Passaggio 1. Rendi graphlib.jar disponibile per il tuo progetto Android

Create a new project using Android Studio and copy the JAR file graphlib.jar to the libs subdirectory of your project's app directory. In Android Studio, switch the folder structure from Android to Project. Next, in the libs folder (nested within the app folder), right-click on the JAR file and click on Add as library. This last action will add the JAR file in the dependencies section of your app's build.gradle file. See "How to add a jar in external libraries in Android Studio" if you need help with this step.

Step 2. Create an Android activity that will use GraphLib

In Android applications, an activity represents a single screen with a user interface. Activities are defined primarily in two files: an XML file that declares the UI layout and components, and a Java file that defines runtime functionality such as event handling. When a new project is created, Android Studio usually creates a default activity named MainActivity. Use this activity or create a new one for your application.

Step 3. Add a GraphView to the layout for the activity

In the XML file for the activity's layout, you will declare a GraphView object in much the same way that you declare a button or a text view, except that you need to provide the full package name for the GraphView. Listing 3 shows an excerpt from a layout file that declares a GraphView followed by a TextView as part of a vertical linear layout. Following recommended practice, the actual values for the width and height of the GraphView are defined in separate dimen resource files, where different resource files provide values for different screen sizes/densities. (Note: I used 325 for both values in the examples below.)

Listing 3. Declaring a GraphView and a TextView in a layout XML file

Step 4. Import the library classes into the activity

Listing 4 shows the list of import statements for an application if the library classes are imported individually. The list of imports can be abbreviated to a single line as import com.softmoore.android.graphlib.* if desired. Personally, I prefer to see the expanded list as shown in Listing 4.

Listing 4. Import the library classes

 import com.softmoore.android.graphlib.Function; import com.softmoore.android.graphlib.Graph; import com.softmoore.android.graphlib.GraphView; import com.softmoore.android.graphlib.Label; import com.softmoore.android.graphlib.Point; 

Step 5. Create a Graph object and add it to the GraphView

Listing 5 shows the creation of a simple graph object--in this case a graph object that uses all of the default values. It essentially contains only a set of x- and y-axes, where the values on both axes range from 0 to 10. The listing also sets a title for the screen and text for the text view below the graph.

Listing 5. Create a Graph object and add it to the GraphView

 Graph graph = new Graph.Builder() .build(); GraphView graphView = findViewById(R.id.graph_view); graphView.setGraph(graph); setTitle("Empty Graph"); TextView textView = findViewById(R.id.graph_view_label); textView.setText("Graph of Axes"); 

La figura 2 mostra il risultato dell'esecuzione di questa applicazione su un dispositivo Android.

John I. Moore

Utilizzo di GraphLib nelle applicazioni Android

Per il resto dell'articolo mi concentrerò sugli usi nel mondo reale della libreria GraphLib nello sviluppo di applicazioni Android. Presenterò sette esempi con brevi descrizioni ed estratti di codice sorgente. Notare che gli elenchi di codice Java per questi esempi sono focalizzati sull'utilizzo Graph.Builderper creare l' Graphoggetto appropriato . Chiede a findViewById(), setGraph(), setTitle(), ecc, sarebbero simili a quelle mostrate nel Listato 5 e non sono inclusi negli elenchi di codice.