Started implementing drawable line chart that allows the user to draw into the chart

This commit is contained in:
ph1lb4 2014-05-29 22:41:46 +02:00
parent 3ec5abf750
commit f21692312d
12 changed files with 2383 additions and 2005 deletions

View file

@ -29,6 +29,7 @@
<activity android:name="PieChartActivity"></activity>
<activity android:name="MultiLineChartActivity"></activity>
<activity android:name="BarChartActivityMultiDataset"></activity>
<activity android:name="DrawChartActivity"></activity>
</application>
</manifest>

View file

@ -10,62 +10,69 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:padding="10dp" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:padding="10dp" >
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="LineChart" />
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="LineChart" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="BarChart" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="PieChart" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="BarChart" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="LineChart (more datasets)" />
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="BarChart (more datasets)" />
<Button
android:id="@+id/button6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="MultipleCharts" />
<Button
android:id="@+id/button7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="View on GitHub" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="PieChart" />
</LinearLayout>
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="LineChart (more datasets)" />
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="BarChart (more datasets)" />
<Button
android:id="@+id/button6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="MultipleCharts" />
<Button
android:id="@+id/button7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="Draw Chart" />
<Button
android:id="@+id/button8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="View on GitHub" />
</LinearLayout>
</ScrollView>
</RelativeLayout>

View file

@ -0,0 +1,180 @@
package com.example.mpchartexample;
import java.util.ArrayList;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import com.github.mikephil.charting.ChartData;
import com.github.mikephil.charting.ColorTemplate;
import com.github.mikephil.charting.DataSet;
import com.github.mikephil.charting.Entry;
import com.github.mikephil.charting.Highlight;
import com.github.mikephil.charting.LineChart;
import com.github.mikephil.charting.OnChartValueSelectedListener;
public class DrawChartActivity extends Activity implements OnChartValueSelectedListener {
private LineChart mChart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_linechart);
// create a color template for one dataset with only one color
ColorTemplate ct = new ColorTemplate();
ct.addColorsForDataSets(new int[] { R.color.colorful_1 }, this);
mChart = (LineChart) findViewById(R.id.chart1);
mChart.setOnChartValueSelectedListener(this);
mChart.setColorTemplate(ct);
mChart.setDrawingEnabled(true);
// mChart.setDrawFilled(true);
// mChart.setRoundedYLegend(false);
// mChart.setStartAtZero(true);
mChart.setDrawYValues(false);
mChart.setLineWidth(5f);
mChart.setCircleSize(5f);
// mChart.setSpacePercent(20, 10);
mChart.setYLegendCount(6);
mChart.setTouchEnabled(true);
mChart.setHighlightEnabled(true);
// highlight index 2 and 6 in dataset 0
// mChart.highlightValues(new Highlight[] {new Highlight(2, 0), new
// Highlight(6, 0)});
mChart.setDragEnabled(true);
mChart.setTouchEnabled(true);
TextView textView = new TextView(this);
textView.setVisibility(View.VISIBLE);
textView.setBackgroundColor(Color.WHITE);
textView.setPadding(15, 15, 15, 15);
textView.setText("Marker View");
mChart.setDrawMarkerView(true);
mChart.setMarkerView(textView);
initWithDummyData();
}
private void initWithDummyData() {
ArrayList<String> xVals = new ArrayList<String>();
for (int i = 0; i < 25; i++) {
xVals.add((i) + ":00");
}
ArrayList<Entry> yVals = new ArrayList<Entry>();
// create a dataset and give it a type (0)
DataSet set1 = new DataSet(yVals, 0);
ArrayList<DataSet> dataSets = new ArrayList<DataSet>();
dataSets.add(set1); // add the datasets
// create a data object with the datasets
ChartData data = new ChartData(xVals, dataSets);
mChart.setData(data);
mChart.invalidate();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.line, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.actionToggleRound: {
if (mChart.isYLegendRounded())
mChart.setRoundedYLegend(false);
else
mChart.setRoundedYLegend(true);
mChart.invalidate();
break;
}
case R.id.actionToggleValues: {
if (mChart.isDrawYValuesEnabled())
mChart.setDrawYValues(false);
else
mChart.setDrawYValues(true);
mChart.invalidate();
break;
}
case R.id.actionToggleHighlight: {
if (mChart.isHighlightEnabled())
mChart.setHighlightEnabled(false);
else
mChart.setHighlightEnabled(true);
mChart.invalidate();
break;
}
case R.id.actionToggleFilled: {
if (mChart.isDrawFilledEnabled())
mChart.setDrawFilled(false);
else
mChart.setDrawFilled(true);
mChart.invalidate();
break;
}
case R.id.actionToggleCircles: {
if (mChart.isDrawCirclesEnabled())
mChart.setDrawCircles(false);
else
mChart.setDrawCircles(true);
mChart.invalidate();
break;
}
case R.id.actionToggleStartzero: {
if (mChart.isStartAtZeroEnabled())
mChart.setStartAtZero(false);
else
mChart.setStartAtZero(true);
mChart.invalidate();
break;
}
case R.id.actionToggleAdjustXLegend: {
if (mChart.isAdjustXLegendEnabled())
mChart.setAdjustXLegend(false);
else
mChart.setAdjustXLegend(true);
mChart.invalidate();
break;
}
case R.id.actionSave: {
// mChart.saveToGallery("title"+System.currentTimeMillis());
mChart.saveToPath("title" + System.currentTimeMillis(), "");
break;
}
}
return true;
}
@Override
public void onValuesSelected(Entry[] values, Highlight[] highlights) {
Log.i("VALS SELECTED", "Value: " + values[0].getVal() + ", xIndex: " + highlights[0].getXIndex()
+ ", DataSet index: " + highlights[0].getDataSetIndex());
}
@Override
public void onNothingSelected() {
// TODO Auto-generated method stub
}
}

View file

@ -1,4 +1,3 @@
package com.example.mpchartexample;
import android.app.Activity;
@ -11,64 +10,69 @@ import android.view.WindowManager;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
Button btn1 = (Button) findViewById(R.id.button1);
Button btn2 = (Button) findViewById(R.id.button2);
Button btn3 = (Button) findViewById(R.id.button3);
Button btn4 = (Button) findViewById(R.id.button4);
Button btn5 = (Button) findViewById(R.id.button5);
Button btn6 = (Button) findViewById(R.id.button6);
Button btn7 = (Button) findViewById(R.id.button7);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
btn4.setOnClickListener(this);
btn5.setOnClickListener(this);
btn6.setOnClickListener(this);
btn7.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent i;
switch(v.getId()) {
case R.id.button1:
i = new Intent(this, LineChartActivity.class);
startActivity(i);
break;
case R.id.button2:
i = new Intent(this, BarChartActivity.class);
startActivity(i);
break;
case R.id.button3:
i = new Intent(this, PieChartActivity.class);
startActivity(i);
break;
case R.id.button4:
i = new Intent(this, MultiLineChartActivity.class);
startActivity(i);
break;
case R.id.button5:
i = new Intent(this, BarChartActivityMultiDataset.class);
startActivity(i);
break;
case R.id.button6:
i = new Intent(this, MultipleChartsActivity.class);
startActivity(i);
break;
case R.id.button7:
i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/PhilJay/MPAndroidChart"));
startActivity(i);
break;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
Button btn1 = (Button) findViewById(R.id.button1);
Button btn2 = (Button) findViewById(R.id.button2);
Button btn3 = (Button) findViewById(R.id.button3);
Button btn4 = (Button) findViewById(R.id.button4);
Button btn5 = (Button) findViewById(R.id.button5);
Button btn6 = (Button) findViewById(R.id.button6);
Button btn7 = (Button) findViewById(R.id.button7);
Button btn8 = (Button) findViewById(R.id.button8);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
btn4.setOnClickListener(this);
btn5.setOnClickListener(this);
btn6.setOnClickListener(this);
btn7.setOnClickListener(this);
btn8.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent i;
switch (v.getId()) {
case R.id.button1:
i = new Intent(this, LineChartActivity.class);
startActivity(i);
break;
case R.id.button2:
i = new Intent(this, BarChartActivity.class);
startActivity(i);
break;
case R.id.button3:
i = new Intent(this, PieChartActivity.class);
startActivity(i);
break;
case R.id.button4:
i = new Intent(this, MultiLineChartActivity.class);
startActivity(i);
break;
case R.id.button5:
i = new Intent(this, BarChartActivityMultiDataset.class);
startActivity(i);
break;
case R.id.button6:
i = new Intent(this, MultipleChartsActivity.class);
startActivity(i);
break;
case R.id.button7:
i = new Intent(this, DrawChartActivity.class);
startActivity(i);
break;
case R.id.button8:
i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/PhilJay/MPAndroidChart"));
startActivity(i);
break;
}
}
}

