GraphLib: An open source Android library for graphs

An easy way to draw graphs and data plots in your Android applications

Graphs and data plots are wonderful tools for illustrating relationships, depicting data trends, and tracking goals in your Android applications. I saw this for myself several years ago, when a former student of mine won first place in a student mobile app competition sponsored by the Charleston Defense Contractors Association. A key feature of the winning app, "Diabetes and Me," was the ability to graph daily sugar levels.

As another example, consider a weight-tracking application that plots progress against a goal weight. Figure 1 illustrates how such an application might look on an Android phone. The figure uses a red line-graph to show average monthly weights for the year 2017. It shows the goal weight as a green straight line near the bottom. (Although the data values shown in the line graph are hypothetical, they are realistic pertaining to the author of this article.)

In this article I'll use my open source library, GraphLib, to demonstrate the basics of graphing mathematical functions in Android. It's not the same graph library that my student used for his application. In fact, it's much simpler and easier to use.

Get the source code for the open source Android graphing library introduced in this article. Created by John I. Moore.

Overview of GraphLib

`GraphLib` consists of one interface and eight classes. Three of those classes are internal to the library and have only package access, so you will not need to understand them in order to use GraphLib. Two of the remaining classes have very simple functionality, and the remainder are not hard to pick up.

Below I will describe the GraphLib interface and each of its eight classes. Note that I used Java 8 features such as functional interfaces and lambda expressions to develop and test the library, but it's relatively straightforward to modify these features for earlier versions of Java.

GraphLib's functional interface

As shown in Listing 1, interface `Function` has only one abstract method and is, therefore, a functional interface. Note that this interface is roughly equivalent to Java 8's `DoubleUnaryOperator`, found in package `java.util.function`. The difference is that `Function` does not use any Java 8 features other than the annotation `@FunctionalInterface`. Removing this annotation is the only change necessary to make the `Function` interface compatible with earlier versions of Java.

Listing 1. interface Function

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

GraphLib classes

Classes `Point` and `Label` are relatively simple: `Point` encapsulates a pair of double values representing a point in the x,y-plane, and `Label` encapsulates a double value and a string, where the double value represents a point on an axis and the string is used to label that point. The example in Figure 1 uses points to describe the line graph and labels for the axis at the bottom, showing one-letter abbreviations for the months. I'll provide more examples illustrating the use of these classes later in the article.

Classes `GraphFunction`, `GraphPoints`, and `ScreenPoint` are not only very simple, they are also internal to the library and have only package access. You don't really need to understand these classes to use the library, so I'll describe them just briefly here:

• `GraphFunction` encapsulates a function (i.e., a class that implements interface `Function`) and a color used to draw that function.
• `GraphPoints` encapsulates a list of points together with a color used to plot them. This class is used internally for both plotting points and drawing line graphs.
• `ScreenPoint` encapsulates a pair of integer values representing pixel coordinates on the screen of an Android device. This class is similar to but simpler than the Android class `Point` in package `android.graphics`.

I've provided the source code for these classes in case you are interested in the details.

The three remaining classes in the GraphLib library are `Graph`, `Graph.Builder`, and `GraphView`. It's important to understand the role that each of them plays in an Android application.

Class `Graph` contains information about the colors, points, labels, graphs, etc., to be drawn, but is essentially independent of Android graphics details. While `Graph` has a lot of fields, they all have default values, and therefore it makes sense to use the Builder pattern to create instances of this class. Class `Graph` contains a nested static subclass named `Builder`, which is used to create `Graph` objects.

