GraphLib: una biblioteca de Android de código abierto para gráficos

Los gráficos y diagramas de datos son herramientas maravillosas para ilustrar relaciones, representar tendencias de datos y rastrear objetivos en sus aplicaciones de Android. Lo vi por mí mismo hace varios años, cuando un ex alumno mío ganó el primer lugar en un concurso de aplicaciones móviles para estudiantes patrocinado por la Asociación de Contratistas de Defensa de Charleston. Una característica clave de la aplicación ganadora, "Diabetes and Me", fue la capacidad de graficar los niveles diarios de azúcar.

Como otro ejemplo, considere una aplicación de seguimiento de peso que traza el progreso contra un peso objetivo. La Figura 1 ilustra cómo se vería una aplicación de este tipo en un teléfono Android. La figura usa un gráfico de líneas rojas para mostrar los pesos mensuales promedio para el año 2017. Muestra el peso objetivo como una línea recta verde cerca de la parte inferior. (Aunque los valores de los datos que se muestran en el gráfico de líneas son hipotéticos, son realistas en relación con el autor de este artículo).

John I. Moore

En este artículo usaré mi biblioteca de código abierto, GraphLib, para demostrar los conceptos básicos de la representación gráfica de funciones matemáticas en Android. No es la misma biblioteca de gráficos que usó mi alumno para su aplicación. De hecho, es mucho más simple y fácil de usar.

descargar Descargar GraphLib Obtenga el código fuente de la biblioteca de gráficos de Android de código abierto presentada en este artículo. Creado por John I. Moore.

Descripción general de GraphLib

GraphLibconsta de una interfaz y ocho clases. Tres de esas clases son internas a la biblioteca y solo tienen acceso al paquete, por lo que no necesitará comprenderlas para usar GraphLib. Dos de las clases restantes tienen una funcionalidad muy simple, y el resto no es difícil de aprender.

A continuación, describiré la interfaz GraphLib y cada una de sus ocho clases. Tenga en cuenta que utilicé características de Java 8, como interfaces funcionales y expresiones lambda, para desarrollar y probar la biblioteca, pero es relativamente sencillo modificar estas características para versiones anteriores de Java.

Interfaz funcional de GraphLib

Como se muestra en el Listado 1, la interfaz Functiontiene solo un método abstracto y, por lo tanto, es una interfaz funcional. Tenga en cuenta que esta interfaz es aproximadamente equivalente a la de Java 8 DoubleUnaryOperator, que se encuentra en el paquete java.util.function. La diferencia es que Functionno utiliza ninguna característica de Java 8 que no sea la anotación @FunctionalInterface. La eliminación de esta anotación es el único cambio necesario para que la Functioninterfaz sea compatible con versiones anteriores de Java.

Listado 1. Función de interfaz

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

Aprendiendo sobre expresiones lambda

Las expresiones lambda, también conocidas como cierres, literales de función o simplemente lambdas, describen un conjunto de características definidas en Java Specification Request (JSR) 335. Se proporcionan introducciones menos formales a las expresiones lambda en una sección de la última versión del Tutorial de Java; en el artículo de JavaWorld "Programación Java con expresiones lambda", y en un par de artículos de Brian Goetz, "Estado de la lambda" y "Estado de la lambda: edición de bibliotecas".

Clases GraphLib

Clases Pointy Labelson relativamente simples: Pointencapsula un par de valores dobles que representan un punto en el plano x, y , y Labelencapsula un valor doble y una cadena, donde el valor doble representa un punto en un eje y la cadena se usa para etiquetar eso punto. El ejemplo de la Figura 1 usa puntos para describir el gráfico de líneas y etiquetas para el eje en la parte inferior, mostrando abreviaturas de una letra para los meses. Proporcionaré más ejemplos que ilustran el uso de estas clases más adelante en el artículo.

Las clases GraphFunction, GraphPointsy ScreenPointno solo son muy simples, también son internas a la biblioteca y solo tienen acceso a paquetes. Realmente no necesita comprender estas clases para usar la biblioteca, así que las describiré brevemente aquí:

  • GraphFunctionencapsula una función (es decir, una clase que implementa la interfaz Function) y un color utilizado para dibujar esa función.
  • GraphPointsencapsula una lista de puntos junto con un color utilizado para trazarlos. Esta clase se utiliza internamente tanto para trazar puntos como para trazar gráficos de líneas.
  • ScreenPointencapsula un par de valores enteros que representan coordenadas de píxeles en la pantalla de un dispositivo Android. Esta clase es similar pero más simple que la clase de Android Pointen el paquete android.graphics.