View file

@ -1,4 +1,3 @@
package com.github.mikephil.charting;
import android.graphics.Matrix;
@ -14,261 +13,307 @@ import android.widget.ImageView;
public class BarLineChartTouchListener extends SimpleOnGestureListener implements OnTouchListener {
private static final float MAX_SCALE = Float.MAX_VALUE; // 10f;
private static final float MIN_SCALE = 0.5f;
private static final float MAX_SCALE = Float.MAX_VALUE; // 10f;
private static final float MIN_SCALE = 0.5f;
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
PointF start = new PointF();
PointF mid = new PointF();
PointF start = new PointF();
PointF mid = new PointF();
// We can be in one of these 3 states
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private static final int POSTZOOM = 3;
private static final int LONGPRESS = 4;
// We can be in one of these 3 states
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private static final int POSTZOOM = 3;
private static final int LONGPRESS = 4;
private static final int DRAWING = 5;
private int mode = NONE;
private float oldDist = 1f;
private BarLineChartBase mChart;
private GestureDetector mGestureDetector;
/** if ture, user can draw on the chart */
private boolean mDrawingEnabled = false;
public BarLineChartTouchListener(BarLineChartBase chart, Matrix start) {
this.mChart = chart;
this.matrix = start;
mGestureDetector = new GestureDetector(chart.getContext(), this);
}
private int mode = NONE;
private float oldDist = 1f;
private BarLineChartBase mChart;
@Override
public boolean onTouch(View v, MotionEvent event) {
if(mode == NONE) {
mGestureDetector.onTouchEvent(event);
}
if(!mChart.isDragEnabled()) return true;
private GestureDetector mGestureDetector;
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d("TouchListener", "oldDist=" + oldDist);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
mChart.disableScroll();
Log.d("TouchListener","mode=ZOOM");
}
break;
public BarLineChartTouchListener(BarLineChartBase chart, Matrix start) {
this.mChart = chart;
this.matrix = start;
case MotionEvent.ACTION_UP:
if (mode == NONE) {
}
Log.d("TouchListener","mode=NONE");
mode = NONE;
mChart.enableScroll();
break;
case MotionEvent.ACTION_POINTER_UP:
Log.d("TouchListener","mode=POSTZOOM");
mode = POSTZOOM;
break;
case MotionEvent.ACTION_MOVE:
if (mode == NONE && distance(event.getX(), start.x, event.getY(), start.y) > 25f) {
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d("TouchListener","mode=DRAG");
mode = DRAG;
mChart.disableScroll();
} else if (mode == DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, 0);
} else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d("TouchListener","newDist=" + newDist);
if (newDist > 10f) {
float scale = newDist / oldDist;
float[] values = new float[9];
matrix.getValues(values);
float oldScale = values[0];
if ((scale < 1 || oldScale < mChart.getMaxScale())
&& (scale > 1 || oldScale > MIN_SCALE)) {
matrix.set(savedMatrix);
matrix.postScale(scale, 1, mid.x, mid.y);
}
}
}
else if (mode == LONGPRESS) {
mChart.disableScroll();
}
break;
}
mGestureDetector = new GestureDetector(chart.getContext(), this);
}
// Perform the transformation
matrix = mChart.refreshTouch(matrix);
@Override
public boolean onTouch(View v, MotionEvent event) {
return true; // indicate event was handled
}
if (mode == NONE) {
mGestureDetector.onTouchEvent(event);
}
private PointF calcImagePosition(PointF klick) {
PointF point = new PointF();
Matrix inverse = new Matrix();
matrix.invert(inverse);
float[] pts = new float[2];
float[] values = new float[9];
pts[0] = klick.x;
pts[1] = klick.y;
inverse.mapPoints(pts);
matrix.getValues(values);
Log.d("TouchListener","Pts 0: " + pts[0] + ", Pts 1: " + pts[1]);
point.x = (klick.x - values[Matrix.MTRANS_X]) / values[Matrix.MSCALE_X];
point.y = (klick.y - values[Matrix.MTRANS_Y]) / values[Matrix.MSCALE_Y];
Log.d("TouchListener","Pts X: " + point.x + ", Pts 1: " + point.y);
return point;
}
if (mDrawingEnabled) {
if (mChart instanceof LineChart) {
ChartData data = mChart.getData();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if (mode == NONE) {
mode = DRAWING;
data.createNewDrawingDataSet(1);
Log.i("Drawing", "New drawing data set created");
}
break;
public void resetMatrix(ImageView view) {
matrix.reset();
view.setImageMatrix(matrix);
}
case MotionEvent.ACTION_MOVE:
if (mode == DRAWING) {
int xIndex = ((LineChart) mChart).getXIndexByTouchPoint(event.getX(), event.getY());
// TODO feed the right y value
Entry entry = new Entry(event.getY(), xIndex);
boolean added = data.addNewDrawingEntry(entry);
if (added) {
Log.i("Drawing", "Added entry " + entry.toString());
// TODO it is bad to call prepare all the time
mChart.prepare();
mChart.invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mode == DRAWING) {
data.finishNewDrawingEntry();
mode = NONE;
mChart.prepare();
mChart.invalidate();
Log.i("Drawing", "Drawing finished");
}
break;
private void limitScale() {
// float[] values = new float[9];
// matrix.getValues(values);
// float sX = values[Matrix.MSCALE_X];
//
// float minScale = Math.max(minScale(), sX);
// values[Matrix.MSCALE_X] = minScale;
// values[Matrix.MSCALE_Y] = minScale;
// matrix.setValues(values);
}
default:
break;
}
// currently no dragging when drawing
return true;
}
} else {
// public void moveTo(float x, float y) {
// matrix.postTranslate(x, y);
// limitPan();
// view.setImageMatrix(matrix);
// view.invalidate();
// }
//
// private float minScale() {
// return Math.max(view.getWidth() / PlanModel.imageWidth, view.getHeight()
// / PlanModel.imageHeight);
// }
if (!mChart.isDragEnabled())
return true;
// public void setMatrixMinScale() {
// DisplayMetrics metrics = new DisplayMetrics();
// ctx.getWindowManager().getDefaultDisplay().getMetrics(metrics);
//
// float minScale = Math.max(metrics.widthPixels / PlanModel.imageWidth,
// (metrics.heightPixels - (50 + 25) * metrics.density)
// / PlanModel.imageHeight);
// matrix.setScale(minScale, minScale);
// view.setImageMatrix(matrix);
// }
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d("TouchListener", "oldDist=" + oldDist);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
mChart.disableScroll();
Log.d("TouchListener", "mode=ZOOM");
}
break;
private static float distance(float eventX, float startX, float eventY, float startY) {
float dx = eventX - startX;
float dy = eventY - startY;
return (float) Math.sqrt(dx * dx + dy * dy);
}
case MotionEvent.ACTION_UP:
if (mode == NONE) {
}
Log.d("TouchListener", "mode=NONE");
mode = NONE;
mChart.enableScroll();
break;
case MotionEvent.ACTION_POINTER_UP:
Log.d("TouchListener", "mode=POSTZOOM");
mode = POSTZOOM;
break;
case MotionEvent.ACTION_MOVE:
if (mode == NONE && distance(event.getX(), start.x, event.getY(), start.y) > 25f) {
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d("TouchListener", "mode=DRAG");
mode = DRAG;
mChart.disableScroll();
} else if (mode == DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, 0);
} else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d("TouchListener", "newDist=" + newDist);
if (newDist > 10f) {
float scale = newDist / oldDist;
float[] values = new float[9];
matrix.getValues(values);
float oldScale = values[0];
if ((scale < 1 || oldScale < mChart.getMaxScale()) && (scale > 1 || oldScale > MIN_SCALE)) {
matrix.set(savedMatrix);
matrix.postScale(scale, 1, mid.x, mid.y);
}
}
} else if (mode == LONGPRESS) {
mChart.disableScroll();
}
break;
}
}
private static void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
// Perform the transformation
matrix = mChart.refreshTouch(matrix);
private static float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
return true; // indicate event was handled
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = {
"DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?"
};
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append(", dist: " + distance(event.getX(), start.x, event.getY(), start.y) + "]");
Log.d("TouchListener",sb.toString());
}
public void setDrawingEnabled(boolean mDrawingEnabled) {
this.mDrawingEnabled = mDrawingEnabled;
}
public Matrix getMatrix() {
return matrix;
}
private PointF calcImagePosition(PointF klick) {
PointF point = new PointF();
Matrix inverse = new Matrix();
matrix.invert(inverse);
float[] pts = new float[2];
float[] values = new float[9];
pts[0] = klick.x;
pts[1] = klick.y;
inverse.mapPoints(pts);
matrix.getValues(values);
Log.d("TouchListener", "Pts 0: " + pts[0] + ", Pts 1: " + pts[1]);
point.x = (klick.x - values[Matrix.MTRANS_X]) / values[Matrix.MSCALE_X];
point.y = (klick.y - values[Matrix.MTRANS_Y]) / values[Matrix.MSCALE_Y];
Log.d("TouchListener", "Pts X: " + point.x + ", Pts 1: " + point.y);
return point;
}
// @Override
// public boolean onDoubleTap(MotionEvent e) {
//
// float[] values = new float[9];
// matrix.getValues(values);
// float sX = values[Matrix.MSCALE_X];
// float minScale = minScale();
//
// if (sX > minScale * 1.5f) {
// matrix.postScale(0.5f, 0.5f, e.getX(), e.getY());
// } else {
// matrix.postScale(2, 2, e.getX(), e.getY());
// }
// limitScale();
// limitPan();
// ctx.update();
//
// return true;
// }
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
public void resetMatrix(ImageView view) {
matrix.reset();
view.setImageMatrix(matrix);
}
Highlight h = mChart.getIndexByTouchPoint(e.getX(), e.getY());
mChart.highlightValues(new Highlight[] { h });
private void limitScale() {
// float[] values = new float[9];
// matrix.getValues(values);
// float sX = values[Matrix.MSCALE_X];
//
// float minScale = Math.max(minScale(), sX);
// values[Matrix.MSCALE_X] = minScale;
// values[Matrix.MSCALE_Y] = minScale;
// matrix.setValues(values);
}
return super.onSingleTapConfirmed(e);
}
// public void moveTo(float x, float y) {
// matrix.postTranslate(x, y);
// limitPan();
// view.setImageMatrix(matrix);
// view.invalidate();
// }
//
// private float minScale() {
// return Math.max(view.getWidth() / PlanModel.imageWidth, view.getHeight()
// / PlanModel.imageHeight);
// }
@Override
public void onLongPress(MotionEvent arg0) {
if (mode == NONE) {
mode = LONGPRESS;
// ctx.showValue(arg0, matrix);
}
};
// public void setMatrixMinScale() {
// DisplayMetrics metrics = new DisplayMetrics();
// ctx.getWindowManager().getDefaultDisplay().getMetrics(metrics);
//
// float minScale = Math.max(metrics.widthPixels / PlanModel.imageWidth,
// (metrics.heightPixels - (50 + 25) * metrics.density)
// / PlanModel.imageHeight);
// matrix.setScale(minScale, minScale);
// view.setImageMatrix(matrix);
// }
@Override
public boolean onSingleTapUp(MotionEvent e) {
// ctx.showValue(e, matrix);
return true;
}
private static float distance(float eventX, float startX, float eventY, float startY) {
float dx = eventX - startX;
float dy = eventY - startY;
return (float) Math.sqrt(dx * dx + dy * dy);
}
private static void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
private static float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append(", dist: " + distance(event.getX(), start.x, event.getY(), start.y) + "]");
Log.d("TouchListener", sb.toString());
}
public Matrix getMatrix() {
return matrix;
}
// @Override
// public boolean onDoubleTap(MotionEvent e) {
//
// float[] values = new float[9];
// matrix.getValues(values);
// float sX = values[Matrix.MSCALE_X];
// float minScale = minScale();
//
// if (sX > minScale * 1.5f) {
// matrix.postScale(0.5f, 0.5f, e.getX(), e.getY());
// } else {
// matrix.postScale(2, 2, e.getX(), e.getY());
// }
// limitScale();
// limitPan();
// ctx.update();
//
// return true;
// }
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY());
mChart.highlightValues(new Highlight[] { h });
return super.onSingleTapConfirmed(e);
}
@Override
public void onLongPress(MotionEvent arg0) {
if (mode == NONE) {
mode = LONGPRESS;
// ctx.showValue(arg0, matrix);
}
};
@Override
public boolean onSingleTapUp(MotionEvent e) {
// ctx.showValue(e, matrix);
return true;
}
}

