GraphLib: Eine Open-Source-Android-Bibliothek für Grafiken

Grafiken und Datenplots sind wunderbare Werkzeuge zur Veranschaulichung von Beziehungen, zur Darstellung von Datentrends und zur Verfolgung von Zielen in Ihren Android-Anwendungen. Ich habe das vor einigen Jahren selbst gesehen, als ein ehemaliger Student von mir den ersten Platz bei einem von der Charleston Defense Contractors Association gesponserten Wettbewerb für mobile Apps für Studenten gewann. Ein wichtiges Merkmal der Gewinner-App "Diabetes and Me" war die Möglichkeit, den täglichen Zuckergehalt grafisch darzustellen.

Betrachten Sie als weiteres Beispiel eine Anwendung zur Gewichtsverfolgung, die den Fortschritt anhand eines Zielgewichts aufzeichnet. Abbildung 1 zeigt, wie eine solche Anwendung auf einem Android-Telefon aussehen könnte. Die Abbildung verwendet ein rotes Liniendiagramm, um die durchschnittlichen monatlichen Gewichte für das Jahr 2017 anzuzeigen. Sie zeigt das Zielgewicht als grüne gerade Linie in der Nähe des unteren Bereichs. (Obwohl die im Liniendiagramm angezeigten Datenwerte hypothetisch sind, sind sie für den Autor dieses Artikels realistisch.)

John I. Moore

In diesem Artikel werde ich meine Open-Source-Bibliothek GraphLib verwenden, um die Grundlagen der grafischen Darstellung mathematischer Funktionen in Android zu demonstrieren. Es ist nicht dieselbe Grafikbibliothek, die mein Schüler für seine Anwendung verwendet hat. In der Tat ist es viel einfacher und einfacher zu bedienen.

download Download GraphLib Ruft den Quellcode für die in diesem Artikel vorgestellte Open Source-Grafikbibliothek für Android ab. Erstellt von John I. Moore.

Übersicht über GraphLib

GraphLibbesteht aus einer Schnittstelle und acht Klassen. Drei dieser Klassen sind bibliotheksintern und haben nur Paketzugriff, sodass Sie sie nicht verstehen müssen, um GraphLib verwenden zu können. Zwei der verbleibenden Klassen haben eine sehr einfache Funktionalität, und der Rest ist nicht schwer zu erlernen.

Im Folgenden werde ich die GraphLib-Schnittstelle und jede ihrer acht Klassen beschreiben. Beachten Sie, dass ich Java 8-Funktionen wie Funktionsschnittstellen und Lambda-Ausdrücke zum Entwickeln und Testen der Bibliothek verwendet habe. Es ist jedoch relativ einfach, diese Funktionen für frühere Java-Versionen zu ändern.

Funktionsschnittstelle von GraphLib

Wie in Listing 1 gezeigt, hat die Schnittstelle Functionnur eine abstrakte Methode und ist daher eine funktionale Schnittstelle. Beachten Sie, dass diese Schnittstelle in etwa den DoubleUnaryOperatorim Paket enthaltenen Java 8-Schnittstellen entspricht java.util.function. Der Unterschied besteht darin, dass Functionkeine anderen Java 8-Funktionen als die Anmerkung verwendet werden @FunctionalInterface. Das Entfernen dieser Anmerkung ist die einzige Änderung, die erforderlich ist, um die FunctionSchnittstelle mit früheren Java-Versionen kompatibel zu machen .

Listing 1. Schnittstelle Funktion

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

Lernen über Lambda-Ausdrücke

Lambda-Ausdrücke, auch als Closures, Funktionsliterale oder einfach Lambdas bezeichnet, beschreiben eine Reihe von Funktionen, die in Java Specification Request (JSR) 335 definiert sind. Weniger formale Einführungen in Lambda-Ausdrücke finden Sie in einem Abschnitt der neuesten Version des Java-Lernprogramms. im JavaWorld-Artikel "Java-Programmierung mit Lambda-Ausdrücken" und in einigen Artikeln von Brian Goetz, "State of the Lambda" und "State of the Lambda: Libraries Edition".

GraphLib-Klassen

Klassen Pointund Labelsind relativ einfach: Pointkapselt ein Paar von Doppelwerten, die einen Punkt in der x-, y- Ebene darstellen, und Labelkapselt einen Doppelwert und eine Zeichenfolge, wobei der Doppelwert einen Punkt auf einer Achse darstellt und die Zeichenfolge verwendet wird, um diesen zu kennzeichnen Punkt. In dem Beispiel in Abbildung 1 werden Punkte verwendet, um das Liniendiagramm und die Beschriftungen für die Achse unten zu beschreiben, wobei Abkürzungen mit einem Buchstaben für die Monate angezeigt werden. Ich werde später in diesem Artikel weitere Beispiele zur Verwendung dieser Klassen bereitstellen.