Proporcioné el código fuente para estas clases en caso de que esté interesado en los detalles.

Las tres clases restantes en la biblioteca GraphLib son Graph, Graph.Buildery GraphView. Es importante comprender el papel que desempeña cada uno de ellos en una aplicación de Android.

La clase Graphcontiene información sobre los colores, puntos, etiquetas, gráficos, etc., que se dibujarán, pero es esencialmente independiente de los detalles gráficos de Android. Si bien Graphtiene muchos campos, todos tienen valores predeterminados y, por lo tanto, tiene sentido usar el patrón Builder para crear instancias de esta clase. La clase Graphcontiene una subclase estática anidada denominada Builder, que se utiliza para crear Graphobjetos.

Las dos clases Graphy Graph.Buildervan de la mano, desde la perspectiva de un desarrollador, y deben entenderse, en esencia, como una sola. En realidad, solo necesita comprender cómo usar la clase anidada Builderpara crear un Graphobjeto. Los desarrolladores realmente no hacen nada directamente con un Graphobjeto después de que ha sido creado más que pasarlo a un GraphViewobjeto, que hace el trabajo de mostrar todo en un dispositivo Android.

Listing 2 summarizes the methods available in class Graph.Builder. Later examples will illustrate how to use the Builder pattern to create Graph objects. For now, it's enough to note that, other than the default constructor (first line in Listing 2) and the build() method (last line in Listing 2), all other methods return the Builder object. This makes it possible to chain calls to builder methods.

Listing 2. Summary of methods in class 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() 

You'll note in Listing 2 that many of the methods are overloaded to accept either arrays of objects or lists of objects. I give preference to arrays over lists for examples in this article, simply because it is much easier to initialize arrays, but GraphLib supports both. However, Java 9 will contain convenience factory methods for collections, thereby removing this small advantage for arrays. Were Java 9 in widespread use at the time of this article, I would have preferred lists over arrays in both GraphLib and the later examples.

The Builder pattern

To learn more about the Builder pattern, see the second edition of Effective Java by Joshua Bloch or the JavaWorld article "Too many parameters in Java methods, Part 3: Builder pattern" by Dustin Marx.

User interface classes in Android are called views, and class View in package android.view is the basic building block for user interface components. A view occupies a rectangular area on the screen, and is responsible for drawing and event handling. From an inheritance perspective, class View is an ancestor class not only of user interface controls (buttons, text fields, etc.) but also of layouts, which are invisible view groups that are primarily responsible for arranging their child components.

Class GraphView extends class View and is responsible for displaying the information encapsulated in a Graph on the screen of an Android device. Thus, class GraphView is where all the drawing takes place.

Using GraphLib

Hay dos enfoques para crear interfaces de usuario para Android: un enfoque de procedimiento (dentro del código fuente de Java) o un enfoque declarativo (en un archivo XML). Cualquiera de los dos es válido, pero el consenso es utilizar el enfoque declarativo tanto como sea posible. He utilizado un enfoque declarativo para mis ejemplos.

Hay cinco pasos básicos para usar la GraphLibbiblioteca. Antes de comenzar, descargue el código fuente compilado de Java para la biblioteca GraphLib. 

descargar Descargar GraphLib.jar Obtenga el código fuente compilado de Java para GraphLib. Creado por John I. Moore.

Paso 1.Haga que graphlib.jar esté disponible para su proyecto de 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 muestra el resultado de ejecutar esta aplicación en un dispositivo Android.

John I. Moore

Usando GraphLib en aplicaciones de Android

En el resto del artículo, me centraré en los usos del mundo real de la biblioteca GraphLib en el desarrollo de aplicaciones de Android. Presentaré siete ejemplos con breves descripciones y extractos del código fuente. Tenga en cuenta que los listados de código Java para estos ejemplos se centran en el uso Graph.Builderpara crear el Graphobjeto apropiado . Las llamadas a findViewById(), setGraph(), setTitle(), etc., serían similares a los mostrados en el Listado 5 y no están incluidos en los listados de código.