View file

@ -1,8 +1,11 @@
package com.github.mikephil.charting;
import java.util.ArrayList;
import com.github.mikephil.charting.exception.DrawingDataSetNotCreatedException;
import android.util.Log;
/**
* Class that holds all relevant data that represents the chart
*
@ -10,215 +13,312 @@ import java.util.ArrayList;
*/
public class ChartData {
/** maximum y-value in the y-value array */
private float mYMax = 0.0f;
/** maximum y-value in the y-value array */
private float mYMax = 0.0f;
/** the minimum y-value in the y-value array */
private float mYMin = 0.0f;
/** the minimum y-value in the y-value array */
private float mYMin = 0.0f;
/** the total sum of all y-values */
private float mYValueSum = 0f;
/** the total sum of all y-values */
private float mYValueSum = 0f;
/** holds all x-values the chart represents */
private ArrayList<String> mXVals;
/** holds all x-values the chart represents */
private ArrayList<String> mXVals;
/** holds all the datasets (e.g. different lines) the chart represents */
private ArrayList<DataSet> mDataSets;
/** holds all the datasets (e.g. different lines) the chart represents */
private ArrayList<DataSet> mDataSets;
/** array that holds all the different type ids that are in the series array */
private ArrayList<Integer> mDiffTypes;
/** holds a DataSet that can be manipulated on the go, to allow users to draw into the chart */
private DataSet mCurrentDrawingDataSet;
/**
* constructor for chart data
*
* @param xVals must be at least as long as the highest xIndex in the Series
* objects across all DataSets
* @param dataSets all DataSet objects the chart needs to represent
*/
public ChartData(ArrayList<String> xVals, ArrayList<DataSet> dataSets) {
this.mXVals = xVals;
this.mDataSets = dataSets;
private ArrayList<Entry> mCurrentDrawingEntries;
calcTypes();
calcMinMax();
calcYValueSum();
/** array that holds all the different type ids that are in the series array */
private ArrayList<Integer> mDiffTypes;
for (int i = 0; i < mDataSets.size(); i++) {
if (mDataSets.get(i).getYVals().size() > xVals.size()) {
throw new IllegalArgumentException(
"x values are smaller than the largest y series array of one type");
}
}
}
/**
* constructor for chart data
*
* @param xVals
* must be at least as long as the highest xIndex in the Series objects across all DataSets
* @param dataSets
* all DataSet objects the chart needs to represent
*/
public ChartData(ArrayList<String> xVals, ArrayList<DataSet> dataSets) {
this.mXVals = xVals;
this.mDataSets = dataSets;
/**
* calculates all different types that occur in the datasets and stores them
* for fast access
*/
private void calcTypes() {
mDiffTypes = new ArrayList<Integer>();
calcTypes();
calcMinMax();
calcYValueSum();
for (int i = 0; i < mDataSets.size(); i++) {
for (int i = 0; i < mDataSets.size(); i++) {
if (mDataSets.get(i).getYVals().size() > xVals.size()) {
throw new IllegalArgumentException("x values are smaller than the largest y series array of one type");
}
}
}
int type = mDataSets.get(i).getType();
/**
* Call this method to let the CartData know that the underlying data has changed
*/
public void notifyDataChanged() {
calcTypes();
calcMinMax();
calcYValueSum();
}
if (!alreadyCounted(mDiffTypes, type)) {
mDiffTypes.add(type);
}
}
}
/**
* calculates all different types that occur in the datasets and stores them for fast access
*/
private void calcTypes() {
mDiffTypes = new ArrayList<Integer>();
/**
* calc minimum and maximum y value over all datasets
*/
private void calcMinMax() {
for (int i = 0; i < mDataSets.size(); i++) {
mYMin = mDataSets.get(0).getYMin();
mYMax = mDataSets.get(0).getYMax();
int type = mDataSets.get(i).getType();
for (int i = 0; i < mDataSets.size(); i++) {
if (mDataSets.get(i).getYMin() < mYMin)
mYMin = mDataSets.get(i).getYMin();
if (!alreadyCounted(mDiffTypes, type)) {
mDiffTypes.add(type);
}
}
}
if (mDataSets.get(i).getYMax() > mYMax)
mYMax = mDataSets.get(i).getYMax();
}
}
/**
* calc minimum and maximum y value over all datasets
*/
private void calcMinMax() {
/**
* calculates the sum of all y-values in all datasets
*/
private void calcYValueSum() {
mYMin = mDataSets.get(0).getYMin();
mYMax = mDataSets.get(0).getYMax();
mYValueSum = 0;
for (int i = 0; i < mDataSets.size(); i++) {
if (mDataSets.get(i).getYMin() < mYMin)
mYMin = mDataSets.get(i).getYMin();
for (int i = 0; i < mDataSets.size(); i++) {
mYValueSum += Math.abs(mDataSets.get(i).getYValueSum());
}
}
if (mDataSets.get(i).getYMax() > mYMax)
mYMax = mDataSets.get(i).getYMax();
}
}
private boolean alreadyCounted(ArrayList<Integer> countedTypes, int type) {
for (int i = 0; i < countedTypes.size(); i++) {
if (countedTypes.get(i) == type)
return true;
}
/**
* calculates the sum of all y-values in all datasets
*/
private void calcYValueSum() {
return false;
}
mYValueSum = 0;
public int getDataSetCount() {
return mDataSets.size();
}
for (int i = 0; i < mDataSets.size(); i++) {
mYValueSum += Math.abs(mDataSets.get(i).getYValueSum());
}
}
public float getYMin() {
return mYMin;
}
/**
* Call this method to create a new drawing DataSet
*
* @param type
* the type of the new DataSet
*/
public void createNewDrawingDataSet(int type) {
if (mCurrentDrawingDataSet != null && mCurrentDrawingEntries != null) {
// if an old one exist, finish the other one first
finishNewDrawingEntry();
}
// keep type count correct
if (!mDiffTypes.contains(type)) {
mDiffTypes.add(type);
}
mCurrentDrawingEntries = new ArrayList<Entry>();
this.mCurrentDrawingDataSet = new DataSet(mCurrentDrawingEntries, type);
mDataSets.add(mCurrentDrawingDataSet);
}
public float getYMax() {
return mYMax;
}
/**
* Add a new entry.
*
* @param entry
* @return true if entry added, false if an entry on this x index already existed
*/
public boolean addNewDrawingEntry(Entry entry) {
if (mCurrentDrawingDataSet != null && mCurrentDrawingEntries != null) {
// only add if no value for this x already exist
for (int i = 0; i < mCurrentDrawingEntries.size(); i++) {
if (mCurrentDrawingEntries.get(i).getXIndex() == entry.getXIndex()) {
return false;
}
}
mCurrentDrawingEntries.add(entry);
correctDataForNewEntry(entry);
mCurrentDrawingDataSet.notifyDataSetChanged();
return true;
} else {
// new data set has to be created first
throw new DrawingDataSetNotCreatedException();
}
}
public float getYValueSum() {
return mYValueSum;
}
/**
* Finishes a drawing entry and adds values at the beginning and the end to fill up the line
*/
public void finishNewDrawingEntry() {
Entry firstEntry = mCurrentDrawingEntries.get(0);
int xIndex = 0;
while (xIndex < firstEntry.getXIndex()) {
Entry entry = new Entry(firstEntry.getVal(), xIndex);
mCurrentDrawingEntries.add(xIndex, entry);
correctDataForNewEntry(entry);
xIndex++;
}
Entry lastEntry = mCurrentDrawingEntries.get(mCurrentDrawingEntries.size() - 1);
xIndex = lastEntry.getXIndex();
while (xIndex < getXValCount()) {
Entry entry = new Entry(lastEntry.getVal(), xIndex);
mCurrentDrawingEntries.add(entry);
correctDataForNewEntry(entry);
xIndex++;
}
mCurrentDrawingDataSet.notifyDataSetChanged();
mCurrentDrawingDataSet = null;
mCurrentDrawingEntries = null;
}
/**
* Checks if the ChartData object contains valid data
*
* @return
*/
public boolean isValid() {
private boolean alreadyCounted(ArrayList<Integer> countedTypes, int type) {
for (int i = 0; i < countedTypes.size(); i++) {
if (countedTypes.get(i) == type)
return true;
}
if (mXVals == null || mXVals.size() <= 1 || mDataSets == null || mDataSets.size() < 1
|| mDataSets.get(0).getYVals().size() <= 1)
return false;
else
return true;
}
return false;
}
/**
* returns the x-values the chart represents
*
* @return
*/
public ArrayList<String> getXVals() {
return mXVals;
}
/**
* Corrects all values that are kept as member variables after a new entry was added. This saves recalculating all
* values.
*
* @param entry
* the new entry
*/
private void correctDataForNewEntry(Entry entry) {
mYValueSum += Math.abs(entry.getVal());
if (mYMin > entry.getVal()) {
mYMin = entry.getVal();
}
if (mYMax < entry.getVal()) {
mYMax = entry.getVal();
}
}
/**
* returns the Entries array from the DataSet at the given index
*
* @param index
* @return
*/
public ArrayList<Entry> getYVals(int index) {
return mDataSets.get(index).getYVals();
}
public int getDataSetCount() {
return mDataSets.size();
}
/**
* returns the dataset at the given index
*
* @param index
* @return
*/
public DataSet getDataSetByIndex(int index) {
return mDataSets.get(index);
}
public float getYMin() {
return mYMin;
}
/**
* retrieve a dataset with a specific type from the chartdata
*
* @param type
* @return
*/
public DataSet getDataSetByType(int type) {
for (int i = 0; i < mDataSets.size(); i++)
if (type == mDataSets.get(i).getType())
return mDataSets.get(i);
public float getYMax() {
return mYMax;
}
return null;
}
public float getYValueSum() {
return mYValueSum;
}
/**
* returns all DataSet objects the ChartData represents.
*
* @return
*/
public ArrayList<DataSet> getDataSets() {
return mDataSets;
}
/**
* Checks if the ChartData object contains valid data
*
* @return
*/
public boolean isValid() {
/**
* returns all the different DataSet types the chartdata represents
*
* @return
*/
public ArrayList<Integer> getTypes() {
return mDiffTypes;
}
if (mXVals == null || mXVals.size() <= 1 || mDataSets == null || mDataSets.size() < 1)
// || mDataSets.get(0).getYVals().size() <= 1)
return false;
else
return true;
}
/**
* returns the total number of x-values this chartdata represents (the size
* of the xvals array)
*
* @return
*/
public int getXValCount() {
return mXVals.size();
}
/**
* returns the x-values the chart represents
*
* @return
*/
public ArrayList<String> getXVals() {
return mXVals;
}
/**
* returns the total number of y-values across all DataSets the chartdata
* represents
*
* @return
*/
public int getYValCount() {
int count = 0;
for (int i = 0; i < mDataSets.size(); i++) {
count += mDataSets.get(i).getEntryCount();
}
/**
* returns the Entries array from the DataSet at the given index
*
* @param index
* @return
*/
public ArrayList<Entry> getYVals(int index) {
return mDataSets.get(index).getYVals();
}
return count;
}
/**
* returns the dataset at the given index
*
* @param index
* @return
*/
public DataSet getDataSetByIndex(int index) {
return mDataSets.get(index);
}
/**
* retrieve a dataset with a specific type from the chartdata
*
* @param type
* @return
*/
public DataSet getDataSetByType(int type) {
for (int i = 0; i < mDataSets.size(); i++)
if (type == mDataSets.get(i).getType())
return mDataSets.get(i);
return null;
}
/**
* returns all DataSet objects the ChartData represents.
*
* @return
*/
public ArrayList<DataSet> getDataSets() {
return mDataSets;
}
/**
* returns all the different DataSet types the chartdata represents
*
* @return
*/
public ArrayList<Integer> getTypes() {
return mDiffTypes;
}
/**
* returns the total number of x-values this chartdata represents (the size of the xvals array)
*
* @return
*/
public int getXValCount() {
return mXVals.size();
}
/**
* returns the total number of y-values across all DataSets the chartdata represents
*
* @return
*/
public int getYValCount() {
int count = 0;
for (int i = 0; i < mDataSets.size(); i++) {
count += mDataSets.get(i).getEntryCount();
}
return count;
}
}

