Android, Diagramme mit AFreeChart erstellen

Wer sich mit Java schon etwas beschäftigt hat sollte das Framework JFreeChart kennen. Für Android wurde dieses durchaus sehr gute Framework portiert und kann aus dem Repository von Google „https://code.google.com/p/afreechart/“ heruntergeladen werden.

Für mein Projekt habe ich die Version 0.0.4 gewählte welche unter der Adresse AFreeChart Downloads bezogen werden kann (dort befindet sich unter anderem die JavaDoc und eine *.apk Datei als Lauffähige Android App).

Einbinden der Sourcen

Zuerst muss die Datei afreechart-0.0.4.jar in das Projekt eingebunden werden. Dieses ist in jeder IDE unterschiedlich. Da ich InteliJ von JetBrains verwende und mein Projekt mit Gradle erstelle muss ich einen Ordner „libs“ neben dem „src“ Ordner erstellen.

"libs" Ordner für externe Bibliotheken
„libs“ Ordner für externe Bibliotheken

Nun noch die Datei in diesem Ordner ablegen und im Android Projekt bekannt machen.

Als nächstes muss dieser Ordner in der Konfigurationsdatei „build.gradle(Module: app)“  eingetragen werden.

Konfigurationsdatei für die Abhängigkeiten im Android Projekt.
Konfigurationsdatei für die Abhängigkeiten im Android Projekt.

Es wird lediglich folgender Eintrag hinzugefügt bzw ergänzt.

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

 

Erstellen einer Activity mit einem Liniendiagramm

Um eine Activity mit zbsp. einem Liniendiagramm zu erstellen muss man ähnlich wie bei JFreeChart das Diagramm Objekt  „AFreeChart“ erstellen.

AFreeChart chart = ChartFactory.createTimeSeriesChart(
   "Titel",//Der Titel des Diagramms
   "Datum", //Die Achsenbeschriftung in X Richtung
   "Werte", //Die Achsenbeschriftung in Y Richtung
   dataset, //Das DataSet Objekt mit den Werten
   true, //Legende Anzeigen
   false, //Tooltips Anzeigen
   false //URL's Anzeigen
);

Diagramm einer View hinzufügen

Dieses Diagramm kann nun zu einer View hinzugefügt werden. Ich wende aus dem Beispiel die DemoView und leite davon meine neue View ab somit muss ich mich nicht mit dem Überschreiben von Methoden kümmern.

public class EvaluationChartView extends DemoView {}

Als erstes müssen wir unseren Konstruktur erstellen welcher als Parameter den Context und eine java.util.List (oder eine andere Collection) mit den Werten enthält. In meinem Fall sind dies Messergebnisse.

public EvaluationChartView(Context context, List<Measurement> measurements) {
        super(context);
}

Diese Messergebnisse müssen nun einem Dataset zugeordnet werden.

String text = ctx.getResources().getString(R.string.seriesTxt);
TimeSeries myTimeSerie = new TimeSeries(text);
for (Measurement measurement : measurements) {
  Minute minute = formatTimestamp(measurement.getTimestamp());
  myTimeSerie.add(minute, measurement.getValue());
}
TimeSeriesCollection dataset = new TimeSeriesCollection();
dataset.addSeries(myTimeSerie);

In diesem Beispiel habe ich die Zeitachse in Minuten Abstände eingestellt. Andere Werte können zbsp. sein:

RegularTimePeriod
RegularTimePeriod

Der Konstruktor der Klasse „Minute“ erwartet ein java.util.Date Objekt daher muss ich erst mein Timestamp (long) in ein Date Objekt umwandeln. Am einfachsten geht dieses mit der Klasse java.util.Calendar.

private Minute formatTimestamp(long timestamp) {
   Calendar cal = Calendar.getInstance();
   cal.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
   cal.setTimeInMillis(timestamp);
   return new Minute(cal.getTime());
}