The two classes `Graph` and `Graph.Builder` go together, from a developer's perspective, and should be understood, essentially, as one. In truth, you only need to understand how to use the nested class `Builder` to create a `Graph` object. Developers don't really do anything directly with a `Graph` object after it has been created other than pass it to a `GraphView` object, which does the work of displaying everything on an Android device.

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 addPoints(Point[] points, int pointColor)
public Builder addPoints(List<Point> points, int pointColor)
public Builder addLineGraph(Point[] points, int lineGraphColor)
public Builder addLineGraph(List<Point> points, int lineGraphColor)
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<Double> xTicks)
public Builder setYTicks(double[] yTicks)
public Builder setYTicks(List<Double> yTicks)
public Builder setXLabels(Label[] xLabels)
public Builder setXLabels(List<Label> xLabels)
public Builder setYLabels(Label[] yLabels)
public Builder setYLabels(List<Label> 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.

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

There are two approaches to creating user interfaces for Android: a procedural approach (within the Java source code) or a declarative approach (in an XML file). Either one is valid, but the consensus is to use the declarative approach as much as possible. I've used a declarative approach for my examples.

There are five basic steps to using the `GraphLib` library. Before you start, download the compiled Java source code for the GraphLib library.

Get the compiled Java source code for GraphLib. Created by John I. Moore.

Step 1. Make graphlib.jar available to your Android project

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

``````
<com.softmoore.android.graphlib.GraphView
android:id="@+id/graph_view"
android:layout_width="@dimen/graphView_width"
android:layout_height="@dimen/graphView_height"/>
<TextView
android:id="@+id/graph_view_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textStyle="bold"/>
```
```

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");
```
```

Figure 2 shows the result of running this application on an Android device.

Using GraphLib in Android applications

For the remainder of the article I'll focus on real-world uses of the GraphLib library in Android application development. I'll present seven examples with brief descriptions and source code excerpts. Note that the Java code listings for these examples are focused on using `Graph.Builder` to create the appropriate `Graph` object. Calls to `findViewById()`, `setGraph()`, `setTitle()`, etc., would be similar to those shown in Listing 5 and are not included in the code listings.

Example 1: Graphing y = x2

Let's start by adding a simple function to a graph. If your version of Android Studio is configured to use Java 8 or later, you can use lambda expressions as shown in Listing 6.

Listing 6. Using a lambda expression.

``````
Graph graph = new Graph.Builder()
.build();
```
```

If you are using a version of Java prior to Java 8, you will need to create an instance of a class that implements the `Function` interface. I've done this in Listing 7 using an anonymous class.

Listing 7. Using an anonymous class

``````
Function xSquared = new Function()
{
public double apply(double x)
{
return x*x;
}
};
Graph graph = new Graph.Builder()
.build();
```
```

Figure 3 shows how this application would look on an Android device.

Note: The remaining examples will use only lambda expressions for mathematical functions.

Example 2: Adding color and setting world coordinates

For this example we'll make a few changes to the previous example. First, let's change the color of the graph to red. There are a couple of ways to do this. We could set the default color for functions by calling method `setFunctionColor()` before calling `addFunction()`. Another option is to call the overloaded version of `addFunction()`, which has two parameters, both the function to be graphed and the color to be used for that graph. This example demonstrates the latter approach.

For another change to the previous example, let's adjust the world coordinates (a.k.a. window) for the graph. Notice how much of the graph in Figure 3 is in the top half of the view. For this example we'll let the x-axis range from -5 to 5 and the y-axis range from -2 to 10. When we make this change, we also need to modify the "tick" marks and labels on the axes, using calls to methods `setXTicks()` and `setYTicks()`. Listing 8 shows the code to build this graph.

Listing 8. Adding color and setting world coordinates

``````
Graph graph = new Graph.Builder()
.setWorldCoordinates(-5, 5, -2, 20)
.setXTicks(new double[] {-4, -3, -2, -1, 1, 2, 3, 4})
.setYTicks(new double[] {2, 4, 6, 8, 10, 12, 14, 16, 18})
.build();
```
```

Figure 4 shows the resulting application running on an Android device.

Example 3: Graphing three functions

Observe that some of the builder methods shown in Listing 2 start with the `set` prefix, and some start with the `add` prefix. Those starting with `set` control access to a single attribute value, but those starting with `add` can be called multiple times to append to a list of similar attribute values. For example, we can call either of the overloaded versions of the `addFunction()` method to add more than one function to a graph.

Listing 9 shows how to add three functions, each with different colors.

Listing 9. Graphing two functions

``````
Graph graph = new Graph.Builder()
.setWorldCoordinates(-2*Math.PI, 2*Math.PI, -5, 5)
.setXTicks(new double[] {-3, -1, 1, 3})
.setYTicks(new double[] {-3, -1, 1, 3})
.build();
```
```

Figure 5 shows the application running on an Android device.

Example 4: Straight line plus data points

This example illustrates how to add data points to a graph. When a set of points visually appear to be in a pattern that is "almost" a straight line, it is possible use a technique known as linear regression to determine the line that best fits the data points. A detailed discussion of linear regression is beyond the scope of this article, but I have used that technique (with some numerical rounding) to find the formula for the line used in this example.

Listing 10 shows how to add both a function (in this case a straight line) and a set of four data points to a graph

Listing 10. Straight line plus data points

``````
Graph graph = new Graph.Builder()
.addPoints(new Point[] { new Point(-6, -6), new Point(-2, 3),
new Point(2, 6),   new Point(5, 7)},
Color.RED)
.build();
```
```

Figure 6 shows the application running on an Android device.

Example 5: Line graph

This example illustrates how to add a line graph. A line graph is defined by a set of points. Defining a line graph to be drawn on the screen is similar to defining a set data points to be plotted on the screen. The difference is that when defining a line graph, we need to call method `addLineGraph()` instead of calling `addPoints()`. Listing 11 shows how to add a line graph.

Listing 11. Line graph

``````
Point[] points =
{
new Point(-10, 3), new Point(-8, 4),  new Point(5, 2),
new Point(0, 0),   new Point(2, -6),  new Point(3,3),
new Point(7,5),    new Point(9, 9),   new Point(12, 6)
};
Graph graph = new Graph.Builder()
.build();
```
```

Figure 7 shows the application running on an Android device.

Example 6: Tracking weight for one month

For this example let's assume that we have a weight-tracking application that shows weights measured for several days throughout a given month. Similar to the example at the beginning of this article (and depicted in Figure 1), this example combines a horizontal goal weight line and a line graph showing actual weight measurements. Whereas the example shown in Figure 1 used string labels on the horizontal axis, this one will use numbers representing the days of the month when weights were taken.

Listing 12 shows the code to add both a horizontal green line for the goal weight and a line graph for the measured weight.

Listing 12. Tracking weight for one month

``````
Point[] points =
{
new Point(1, 178),  new Point(4, 179),  new Point(7, 179),
new Point(10, 181), new Point(13, 180), new Point(16, 182),
new Point(19, 182), new Point(22, 184), new Point(25, 183),
new Point(28, 185), new Point(31, 185)
};
Graph graph = new Graph.Builder()
.setWorldCoordinates(-5, 33, 165, 191)
.setAxes(0, 167)
.setXTicks(new double[] {5, 10, 15, 20, 25, 30})
.setYTicks(new double[] {170, 175, 180, 185, 190})
.build();
``````

Figure 8 shows the application running on an Android device.

Example 7: Tracking weight for a year

We now come full circle back to that motivating example from Figure 1, showing weight measurements for an entire year. Similar to Example 6 above, this application combines a horizontal goal weight line together with a line graph showing actual weight measurements. But in this case we've added nonnumeric (string) labels for the x-axis.

Note, too, that all previous examples have used methods `setXTicks()` and `setYTicks()` to specify the placement of numeric labels for the axes. In this example, we use `setXLabels()` for the month abbreviations on the x-axis, but still use `setYTicks()` to show the numerical values for weights on the y-axis. Also note that I've used only the first letter of each month, accounting for space limitations on most Android phone screens.

Listing 13 shows the source code to create the graph.

Listing 13. Tracking weight for a year

``````
Point[] points =
{
new Point(1, 178),  new Point(2, 179),  new Point(3, 179),
new Point(4, 181),  new Point(5, 180),  new Point(6, 182),
new Point(7, 182),  new Point(8, 184),  new Point(9, 183),
new Point(10, 185), new Point(11, 185), new Point(12, 186)
};
Label[] xLabels =
{
new Label(1, "J"),  new Label(2, "F"),  new Label(3, "M"),
new Label(4, "A"),  new Label(5, "M"),  new Label(6, "J"),
new Label(7, "J"),  new Label(8, "A"),  new Label(9, "S"),
new Label(10, "O"), new Label(11, "N"), new Label(12, "D")
};
Graph graph = new Graph.Builder()
.setWorldCoordinates(-2, 13, 165, 191)
.setAxes(0, 167)
.setXLabels(xLabels)
.setYTicks(new double[] {170, 175, 180, 185, 190})
Many Android applications can make effective use of graphs and data plots to help illustrate relationships, depict data trends, and track progress versus goals. I created the open source library `GraphLib` in order to support this type of functionality. As you've seen, GraphLib is an easy-to-use library for graphing functions, plotting data points, and drawing line graphs. This library is freely available, and you can get the source code for it here.