View file

@ -1,218 +1,215 @@
package com.github.mikephil.charting;
import java.util.ArrayList;
/**
* The DataSet class represents one group or type of entries (Entry) in the
* Chart that belong together. It is designed to logically separate different
* groups of values inside the Chart (e.g. the values for a specific line in the
* The DataSet class represents one group or type of entries (Entry) in the Chart that belong together. It is designed
* to logically separate different groups of values inside the Chart (e.g. the values for a specific line in the
* LineChart, or the values of a specific group of bars in the BarChart).
*
* @author Philipp Jahoda
*/
public class DataSet {
/** the entries that this dataset represents / holds together */
private ArrayList<Entry> mYVals;
/** the entries that this dataset represents / holds together */
private ArrayList<Entry> mYVals;
/** maximum y-value in the y-value array */
private float mYMax = 0.0f;
/** maximum y-value in the y-value array */
private float mYMax = 0.0f;
/** the minimum y-value in the y-value array */
private float mYMin = 0.0f;
/** the minimum y-value in the y-value array */
private float mYMin = 0.0f;
/** the total sum of all y-values */
private float mYValueSum = 0f;
/** the total sum of all y-values */
private float mYValueSum = 0f;
/** type, used for identification amongst other DataSets */
private int mType = 0;
/** type, used for identification amongst other DataSets */
private int mType = 0;
/**
* Creates a new DataSet object with the given values it represents and a
* type for identification amongst other DataSet objects (the type can be
* chosen freely and must not be equal to another type in the ChartData
* object).
*
* @param yVals
* @param type
*/
public DataSet(ArrayList<Entry> yVals, int type) {
this.mType = type;
this.mYVals = yVals;
/**
* Creates a new DataSet object with the given values it represents and a type for identification amongst other
* DataSet objects (the type can be chosen freely and must not be equal to another type in the ChartData object).
*
* @param yVals
* @param type
*/
public DataSet(ArrayList<Entry> yVals, int type) {
this.mType = type;
this.mYVals = yVals;
if (yVals == null || yVals.size() <= 0)
return;
if (yVals == null || yVals.size() <= 0)
return;
calcMinMax();
calcYValueSum();
}
calcMinMax();
calcYValueSum();
}
/**
* calc minimum and maximum y value
*/
private void calcMinMax() {
/**
* Use this method to tell the data set that the underlying data has changed
*/
public void notifyDataSetChanged() {
calcMinMax();
calcYValueSum();
}
mYMin = mYVals.get(0).getVal();
mYMax = mYVals.get(0).getVal();
/**
* calc minimum and maximum y value
*/
private void calcMinMax() {
for (int i = 0; i < mYVals.size(); i++) {
if (mYVals.get(i).getVal() < mYMin)
mYMin = mYVals.get(i).getVal();
mYMin = mYVals.get(0).getVal();
mYMax = mYVals.get(0).getVal();
if (mYVals.get(i).getVal() > mYMax)
mYMax = mYVals.get(i).getVal();
}
}
for (int i = 0; i < mYVals.size(); i++) {
if (mYVals.get(i).getVal() < mYMin)
mYMin = mYVals.get(i).getVal();
/**
* calculates the sum of all y-values
*/
private void calcYValueSum() {
if (mYVals.get(i).getVal() > mYMax)
mYMax = mYVals.get(i).getVal();
}
}
mYValueSum = 0;
/**
* calculates the sum of all y-values
*/
private void calcYValueSum() {
for (int i = 0; i < mYVals.size(); i++) {
mYValueSum += Math.abs(mYVals.get(i).getVal());
}
}
mYValueSum = 0;
/**
* returns the number of y-values this DataSet represents
*
* @return
*/
public int getEntryCount() {
return mYVals.size();
}
for (int i = 0; i < mYVals.size(); i++) {
mYValueSum += Math.abs(mYVals.get(i).getVal());
}
}
/**
* Returns the value of the Entry object at the given xIndex. Returns
* Float.NaN if no value is at the given x-index. INFORMATION: This method
* does calculations at runtime. Do not over-use in performance critical
* situations.
*
* @param xIndex
* @return
*/
public float getYValForXIndex(int xIndex) {
/**
* returns the number of y-values this DataSet represents
*
* @return
*/
public int getEntryCount() {
return mYVals.size();
}
Entry s = getEntryForXIndex(xIndex);
/**
* Returns the value of the Entry object at the given xIndex. Returns Float.NaN if no value is at the given x-index.
* INFORMATION: This method does calculations at runtime. Do not over-use in performance critical situations.
*
* @param xIndex
* @return
*/
public float getYValForXIndex(int xIndex) {
if (s != null)
return s.getVal();
else
return Float.NaN;
}
Entry s = getEntryForXIndex(xIndex);
/**
* Returns the Entry object at the given xIndex. Returns null if no Entry
* object at that index. INFORMATION: This method does calculations at
* runtime. Do not over-use in performance critical situations.
*
* @param xIndex
* @return
*/
public Entry getEntryForXIndex(int xIndex) {
if (s != null)
return s.getVal();
else
return Float.NaN;
}
for (int i = 0; i < mYVals.size(); i++) {
if (xIndex == mYVals.get(i).getXIndex())
return mYVals.get(i);
}
/**
* Returns the Entry object at the given xIndex. Returns null if no Entry object at that index. INFORMATION: This
* method does calculations at runtime. Do not over-use in performance critical situations.
*
* @param xIndex
* @return
*/
public Entry getEntryForXIndex(int xIndex) {
return null;
}
for (int i = 0; i < mYVals.size(); i++) {
if (xIndex == mYVals.get(i).getXIndex())
return mYVals.get(i);
}
/**
* returns the DataSets Entry array
*
* @return
*/
public ArrayList<Entry> getYVals() {
return mYVals;
}
return null;
}
/**
* gets the sum of all y-values
*
* @return
*/
public float getYValueSum() {
return mYValueSum;
}
/**
* returns the DataSets Entry array
*
* @return
*/
public ArrayList<Entry> getYVals() {
return mYVals;
}
/**
* returns the minimum y-value this DataSet holds
*
* @return
*/
public float getYMin() {
return mYMin;
}
/**
* gets the sum of all y-values
*
* @return
*/
public float getYValueSum() {
return mYValueSum;
}
/**
* returns the maximum y-value this DataSet holds
*
* @return
*/
public float getYMax() {
return mYMax;
}
/**
* returns the minimum y-value this DataSet holds
*
* @return
*/
public float getYMin() {
return mYMin;
}
/**
* returns the type of the DataSet, specified via constructor
*
* @return
*/
public int getType() {
return mType;
}
/**
* returns the maximum y-value this DataSet holds
*
* @return
*/
public float getYMax() {
return mYMax;
}
/**
* The xIndex of an Entry object is provided. This method returns the actual
* index in the Entry array of the DataSet. IMPORTANT: This method does
* calculations at runtime, do not over-use in performance critical
* situations.
*
* @param xIndex
* @return
*/
public int getIndexInEntries(int xIndex) {
/**
* returns the type of the DataSet, specified via constructor
*
* @return
*/
public int getType() {
return mType;
}
for (int i = 0; i < mYVals.size(); i++) {
if (xIndex == mYVals.get(i).getXIndex())
return i;
}
/**
* The xIndex of an Entry object is provided. This method returns the actual index in the Entry array of the
* DataSet. IMPORTANT: This method does calculations at runtime, do not over-use in performance critical situations.
*
* @param xIndex
* @return
*/
public int getIndexInEntries(int xIndex) {
return -1;
}
for (int i = 0; i < mYVals.size(); i++) {
if (xIndex == mYVals.get(i).getXIndex())
return i;
}
/**
* Convenience method to create multiple DataSets of different types with
* various double value arrays. Each double array represents the data of one
* DataSet with a type created by this method, starting at 0 (and
* incremented).
*
* @param yValues
* @return
*/
public static ArrayList<DataSet> makeDataSets(ArrayList<Double[]> yValues) {
return -1;
}
ArrayList<DataSet> dataSets = new ArrayList<DataSet>();
/**
* Convenience method to create multiple DataSets of different types with various double value arrays. Each double
* array represents the data of one DataSet with a type created by this method, starting at 0 (and incremented).
*
* @param yValues
* @return
*/
public static ArrayList<DataSet> makeDataSets(ArrayList<Double[]> yValues) {
for (int i = 0; i < yValues.size(); i++) {
ArrayList<DataSet> dataSets = new ArrayList<DataSet>();
Double[] curValues = yValues.get(i);
for (int i = 0; i < yValues.size(); i++) {
ArrayList<Entry> entries = new ArrayList<Entry>();
Double[] curValues = yValues.get(i);
for (int j = 0; j < curValues.length; j++) {
entries.add(new Entry(curValues[j].floatValue(), j));
}
ArrayList<Entry> entries = new ArrayList<Entry>();
dataSets.add(new DataSet(entries, i));
}
for (int j = 0; j < curValues.length; j++) {
entries.add(new Entry(curValues[j].floatValue(), j));
}
return dataSets;
}
dataSets.add(new DataSet(entries, i));
}
return dataSets;
}
}