Als Zeitzone setzte ich hier die deutsche Zeitzone, eleganter ist es jedoch die Zeitzone vom Gerät zu verwenden.
Wenn Sie die Zeitzone vom Gerät verwenden möchten kann die Zeile

cal.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

entfallen, da die Klasse java.util.Calendar schon die Zeitzone vom Gerät hat.

Das Diagramm auf einer Activity darstellen

Nachdem wir das Diagramm erstellt und mit Daten befüllt haben, werden wir nun dieses auf einer Activity darstellen.

Das Layout zur Activity

Das Layout für diese Activity wird einfach ausfallen da wir unser View Objekt selbst erstellen benötigen wir zuerst nur einen Container für dieses.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/evaluationCreateLayout"></LinearLayout>
</RelativeLayout>

Das „LinearLayout“ wird unser Container.

Einbetten des Diagramms in den Container

Zuerst erstellen wir unser DiagrammView Objekt und holen eine Referenz zum Linearlayout (unser Container). In dieses Layout wird nun unser DiagrammView Objekt mit der Methode „addView(View view)“ hinzugefügt.

Context ctx = getApplicationContext();
EvaluationChartView chartView = new EvaluationChartView(ctx, measurements, evaluation.getEvaluationChartValues());
Point p = LayoutHelper.getScreenSize(ctx);
chartView.setMinimumWidth(p.x);
LinearLayout evaluationCreateLayout = (LinearLayout) findViewById(R.id.evaluationCreateLayout);
evaluationCreateLayout.addView(chartView);

Damit das Diagramm die volle Breite des Bildschirms einnimmt nutze ich eine Hilfsklasse welche mir die Bildschirmgröße als android.graphics.Point liefert (ähnlich wie java.awt.Point).

public static Point getScreenSize(Context context) {
   WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
   Display display = wm.getDefaultDisplay();
   Point size = new Point();
   display.getSize(size);
   return size;
}

Speichern des Diagramms als Bitmap

Wer sich schonmal mit JFreeChart beschäftig hat wird nun eine UtilKlasse vermissen

ChartUtilities.writeChartAsPNG(outputStream,getDataset(), width,height);

Hier kann mit einem einfachen Trick abhilfe geschaffen werden. Es ist in Android möglich von jedem View Objekt einen „Screenshot“ zu erstellen und somit erstellen wir von unserem Container welches unser Diagramm enthält, einen Screenshot und Speichern dieses als Bitmap auf dem Gerät.

private File storeChart(){
   File file = new File(CardioLoggerStatics.getExternalDirectory(getApplicationContext())+"/chart.png");
   FileOutputStream out = null;
   try {
       out = new FileOutputStream(file);
       LinearLayout view = (LinearLayout) findViewById(R.id.evaluationCreateLayout);
       view.setDrawingCacheEnabled(true);
       view.buildDrawingCache();
       Bitmap bmp = view.getDrawingCache();
       bmp.compress(Bitmap.CompressFormat.PNG, 100, out);
   } catch (Exception e) {
       e.printStackTrace();
   } finally {
      try {
           if (out != null) {
              out.close();
           }
      } catch (IOException e) {
        e.printStackTrace();
    }
  }
 return file;
}

Da wir das Bitmap auf dem Gerät ablegen wollen benötigen wir zusätzliche Rechte (Permissions) welche in der Android.Manifest.xml festgehalten werden.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Teilen des Diagramms

Dieses erstellte Bild kann nun mit anderen Geräte oder Systemen geteilt / verschickt werden. Das Objekt „chartFile“ ist vom Typ java.io.File und enthält unseren Screenshot vom Container.

private void shareEvaluation() {
   Intent intent = new Intent();
   intent.setAction(Intent.ACTION_SEND);
   intent.setType("text/plain");
   intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   File chartFile = storeChart();
   intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(chartFile));
   getApplicationContext().startActivity(intent);
}

Das fertige Diagramm

Folgendes Diagramm habe ich mit dem oben dargestellten Code generiert.
Die Werte sind nur Beispielhaft.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.