Klassen GraphFunction, GraphPointsund ScreenPointsind nicht nur sehr einfach, sie sind in die Bibliothek auch intern und haben nur Zugang zu verpacken. Sie müssen diese Klassen nicht wirklich verstehen, um die Bibliothek nutzen zu können. Deshalb werde ich sie hier nur kurz beschreiben:

  • GraphFunctionkapselt eine Funktion (dh eine Klasse, die die Schnittstelle implementiert Function) und eine Farbe, die zum Zeichnen dieser Funktion verwendet wird.
  • GraphPointskapselt eine Liste von Punkten zusammen mit einer Farbe, mit der sie gezeichnet werden. Diese Klasse wird intern sowohl zum Zeichnen von Punkten als auch zum Zeichnen von Liniendiagrammen verwendet.
  • ScreenPointkapselt ein Paar ganzzahliger Werte, die Pixelkoordinaten auf dem Bildschirm eines Android-Geräts darstellen. Diese Klasse ähnelt der Android-Klasse Pointim Paket, ist jedoch einfacher android.graphics.

Ich habe den Quellcode für diese Klassen bereitgestellt, falls Sie an den Details interessiert sind.

Die drei übrigen Klassen in der Bibliothek GraphLib sind Graph, Graph.Builderund GraphView. Es ist wichtig zu verstehen, welche Rolle jeder von ihnen in einer Android-Anwendung spielt.

Die Klasse Graphenthält Informationen zu den zu zeichnenden Farben, Punkten, Beschriftungen, Grafiken usw., ist jedoch im Wesentlichen unabhängig von den Details der Android-Grafik. Obwohl Graphes viele Felder gibt, haben sie alle Standardwerte. Daher ist es sinnvoll, das Builder-Muster zum Erstellen von Instanzen dieser Klasse zu verwenden. Die Klasse Graphenthält eine verschachtelte statische Unterklasse mit dem Namen Builder, mit der GraphObjekte erstellt werden.

Die beiden Klassen Graphund Graph.Buildergehen zusammen, aus der Sicht des Entwicklers und sollen im Wesentlichen verstanden werden, als eine. In Wahrheit müssen Sie nur verstehen, wie Sie die verschachtelte Klasse Builderzum Erstellen eines GraphObjekts verwenden. Entwickler tun nichts direkt mit einem GraphObjekt, nachdem es erstellt wurde, außer es an ein GraphViewObjekt zu übergeben, das alles auf einem Android-Gerät anzeigt.

Listing 2 fasst die in der Klasse verfügbaren Methoden zusammen Graph.Builder. In späteren Beispielen wird veranschaulicht, wie das Builder-Muster zum Erstellen von GraphObjekten verwendet wird. Im Moment genügt es zu beachten, dass außer dem Standardkonstruktor (erste Zeile in Listing 2) und der build()Methode (letzte Zeile in Listing 2) alle anderen Methoden das BuilderObjekt zurückgeben. Auf diese Weise können Aufrufe an Builder-Methoden verkettet werden.

Listing 2. Zusammenfassung der Methoden in der Klasse 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() 

In Listing 2 werden Sie feststellen, dass viele der Methoden überladen sind, um entweder Arrays von Objekten oder Listen von Objekten zu akzeptieren. Ich bevorzuge Arrays gegenüber Listen für Beispiele in diesem Artikel, einfach weil es viel einfacher ist, Arrays zu initialisieren, aber GraphLibbeide unterstützt. Java 9 enthält jedoch Convenience-Factory-Methoden für Sammlungen, wodurch dieser kleine Vorteil für Arrays beseitigt wird. Wäre Java 9 zum Zeitpunkt dieses Artikels weit verbreitet, hätte ich Listen in beiden GraphLibund den späteren Beispielen Arrays vorgezogen .

Das Builder-Muster

Weitere Informationen zum Builder-Muster finden Sie in der zweiten Ausgabe von Effective Java von Joshua Bloch oder im JavaWorld-Artikel "Zu viele Parameter in Java-Methoden, Teil 3: Builder-Muster" von 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

Es gibt zwei Ansätze zum Erstellen von Benutzeroberflächen für Android: einen prozeduralen Ansatz (innerhalb des Java-Quellcodes) oder einen deklarativen Ansatz (in einer XML-Datei). Beides ist gültig, aber der Konsens besteht darin, den deklarativen Ansatz so weit wie möglich zu verwenden. Ich habe für meine Beispiele einen deklarativen Ansatz verwendet.

Es gibt fünf grundlegende Schritte zur Verwendung der GraphLibBibliothek. Laden Sie vor dem Start den kompilierten Java-Quellcode für die GraphLib-Bibliothek herunter. 

download Download GraphLib.jar Holen Sie sich den kompilierten Java-Quellcode für GraphLib. Erstellt von John I. Moore.

Schritt 1. Stellen Sie graphlib.jar für Ihr Android-Projekt zur Verfügung

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

Abbildung 2 zeigt das Ergebnis der Ausführung dieser Anwendung auf einem Android-Gerät.

John I. Moore

Verwenden von GraphLib in Android-Anwendungen

Im weiteren Verlauf des Artikels werde ich mich auf die reale Verwendung der GraphLib-Bibliothek in der Android-Anwendungsentwicklung konzentrieren. Ich werde sieben Beispiele mit kurzen Beschreibungen und Quellcode-Auszügen vorstellen. Beachten Sie, dass die Java-Codelisten für diese Beispiele darauf ausgerichtet sind Graph.Builder, das entsprechende GraphObjekt zu erstellen . Anrufe zu findViewById(), setGraph(), setTitle()usw. würden wie in Listing ähnlich 5 und sind nicht in den Codebeispielen enthalten.