View file

@ -0,0 +1,15 @@
package com.github.mikephil.charting;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class DrawLineChartTouchListener extends SimpleOnGestureListener implements OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
}

View file

@ -1,6 +1,5 @@
package com.github.mikephil.charting;
public class Entry {
private float mVal = 0f;
@ -21,66 +20,74 @@ public class Entry {
/**
* returns the x-index the value of this object is mapped to
*
* @return
*/
public int getXIndex() {
return mXIndex;
}
//
// /**
// * Convenience method to create a series of double values
// *
// * @param yValues
// * @return
// */
// public static ArrayList<Series> makeSeries(double[] yValues) {
// ArrayList<Series> series = new ArrayList<Series>();
// for (int i = 0; i < yValues.length; i++) {
// series.add(new Series((float) yValues[i], 0, i));
// }
// return series;
// }
//
// /**
// * Convenience method to create multiple series of different types of various double value arrays. Each double array
// * represents one type starting at 0.
// *
// * @param yValues
// * @return
// */
// public static ArrayList<Series> makeMultipleSeries(ArrayList<Double[]> yValues) {
// ArrayList<Series> series = new ArrayList<Series>();
//
// int sizeOfFirst = yValues.get(0).length;
//
// for (int i = 0; i < yValues.size(); i++) {
// Double[] curValues = yValues.get(i);
// if (curValues.length != sizeOfFirst) {
// throw new IllegalArgumentException("Array sizes do not match");
// }
// for (int j = 0; j < curValues.length; j++) {
// series.add(new Series(curValues[j].floatValue(), i, j));
// }
// }
//
// return series;
// }
//
// /**
// * Convenience method to add a series. The new series has to be the same size as the old. If you want to create a
// * different sized series, please add manually.
// *
// * @param series
// * @param yValues
// * @param type
// */
// public static void addSeries(ArrayList<Series> series, double[] yValues, int type) {
// for (int i = 0; i < yValues.length; i++) {
// series.add(new Series((float) yValues[i], type, i));
// }
// }
//
// /**
// * Convenience method to create a series of double values
// *
// * @param yValues
// * @return
// */
// public static ArrayList<Series> makeSeries(double[] yValues) {
// ArrayList<Series> series = new ArrayList<Series>();
// for (int i = 0; i < yValues.length; i++) {
// series.add(new Series((float) yValues[i], 0, i));
// }
// return series;
// }
//
// /**
// * Convenience method to create multiple series of different types of various double value arrays. Each double
// array
// * represents one type starting at 0.
// *
// * @param yValues
// * @return
// */
// public static ArrayList<Series> makeMultipleSeries(ArrayList<Double[]> yValues) {
// ArrayList<Series> series = new ArrayList<Series>();
//
// int sizeOfFirst = yValues.get(0).length;
//
// for (int i = 0; i < yValues.size(); i++) {
// Double[] curValues = yValues.get(i);
// if (curValues.length != sizeOfFirst) {
// throw new IllegalArgumentException("Array sizes do not match");
// }
// for (int j = 0; j < curValues.length; j++) {
// series.add(new Series(curValues[j].floatValue(), i, j));
// }
// }
//
// return series;
// }
//
// /**
// * Convenience method to add a series. The new series has to be the same size as the old. If you want to create a
// * different sized series, please add manually.
// *
// * @param series
// * @param yValues
// * @param type
// */
// public static void addSeries(ArrayList<Series> series, double[] yValues, int type) {
// for (int i = 0; i < yValues.length; i++) {
// series.add(new Series((float) yValues[i], type, i));
// }
// }
public float getVal() {
return mVal;
}
@Override
public String toString() {
return "xIndex: " + mXIndex + " yVal: " + mVal;
}
}

View file

@ -1,378 +1,423 @@
package com.github.mikephil.charting;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
public class LineChart extends BarLineChartBase {
/** the radius of the circle-shaped value indicators */
protected float mCircleSize = 4f;
/** the radius of the circle-shaped value indicators */
protected float mCircleSize = 4f;
/** the width of the drawn data lines */
protected float mLineWidth = 1f;
/** the width of the drawn data lines */
protected float mLineWidth = 1f;
/** the width of the highlighning line */
protected float mHighlightWidth = 3f;
/** the width of the highlighning line */
protected float mHighlightWidth = 3f;
/** if true, the data will also be drawn filled */
protected boolean mDrawFilled = false;
/** if true, the data will also be drawn filled */
protected boolean mDrawFilled = false;
/** if true, drawing circles is enabled */
protected boolean mDrawCircles = true;
/** if true, drawing circles is enabled */
protected boolean mDrawCircles = true;
/** paint for the filled are (if enabled) below the chart line */
protected Paint mFilledPaint;
/** paint for the filled are (if enabled) below the chart line */
protected Paint mFilledPaint;
/** paint for the inner circle of the value indicators */
protected Paint mCirclePaintInner;
/** paint for the inner circle of the value indicators */
protected Paint mCirclePaintInner;
public LineChart(Context context) {
super(context);
}
public LineChart(Context context) {
super(context);
}
public LineChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LineChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LineChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public LineChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
@Override
protected void init() {
super.init();
mCircleSize = Utils.convertDpToPixel(mCircleSize);
mCircleSize = Utils.convertDpToPixel(mCircleSize);
mFilledPaint = new Paint();
mFilledPaint.setStyle(Paint.Style.FILL);
mFilledPaint.setColor(mColorDarkBlue);
mFilledPaint.setAlpha(130); // alpha ~55%
mFilledPaint = new Paint();
mFilledPaint.setStyle(Paint.Style.FILL);
mFilledPaint.setColor(mColorDarkBlue);
mFilledPaint.setAlpha(130); // alpha ~55%
mCirclePaintInner = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaintInner.setStyle(Paint.Style.FILL);
mCirclePaintInner.setColor(Color.WHITE);
mCirclePaintInner = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaintInner.setStyle(Paint.Style.FILL);
mCirclePaintInner.setColor(Color.WHITE);
mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHighlightPaint.setStyle(Paint.Style.STROKE);
mHighlightPaint.setStrokeWidth(2f);
mHighlightPaint.setColor(Color.rgb(255, 187, 115));
}
mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHighlightPaint.setStyle(Paint.Style.STROKE);
mHighlightPaint.setStrokeWidth(2f);
mHighlightPaint.setColor(Color.rgb(255, 187, 115));
}
@Override
protected void drawHighlights() {
@Override
protected void drawHighlights() {
// if there are values to highlight and highlighnting is enabled, do it
if (mHighlightEnabled && valuesToHighlight()) {
// if there are values to highlight and highlighnting is enabled, do it
if (mHighlightEnabled && valuesToHighlight()) {
for (int i = 0; i < mIndicesToHightlight.length; i++) {
for (int i = 0; i < mIndicesToHightlight.length; i++) {
DataSet set = getDataSetByIndex(mIndicesToHightlight[i].getDataSetIndex());
DataSet set = getDataSetByIndex(mIndicesToHightlight[i].getDataSetIndex());
int xIndex = mIndicesToHightlight[i].getXIndex(); // get the
// x-position
float y = set.getYValForXIndex(xIndex); // get the y-position
int xIndex = mIndicesToHightlight[i].getXIndex(); // get the
// x-position
float y = set.getYValForXIndex(xIndex); // get the y-position
float[] pts = new float[] {
xIndex, mYChartMax, xIndex, mYChartMin, 0,
y, mDeltaX, y
};
float[] pts = new float[] { xIndex, mYChartMax, xIndex, mYChartMin, 0, y, mDeltaX, y };
transformPointArray(pts);
// draw the highlight lines
mDrawCanvas.drawLines(pts, mHighlightPaint);
}
}
}
transformPointArray(pts);
// draw the highlight lines
mDrawCanvas.drawLines(pts, mHighlightPaint);
}
}
}
/**
* draws the given y values to the screen
*/
@Override
protected void drawData() {
/**
* draws the given y values to the screen
*/
@Override
protected void drawData() {
ArrayList<DataSet> dataSets = mData.getDataSets();
ArrayList<DataSet> dataSets = mData.getDataSets();
for (int i = 0; i < mData.getDataSetCount(); i++) {
for (int i = 0; i < mData.getDataSetCount(); i++) {
DataSet dataSet = dataSets.get(i);
ArrayList<Entry> entries = dataSet.getYVals();
DataSet dataSet = dataSets.get(i);
ArrayList<Entry> entries = dataSet.getYVals();
float[] valuePoints = generateTransformedValues(entries, 0f);
float[] valuePoints = generateTransformedValues(entries, 0f);
// Get the colors for the DataSet at the current index. If the index
// is out of bounds, reuse DataSet colors.
ArrayList<Integer> colors = mCt.getDataSetColors(i % mCt.getColors().size());
// Get the colors for the DataSet at the current index. If the index
// is out of bounds, reuse DataSet colors.
ArrayList<Integer> colors = mCt.getDataSetColors(i % mCt.getColors().size());
Paint paint = mRenderPaint;
Paint paint = mRenderPaint;
for (int j = 0; j < valuePoints.length - 2; j += 2) {
for (int j = 0; j < valuePoints.length - 2; j += 2) {
// Set the color for the currently drawn value. If the index is
// out of bounds, reuse colors.
paint.setColor(colors.get(j % colors.size()));
// Set the color for the currently drawn value. If the index is
// out of bounds, reuse colors.
paint.setColor(colors.get(j % colors.size()));
if (isOffContentRight(valuePoints[j]))
break;
if (isOffContentRight(valuePoints[j]))
break;
// make sure the lines don't do shitty things outside bounds
if (j != 0 && isOffContentLeft(valuePoints[j - 1]))
continue;
// make sure the lines don't do shitty things outside bounds
if (j != 0 && isOffContentLeft(valuePoints[j - 1]))
continue;
mDrawCanvas.drawLine(valuePoints[j], valuePoints[j + 1], valuePoints[j + 2],
valuePoints[j + 3], paint);
}
mDrawCanvas.drawLine(valuePoints[j], valuePoints[j + 1], valuePoints[j + 2], valuePoints[j + 3], paint);
}
// if drawing filled is enabled
if (mDrawFilled) {
// mDrawCanvas.drawVertices(VertexMode.TRIANGLE_STRIP,
// valuePoints.length, valuePoints, 0,
// null, 0, null, 0, null, 0, 0, paint);
// if drawing filled is enabled
if (mDrawFilled) {
// mDrawCanvas.drawVertices(VertexMode.TRIANGLE_STRIP,
// valuePoints.length, valuePoints, 0,
// null, 0, null, 0, null, 0, 0, paint);
// filled is drawn with less alpha
paint.setAlpha(85);
// filled is drawn with less alpha
paint.setAlpha(85);
Path filled = new Path();
filled.moveTo(0, entries.get(0).getVal());
Path filled = new Path();
filled.moveTo(0, entries.get(0).getVal());
// create a new path
for (int x = 1; x < entries.size(); x++) {
// create a new path
for (int x = 1; x < entries.size(); x++) {
filled.lineTo(x, entries.get(x).getVal());
}
filled.lineTo(x, entries.get(x).getVal());
}
// close up
filled.lineTo(entries.size() - 1, mYChartMin);
filled.lineTo(0f, mYChartMin);
filled.close();
// close up
filled.lineTo(entries.size() - 1, mYChartMin);
filled.lineTo(0f, mYChartMin);
filled.close();
transformPath(filled);
transformPath(filled);
mDrawCanvas.drawPath(filled, paint);
mDrawCanvas.drawPath(filled, paint);
// restore alpha
paint.setAlpha(255);
}
}
}
// restore alpha
paint.setAlpha(255);
}
}
}
@Override
protected void drawValues() {
@Override
protected void drawValues() {
// if values are drawn
if (mDrawYValues && mData.getYValCount() < mMaxVisibleCount * mScaleX) {
// if values are drawn
if (mDrawYValues && mData.getYValCount() < mMaxVisibleCount * mScaleX) {
// make sure the values do not interfear with the circles
int valOffset = (int) (mCircleSize * 1.7f);
// make sure the values do not interfear with the circles
int valOffset = (int) (mCircleSize * 1.7f);
if (!mDrawCircles)
valOffset = valOffset / 2;
if (!mDrawCircles)
valOffset = valOffset / 2;
ArrayList<DataSet> dataSets = mData.getDataSets();
ArrayList<DataSet> dataSets = mData.getDataSets();
for (int i = 0; i < mData.getDataSetCount(); i++) {
for (int i = 0; i < mData.getDataSetCount(); i++) {
DataSet dataSet = dataSets.get(i);
ArrayList<Entry> entries = dataSet.getYVals();
DataSet dataSet = dataSets.get(i);
ArrayList<Entry> entries = dataSet.getYVals();
float[] positions = generateTransformedValues(entries, 0f);
float[] positions = generateTransformedValues(entries, 0f);
for (int j = 0; j < positions.length; j += 2) {
if (isOffContentRight(positions[j]))
break;
if (isOffContentLeft(positions[j]))
continue;
for (int j = 0; j < positions.length; j += 2) {
float val = entries.get(j / 2).getVal();
if (mDrawUnitInChart) {
mDrawCanvas.drawText(mFormatValue.format(val) + mUnit,
positions[j], positions[j + 1] - valOffset, mValuePaint);
} else {
mDrawCanvas.drawText(mFormatValue.format(val), positions[j],
positions[j + 1] - valOffset, mValuePaint);
}
}
}
}
}
/**
* draws the circle value indicators
*/
@Override
protected void drawAdditional() {
// if drawing circles is enabled
if (mDrawCircles) {
ArrayList<DataSet> dataSets = mData.getDataSets();
for (int i = 0; i < mData.getDataSetCount(); i++) {
DataSet dataSet = dataSets.get(i);
ArrayList<Entry> entries = dataSet.getYVals();
// Get the colors for the DataSet at the current index. If the
// index
// is out of bounds, reuse DataSet colors.
ArrayList<Integer> colors = mCt.getDataSetColors(i % mCt.getColors().size());
float[] positions = generateTransformedValues(entries, 0f);
for (int j = 0; j < positions.length; j += 2) {
// Set the color for the currently drawn value. If the index is
// out of bounds, reuse colors.
mRenderPaint.setColor(colors.get(j % colors.size()));
if (isOffContentRight(positions[j]))
break;
// make sure the circles don't do shitty things outside
// bounds
if (isOffContentLeft(positions[j]))
continue;
mDrawCanvas.drawCircle(positions[j], positions[j + 1], mCircleSize,
mRenderPaint);
mDrawCanvas.drawCircle(positions[j], positions[j + 1], mCircleSize / 2,
mCirclePaintInner);
}
}
}
}
/**
* set this to true to enable the drawing of circle indicators
*
* @param enabled
*/
public void setDrawCircles(boolean enabled) {
this.mDrawCircles = enabled;
}
/**
* returns true if drawing circles is enabled, false if not
*
* @return
*/
public boolean isDrawCirclesEnabled() {
return mDrawCircles;
}
/**
* sets the size (radius) of the circle shpaed value indicators, default
* size = 4f
*
* @param size
*/
public void setCircleSize(float size) {
mCircleSize = Utils.convertDpToPixel(size);
}
/**
* returns the circlesize
*
* @param size
*/
public float getCircleSize(float size) {
return Utils.convertPixelsToDp(mCircleSize);
}
/**
* set if the chartdata should be drawn as a line or filled default = line /
* default = false, disabling this will give up to 20% performance boost on
* large datasets
*
* @param filled
*/
public void setDrawFilled(boolean filled) {
mDrawFilled = filled;
}
/**
* returns true if filled drawing is enabled, false if not
*
* @return
*/
public boolean isDrawFilledEnabled() {
return mDrawFilled;
}
/**
* set the line width of the chart (min = 0.5f, max = 10f); default 1f NOTE:
* thinner line == better performance, thicker line == worse performance
*
* @param width
*/
public void setLineWidth(float width) {
if (width < 0.5f)
width = 0.5f;
if (width > 10.0f)
width = 10.0f;
mLineWidth = width;
mRenderPaint.setStrokeWidth(width);
}
/**
* returns the width of the drawn chart line
*
* @return
*/
public float getLineWidth() {
return mLineWidth;
}
/**
* sets the color for the fill-paint
*
* @param color
*/
public void setFillColor(int color) {
mFilledPaint.setColor(color);
}
/**
* set the width of the highlightning lines, default 3f
*
* @param width
*/
public void setHighlightLineWidth(float width) {
mHighlightWidth = width;
}
/**
* returns the width of the highlightning line, default 3f
*
* @return
*/
public float getHighlightLineWidth() {
return mHighlightWidth;
}
@Override
public void setPaint(Paint p, int which) {
super.setPaint(p, which);
switch (which) {
case PAINT_CIRCLES_INNER:
mCirclePaintInner = p;
break;
case PAINT_HIGHLIGHT_LINE:
mHighlightPaint = p;
break;
}
}
if (isOffContentRight(positions[j]))
break;
if (isOffContentLeft(positions[j]))
continue;
float val = entries.get(j / 2).getVal();
if (mDrawUnitInChart) {
mDrawCanvas.drawText(mFormatValue.format(val) + mUnit, positions[j], positions[j + 1]
- valOffset, mValuePaint);
} else {
mDrawCanvas.drawText(mFormatValue.format(val), positions[j], positions[j + 1] - valOffset,
mValuePaint);
}
}
}
}
}
/**
* draws the circle value indicators
*/
@Override
protected void drawAdditional() {
// if drawing circles is enabled
if (mDrawCircles) {
ArrayList<DataSet> dataSets = mData.getDataSets();
for (int i = 0; i < mData.getDataSetCount(); i++) {
DataSet dataSet = dataSets.get(i);
ArrayList<Entry> entries = dataSet.getYVals();
// Get the colors for the DataSet at the current index. If the
// index
// is out of bounds, reuse DataSet colors.
ArrayList<Integer> colors = mCt.getDataSetColors(i % mCt.getColors().size());
float[] positions = generateTransformedValues(entries, 0f);
for (int j = 0; j < positions.length; j += 2) {
// Set the color for the currently drawn value. If the index is
// out of bounds, reuse colors.
mRenderPaint.setColor(colors.get(j % colors.size()));
if (isOffContentRight(positions[j]))
break;
// make sure the circles don't do shitty things outside
// bounds
if (isOffContentLeft(positions[j]))
continue;
mDrawCanvas.drawCircle(positions[j], positions[j + 1], mCircleSize, mRenderPaint);
mDrawCanvas.drawCircle(positions[j], positions[j + 1], mCircleSize / 2, mCirclePaintInner);
}
}
}
}
/**
* Returns the x index of the touch. If touch is out of the chart, the first or last index will be returned
*
* @param x
* @param y
* @return
*/
public int getXIndexByTouchPoint(float x, float y) {
// create an array of the touch-point
float[] pts = new float[2];
pts[0] = x;
pts[1] = y;
Matrix tmp = new Matrix();
// invert all matrixes to convert back to the original value
mMatrixOffset.invert(tmp);
tmp.mapPoints(pts);
mMatrixTouch.invert(tmp);
tmp.mapPoints(pts);
mMatrixValueToPx.invert(tmp);
tmp.mapPoints(pts);
double xTouchVal = pts[0];
double yTouchVal = pts[1];
double base = Math.floor(xTouchVal);
Log.i(LOG_TAG, "touchindex x: " + xTouchVal + ", touchindex y: " + yTouchVal);
// touch out of chart
if (xTouchVal < 0)
return 0;
if (xTouchVal > mDeltaX) {
return mData.getXValCount() - 1;
}
return (int) base;
}
/**
* set this to true to enable the drawing of circle indicators
*
* @param enabled
*/
public void setDrawCircles(boolean enabled) {
this.mDrawCircles = enabled;
}
/**
* returns true if drawing circles is enabled, false if not
*
* @return
*/
public boolean isDrawCirclesEnabled() {
return mDrawCircles;
}
/**
* sets the size (radius) of the circle shpaed value indicators, default size = 4f
*
* @param size
*/
public void setCircleSize(float size) {
mCircleSize = Utils.convertDpToPixel(size);
}
/**
* returns the circlesize
*
* @param size
*/
public float getCircleSize(float size) {
return Utils.convertPixelsToDp(mCircleSize);
}
/**
* set if the chartdata should be drawn as a line or filled default = line / default = false, disabling this will
* give up to 20% performance boost on large datasets
*
* @param filled
*/
public void setDrawFilled(boolean filled) {
mDrawFilled = filled;
}
/**
* set if the user should be allowed to draw onto the chart
*
* @param drawingEnabled
*/
public void setDrawingEnabled(boolean drawingEnabled) {
if (mListener instanceof BarLineChartTouchListener) {
((BarLineChartTouchListener) mListener).setDrawingEnabled(drawingEnabled);
}
}
/**
* returns true if filled drawing is enabled, false if not
*
* @return
*/
public boolean isDrawFilledEnabled() {
return mDrawFilled;
}
/**
* set the line width of the chart (min = 0.5f, max = 10f); default 1f NOTE: thinner line == better performance,
* thicker line == worse performance
*
* @param width
*/
public void setLineWidth(float width) {
if (width < 0.5f)
width = 0.5f;
if (width > 10.0f)
width = 10.0f;
mLineWidth = width;
mRenderPaint.setStrokeWidth(width);
}
/**
* returns the width of the drawn chart line
*
* @return
*/
public float getLineWidth() {
return mLineWidth;
}
/**
* sets the color for the fill-paint
*
* @param color
*/
public void setFillColor(int color) {
mFilledPaint.setColor(color);
}
/**
* set the width of the highlightning lines, default 3f
*
* @param width
*/
public void setHighlightLineWidth(float width) {
mHighlightWidth = width;
}
/**
* returns the width of the highlightning line, default 3f
*
* @return
*/
public float getHighlightLineWidth() {
return mHighlightWidth;
}
@Override
public void setPaint(Paint p, int which) {
super.setPaint(p, which);
switch (which) {
case PAINT_CIRCLES_INNER:
mCirclePaintInner = p;
break;
case PAINT_HIGHLIGHT_LINE:
mHighlightPaint = p;
break;
}
}
}

View file

@ -0,0 +1,9 @@
package com.github.mikephil.charting.exception;
public class DrawingDataSetNotCreatedException extends RuntimeException {
public DrawingDataSetNotCreatedException() {
super("Have to create a new drawing set first. Call ChartData's createNewDrawingDataSet() method");
}
}