This changeset merges all performance updates related to #1892, as well as the bug fix for #1895 for master.
This commit is contained in:
commit
40c5c87d13
77 changed files with 2773 additions and 508 deletions
|
@ -18,7 +18,7 @@
|
|||
android:layout_margin="8dp"
|
||||
android:layout_toLeftOf="@+id/tvYMax"
|
||||
android:layout_marginRight="5dp"
|
||||
android:max="200"
|
||||
android:max="150"
|
||||
android:paddingBottom="12dp" />
|
||||
|
||||
<SeekBar
|
||||
|
@ -30,7 +30,7 @@
|
|||
android:layout_marginBottom="35dp"
|
||||
android:layout_toLeftOf="@+id/tvXMax"
|
||||
android:layout_marginRight="5dp"
|
||||
android:max="500"
|
||||
android:max="1500"
|
||||
android:paddingBottom="12dp" />
|
||||
|
||||
<TextView
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.xxmassdeveloper.mpchartexample;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
@ -33,6 +34,7 @@ import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
|
|||
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
|
||||
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
|
||||
import com.github.mikephil.charting.utils.ColorTemplate;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter;
|
||||
import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter;
|
||||
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
|
||||
|
@ -256,6 +258,7 @@ public class BarChartActivity extends DemoBase implements OnSeekBarChangeListene
|
|||
}
|
||||
}
|
||||
|
||||
protected RectF mOnValueSelectedRectF = new RectF();
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void onValueSelected(Entry e, Highlight h) {
|
||||
|
@ -263,8 +266,9 @@ public class BarChartActivity extends DemoBase implements OnSeekBarChangeListene
|
|||
if (e == null)
|
||||
return;
|
||||
|
||||
RectF bounds = mChart.getBarBounds((BarEntry) e);
|
||||
PointF position = mChart.getPosition(e, AxisDependency.LEFT);
|
||||
RectF bounds = mOnValueSelectedRectF;
|
||||
mChart.getBarBounds((BarEntry) e, bounds);
|
||||
MPPointF position = mChart.getPosition(e, AxisDependency.LEFT);
|
||||
|
||||
Log.i("bounds", bounds.toString());
|
||||
Log.i("position", position.toString());
|
||||
|
@ -272,6 +276,8 @@ public class BarChartActivity extends DemoBase implements OnSeekBarChangeListene
|
|||
Log.i("x-index",
|
||||
"low: " + mChart.getLowestVisibleX() + ", high: "
|
||||
+ mChart.getHighestVisibleX());
|
||||
|
||||
MPPointF.recycleInstance(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.github.mikephil.charting.data.BarDataSet;
|
|||
import com.github.mikephil.charting.data.BarEntry;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.formatter.AxisValueFormatter;
|
||||
import com.github.mikephil.charting.formatter.FormattedStringCache;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
|
||||
|
||||
|
@ -164,15 +165,15 @@ public class BarChartPositiveNegative extends DemoBase {
|
|||
|
||||
private class ValueFormatter implements com.github.mikephil.charting.formatter.ValueFormatter {
|
||||
|
||||
private DecimalFormat mFormat;
|
||||
private FormattedStringCache.PrimIntFloat mFormattedStringCache;
|
||||
|
||||
public ValueFormatter() {
|
||||
mFormat = new DecimalFormat("######.0");
|
||||
mFormattedStringCache = new FormattedStringCache.PrimIntFloat(new DecimalFormat("######.0"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
|
||||
return mFormat.format(value);
|
||||
return mFormattedStringCache.getFormattedValue(value, dataSetIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.xxmassdeveloper.mpchartexample;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
@ -27,6 +28,7 @@ import com.github.mikephil.charting.data.Entry;
|
|||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
|
||||
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -245,6 +247,7 @@ public class HorizontalBarChartActivity extends DemoBase implements OnSeekBarCha
|
|||
}
|
||||
}
|
||||
|
||||
protected RectF mOnValueSelectedRectF = new RectF();
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void onValueSelected(Entry e, Highlight h) {
|
||||
|
@ -252,12 +255,16 @@ public class HorizontalBarChartActivity extends DemoBase implements OnSeekBarCha
|
|||
if (e == null)
|
||||
return;
|
||||
|
||||
RectF bounds = mChart.getBarBounds((BarEntry) e);
|
||||
PointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex())
|
||||
RectF bounds = mOnValueSelectedRectF;
|
||||
mChart.getBarBounds((BarEntry) e, bounds);
|
||||
|
||||
MPPointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex())
|
||||
.getAxisDependency());
|
||||
|
||||
Log.i("bounds", bounds.toString());
|
||||
Log.i("position", position.toString());
|
||||
|
||||
MPPointF.recycleInstance(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -111,7 +111,7 @@ public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListe
|
|||
|
||||
Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf");
|
||||
|
||||
LimitLine ll1 = new LimitLine(130f, "Upper Limit");
|
||||
LimitLine ll1 = new LimitLine(150f, "Upper Limit");
|
||||
ll1.setLineWidth(4f);
|
||||
ll1.enableDashedLine(10f, 10f, 0f);
|
||||
ll1.setLabelPosition(LimitLabelPosition.RIGHT_TOP);
|
||||
|
@ -129,7 +129,7 @@ public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListe
|
|||
leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines
|
||||
leftAxis.addLimitLine(ll1);
|
||||
leftAxis.addLimitLine(ll2);
|
||||
leftAxis.setAxisMaxValue(220f);
|
||||
leftAxis.setAxisMaxValue(200f);
|
||||
leftAxis.setAxisMinValue(-50f);
|
||||
//leftAxis.setYOffset(20f);
|
||||
leftAxis.enableGridDashedLine(10f, 10f, 0f);
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.github.mikephil.charting.data.Entry;
|
|||
import com.github.mikephil.charting.data.LineData;
|
||||
import com.github.mikephil.charting.data.LineDataSet;
|
||||
import com.github.mikephil.charting.formatter.AxisValueFormatter;
|
||||
import com.github.mikephil.charting.formatter.FormattedStringCache;
|
||||
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
|
||||
import com.github.mikephil.charting.utils.ColorTemplate;
|
||||
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
|
||||
|
@ -91,11 +92,12 @@ public class LineChartTime extends DemoBase implements OnSeekBarChangeListener {
|
|||
xAxis.setGranularity(60000L); // one minute in millis
|
||||
xAxis.setValueFormatter(new AxisValueFormatter() {
|
||||
|
||||
private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm");
|
||||
private FormattedStringCache.Generic<Long, Date> mFormattedStringCache = new FormattedStringCache.Generic<>(new SimpleDateFormat("dd MMM HH:mm"));
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value, AxisBase axis) {
|
||||
return mFormat.format(new Date((long) value));
|
||||
Long v = (long) value;
|
||||
return mFormattedStringCache.getFormattedValue(new Date(v), v);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -176,19 +176,22 @@ public class RealtimeLineChartActivity extends DemoBase implements
|
|||
if (thread != null)
|
||||
thread.interrupt();
|
||||
|
||||
final Runnable runnable = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
addEntry();
|
||||
}
|
||||
};
|
||||
|
||||
thread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
addEntry();
|
||||
}
|
||||
});
|
||||
// Don't generate garbage runnables inside the loop.
|
||||
runOnUiThread(runnable);
|
||||
|
||||
try {
|
||||
Thread.sleep(25);
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.github.mikephil.charting.data.BarData;
|
|||
import com.github.mikephil.charting.data.BarDataSet;
|
||||
import com.github.mikephil.charting.data.BarEntry;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.formatter.FormattedStringCache;
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter;
|
||||
import com.github.mikephil.charting.formatter.AxisValueFormatter;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
|
@ -79,11 +80,11 @@ public class StackedBarActivityNegative extends DemoBase implements
|
|||
xAxis.setGranularity(10f);
|
||||
xAxis.setValueFormatter(new AxisValueFormatter() {
|
||||
|
||||
private DecimalFormat format = new DecimalFormat("###");
|
||||
private FormattedStringCache.PrimFloat format = new FormattedStringCache.PrimFloat(new DecimalFormat("###"));
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value, AxisBase axis) {
|
||||
return format.format(value) + "-" + format.format(value + 10);
|
||||
return format.getFormattedValue(value) + "-" + format.getFormattedValue(value+10);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -222,22 +223,25 @@ public class StackedBarActivityNegative extends DemoBase implements
|
|||
|
||||
private class CustomFormatter implements ValueFormatter, AxisValueFormatter {
|
||||
|
||||
private DecimalFormat mFormat;
|
||||
private FormattedStringCache.Generic<Integer, Float> mFormatValue;
|
||||
private FormattedStringCache.PrimFloat mFormatAxis;
|
||||
|
||||
public CustomFormatter() {
|
||||
mFormat = new DecimalFormat("###");
|
||||
mFormatValue = new FormattedStringCache.Generic<>(new DecimalFormat("###"));
|
||||
mFormatAxis = new FormattedStringCache.PrimFloat(new DecimalFormat("###"));
|
||||
}
|
||||
|
||||
// data
|
||||
@Override
|
||||
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
|
||||
return mFormat.format(Math.abs(value)) + "m";
|
||||
return mFormatValue.getFormattedValue(value, dataSetIndex) + "m";
|
||||
}
|
||||
|
||||
// YAxis
|
||||
@Override
|
||||
public String getFormattedValue(float value, AxisBase axis) {
|
||||
return mFormat.format(Math.abs(value)) + "m";
|
||||
Float v = Math.abs(value);
|
||||
return mFormatAxis.getFormattedValue(v) + "m";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,20 +2,22 @@ package com.xxmassdeveloper.mpchartexample.custom;
|
|||
|
||||
import com.github.mikephil.charting.components.AxisBase;
|
||||
import com.github.mikephil.charting.formatter.AxisValueFormatter;
|
||||
import com.github.mikephil.charting.formatter.FormattedStringCache;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class MyAxisValueFormatter implements AxisValueFormatter {
|
||||
|
||||
private DecimalFormat mFormat;
|
||||
private FormattedStringCache.PrimFloat mFormattedStringCache;
|
||||
|
||||
public MyAxisValueFormatter() {
|
||||
mFormat = new DecimalFormat("###,###,###,##0.0");
|
||||
mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0.0"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value, AxisBase axis) {
|
||||
return mFormat.format(value) + " $";
|
||||
return mFormattedStringCache.getFormattedValue(value) + " $";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.xxmassdeveloper.mpchartexample.custom;
|
|||
|
||||
import com.github.mikephil.charting.components.AxisBase;
|
||||
import com.github.mikephil.charting.formatter.AxisValueFormatter;
|
||||
import com.github.mikephil.charting.formatter.FormattedStringCache;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
@ -11,13 +12,13 @@ import java.text.DecimalFormat;
|
|||
*/
|
||||
public class MyCustomXAxisValueFormatter implements AxisValueFormatter {
|
||||
|
||||
private DecimalFormat mFormat;
|
||||
private FormattedStringCache.PrimFloat mFormattedStringCache;
|
||||
private ViewPortHandler mViewPortHandler;
|
||||
|
||||
public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) {
|
||||
mViewPortHandler = viewPortHandler;
|
||||
// maybe do something here or provide parameters in constructor
|
||||
mFormat = new DecimalFormat("###,###,###,##0.0");
|
||||
mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0.0"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,14 +27,15 @@ public class MyCustomXAxisValueFormatter implements AxisValueFormatter {
|
|||
//Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY());
|
||||
|
||||
// e.g. adjust the x-axis values depending on scale / zoom level
|
||||
if (mViewPortHandler.getScaleX() > 5)
|
||||
final float xScale = mViewPortHandler.getScaleX();
|
||||
if (xScale > 5)
|
||||
return "4";
|
||||
else if (mViewPortHandler.getScaleX() > 3)
|
||||
else if (xScale > 3)
|
||||
return "3";
|
||||
else if (mViewPortHandler.getScaleX() > 1)
|
||||
else if (xScale > 1)
|
||||
return "2";
|
||||
else
|
||||
return mFormat.format(value);
|
||||
return mFormattedStringCache.getFormattedValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.xxmassdeveloper.mpchartexample.custom;
|
||||
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.formatter.FormattedStringCache;
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -8,14 +9,14 @@ import java.text.DecimalFormat;
|
|||
|
||||
public class MyValueFormatter implements ValueFormatter {
|
||||
|
||||
private DecimalFormat mFormat;
|
||||
private FormattedStringCache.Generic<Integer, Float> mFormattedStringCache;
|
||||
|
||||
public MyValueFormatter() {
|
||||
mFormat = new DecimalFormat("###,###,###,##0.0");
|
||||
mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0.0"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
|
||||
return mFormat.format(value) + " $";
|
||||
return mFormattedStringCache.getFormattedValue(value, dataSetIndex) + " $";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.widget.TextView;
|
|||
import com.github.mikephil.charting.components.MarkerView;
|
||||
import com.github.mikephil.charting.data.CandleEntry;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.formatter.FormattedStringCache;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
import com.xxmassdeveloper.mpchartexample.R;
|
||||
|
@ -22,7 +23,7 @@ import java.text.DecimalFormat;
|
|||
public class RadarMarkerView extends MarkerView {
|
||||
|
||||
private TextView tvContent;
|
||||
private DecimalFormat format = new DecimalFormat("##0");
|
||||
private FormattedStringCache.PrimFloat mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("##0"));
|
||||
|
||||
public RadarMarkerView(Context context, int layoutResource) {
|
||||
super(context, layoutResource);
|
||||
|
@ -35,7 +36,8 @@ public class RadarMarkerView extends MarkerView {
|
|||
// content (user-interface)
|
||||
@Override
|
||||
public void refreshContent(Entry e, Highlight highlight) {
|
||||
tvContent.setText(format.format(e.getY()) + " %");
|
||||
float value = e.getY();
|
||||
tvContent.setText(mFormattedStringCache.getFormattedValue(value) + " %");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -98,17 +98,37 @@ public class BarChart extends BarLineChartBase<BarData> implements BarDataProvid
|
|||
|
||||
/**
|
||||
* Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be
|
||||
* found in the charts data.
|
||||
* found in the charts data. Performance-intensive code should use void getBarBounds(BarEntry, RectF) instead.
|
||||
*
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
public RectF getBarBounds(BarEntry e) {
|
||||
|
||||
|
||||
RectF bounds = new RectF();
|
||||
getBarBounds(e, bounds);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* The passed outputRect will be assigned the values of the bounding box of the specified Entry in the specified DataSet.
|
||||
* The rect will be assigned Float.MIN_VALUE in all locations if the Entry could not be found in the charts data.
|
||||
*
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
public void getBarBounds(BarEntry e, RectF outputRect){
|
||||
|
||||
RectF bounds = outputRect;
|
||||
|
||||
IBarDataSet set = mData.getDataSetForEntry(e);
|
||||
|
||||
if (set == null)
|
||||
return null;
|
||||
if (set == null) {
|
||||
bounds.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
float y = e.getY();
|
||||
float x = e.getX();
|
||||
|
@ -120,11 +140,10 @@ public class BarChart extends BarLineChartBase<BarData> implements BarDataProvid
|
|||
float top = y >= 0 ? y : 0;
|
||||
float bottom = y <= 0 ? y : 0;
|
||||
|
||||
RectF bounds = new RectF(left, top, right, bottom);
|
||||
bounds.set(left, top, right, bottom);
|
||||
|
||||
getTransformer(set.getAxisDependency()).rectValueToPixel(bounds);
|
||||
getTransformer(set.getAxisDependency()).rectValueToPixel(outputRect);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.graphics.Color;
|
|||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
@ -32,6 +31,7 @@ import com.github.mikephil.charting.listener.BarLineChartTouchListener;
|
|||
import com.github.mikephil.charting.listener.OnDrawListener;
|
||||
import com.github.mikephil.charting.renderer.XAxisRenderer;
|
||||
import com.github.mikephil.charting.renderer.YAxisRenderer;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.PointD;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
@ -589,15 +589,18 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
* VIEWPORT
|
||||
*/
|
||||
|
||||
protected Matrix mZoomInMatrixBuffer = new Matrix();
|
||||
/**
|
||||
* Zooms in by 1.4f, into the charts center. center.
|
||||
*/
|
||||
public void zoomIn() {
|
||||
|
||||
PointF center = mViewPortHandler.getContentCenter();
|
||||
MPPointF center = mViewPortHandler.getContentCenter();
|
||||
|
||||
Matrix save = mViewPortHandler.zoomIn(center.x, -center.y);
|
||||
mViewPortHandler.refresh(save, this, false);
|
||||
mViewPortHandler.zoomIn(center.x, -center.y, mZoomInMatrixBuffer);
|
||||
mViewPortHandler.refresh(mZoomInMatrixBuffer, this, false);
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
|
||||
// Range might have changed, which means that Y-axis labels
|
||||
// could have changed in size, affecting Y-axis size.
|
||||
|
@ -606,15 +609,18 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
postInvalidate();
|
||||
}
|
||||
|
||||
protected Matrix mZoomOutMatrixBuffer = new Matrix();
|
||||
/**
|
||||
* Zooms out by 0.7f, from the charts center. center.
|
||||
*/
|
||||
public void zoomOut() {
|
||||
|
||||
PointF center = mViewPortHandler.getContentCenter();
|
||||
MPPointF center = mViewPortHandler.getContentCenter();
|
||||
|
||||
Matrix save = mViewPortHandler.zoomOut(center.x, -center.y);
|
||||
mViewPortHandler.refresh(save, this, false);
|
||||
mViewPortHandler.zoomOut(center.x, -center.y, mZoomOutMatrixBuffer);
|
||||
mViewPortHandler.refresh(mZoomOutMatrixBuffer, this, false);
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
|
||||
// Range might have changed, which means that Y-axis labels
|
||||
// could have changed in size, affecting Y-axis size.
|
||||
|
@ -623,6 +629,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
postInvalidate();
|
||||
}
|
||||
|
||||
protected Matrix mZoomMatrixBuffer = new Matrix();
|
||||
/**
|
||||
* Zooms in or out by the given scale factor. x and y are the coordinates
|
||||
* (in pixels) of the zoom center.
|
||||
|
@ -633,7 +640,8 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
* @param y
|
||||
*/
|
||||
public void zoom(float scaleX, float scaleY, float x, float y) {
|
||||
Matrix save = mViewPortHandler.zoom(scaleX, scaleY, x, y);
|
||||
Matrix save = mZoomMatrixBuffer;
|
||||
mViewPortHandler.zoom(scaleX, scaleY, x, y, save);
|
||||
mViewPortHandler.refresh(save, this, false);
|
||||
|
||||
// Range might have changed, which means that Y-axis labels
|
||||
|
@ -655,7 +663,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
*/
|
||||
public void zoomAndCenter(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) {
|
||||
|
||||
Runnable job = new ZoomJob(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this);
|
||||
Runnable job = ZoomJob.getInstance(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this);
|
||||
addViewportJob(job);
|
||||
}
|
||||
|
||||
|
@ -677,22 +685,26 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
|
||||
PointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis);
|
||||
|
||||
Runnable job = new AnimatedZoomJob(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis
|
||||
Runnable job = AnimatedZoomJob.getInstance(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis
|
||||
.mAxisRange, scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(),
|
||||
xValue, yValue, (float) origin.x, (float) origin.y, duration);
|
||||
addViewportJob(job);
|
||||
|
||||
PointD.recycleInstance(origin);
|
||||
|
||||
} else {
|
||||
Log.e(LOG_TAG, "Unable to execute zoomAndCenterAnimated(...) on API level < 11");
|
||||
}
|
||||
}
|
||||
|
||||
protected Matrix mFitScreenMatrixBuffer = new Matrix();
|
||||
/**
|
||||
* Resets all zooming and dragging and makes the chart fit exactly it's
|
||||
* bounds.
|
||||
*/
|
||||
public void fitScreen() {
|
||||
Matrix save = mViewPortHandler.fitScreen();
|
||||
Matrix save = mFitScreenMatrixBuffer;
|
||||
mViewPortHandler.fitScreen(save);
|
||||
mViewPortHandler.refresh(save, this, false);
|
||||
|
||||
calculateOffsets();
|
||||
|
@ -787,6 +799,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
mViewPortHandler.setMinMaxScaleY(minScale, maxScale);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Moves the left side of the current viewport to the specified x-position.
|
||||
* This also refreshes the chart by calling invalidate().
|
||||
|
@ -795,7 +808,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
*/
|
||||
public void moveViewToX(float xValue) {
|
||||
|
||||
Runnable job = new MoveViewJob(mViewPortHandler, xValue, 0f,
|
||||
Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, 0f,
|
||||
getTransformer(AxisDependency.LEFT), this);
|
||||
|
||||
addViewportJob(job);
|
||||
|
@ -814,7 +827,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
|
||||
float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
|
||||
|
||||
Runnable job = new MoveViewJob(mViewPortHandler, xValue, yValue + yInView / 2f,
|
||||
Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f,
|
||||
getTransformer(axis), this);
|
||||
|
||||
addViewportJob(job);
|
||||
|
@ -839,10 +852,12 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
|
||||
float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
|
||||
|
||||
Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xValue, yValue + yInView / 2f,
|
||||
Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f,
|
||||
getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration);
|
||||
|
||||
addViewportJob(job);
|
||||
|
||||
PointD.recycleInstance(bounds);
|
||||
} else {
|
||||
Log.e(LOG_TAG, "Unable to execute moveViewToAnimated(...) on API level < 11");
|
||||
}
|
||||
|
@ -859,7 +874,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
|
||||
float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
|
||||
|
||||
Runnable job = new MoveViewJob(mViewPortHandler, 0f, yValue + valsInView / 2f,
|
||||
Runnable job = MoveViewJob.getInstance(mViewPortHandler, 0f, yValue + valsInView / 2f,
|
||||
getTransformer(axis), this);
|
||||
|
||||
addViewportJob(job);
|
||||
|
@ -879,7 +894,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
|
||||
float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX();
|
||||
|
||||
Runnable job = new MoveViewJob(mViewPortHandler,
|
||||
Runnable job = MoveViewJob.getInstance(mViewPortHandler,
|
||||
xValue - xInView / 2f, yValue + yInView / 2f,
|
||||
getTransformer(axis), this);
|
||||
|
||||
|
@ -905,11 +920,13 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
|
||||
float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX();
|
||||
|
||||
Runnable job = new AnimatedMoveViewJob(mViewPortHandler,
|
||||
Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler,
|
||||
xValue - xInView / 2f, yValue + yInView / 2f,
|
||||
getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration);
|
||||
|
||||
addViewportJob(job);
|
||||
|
||||
PointD.recycleInstance(bounds);
|
||||
} else {
|
||||
Log.e(LOG_TAG, "Unable to execute centerViewToAnimated(...) on API level < 11");
|
||||
}
|
||||
|
@ -993,25 +1010,27 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
return mDrawListener;
|
||||
}
|
||||
|
||||
protected float[] mGetPositionBuffer = new float[2];
|
||||
/**
|
||||
* Returns a recyclable MPPointF instance.
|
||||
* Returns the position (in pixels) the provided Entry has inside the chart
|
||||
* view or null, if the provided Entry is null.
|
||||
*
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
public PointF getPosition(Entry e, AxisDependency axis) {
|
||||
public MPPointF getPosition(Entry e, AxisDependency axis) {
|
||||
|
||||
if (e == null)
|
||||
return null;
|
||||
|
||||
float[] vals = new float[]{
|
||||
e.getX(), e.getY()
|
||||
};
|
||||
float[] vals = mGetPositionBuffer;
|
||||
vals[0] = e.getX();
|
||||
vals[1] = e.getY();
|
||||
|
||||
getTransformer(axis).pointValuesToPixel(vals);
|
||||
|
||||
return new PointF(vals[0], vals[1]);
|
||||
return MPPointF.getInstance(vals[0], vals[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1183,6 +1202,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable PointD instance
|
||||
* Returns the x and y values in the chart at the given touch point
|
||||
* (encapsulated in a PointD). This method transforms pixel coordinates to
|
||||
* coordinates / values in the chart. This is the opposite method to
|
||||
|
@ -1193,10 +1213,17 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
* @return
|
||||
*/
|
||||
public PointD getValuesByTouchPoint(float x, float y, AxisDependency axis) {
|
||||
return getTransformer(axis).getValuesByTouchPoint(x, y);
|
||||
PointD result = PointD.getInstance(0,0);
|
||||
getValuesByTouchPoint(x,y,axis,result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void getValuesByTouchPoint(float x, float y, AxisDependency axis, PointD outputPoint){
|
||||
getTransformer(axis).getValuesByTouchPoint(x, y, outputPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable PointD instance
|
||||
* Transforms the given chart values into pixels. This is the opposite
|
||||
* method to getValuesByTouchPoint(...).
|
||||
*
|
||||
|
@ -1208,6 +1235,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
return getTransformer(axis).getPixelsForValues(x, y);
|
||||
}
|
||||
|
||||
PointD pointForGetYValueByTouchPoint = PointD.getInstance(0,0);
|
||||
/**
|
||||
* Returns y value at the given touch position (must not necessarily be
|
||||
* a value contained in one of the datasets)
|
||||
|
@ -1217,7 +1245,9 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
* @return
|
||||
*/
|
||||
public float getYValueByTouchPoint(float x, float y, AxisDependency axis) {
|
||||
return (float) getValuesByTouchPoint(x, y, axis).y;
|
||||
getValuesByTouchPoint(x, y, axis, pointForGetYValueByTouchPoint);
|
||||
float result = (float) pointForGetYValueByTouchPoint.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1250,6 +1280,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
return null;
|
||||
}
|
||||
|
||||
protected PointD posForGetLowestVisibleX = PointD.getInstance(0,0);
|
||||
/**
|
||||
* Returns the lowest x-index (value on the x-axis) that is still visible on
|
||||
* the chart.
|
||||
|
@ -1258,11 +1289,13 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
*/
|
||||
@Override
|
||||
public float getLowestVisibleX() {
|
||||
PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
|
||||
mViewPortHandler.contentBottom());
|
||||
return (float) Math.max(mXAxis.mAxisMinimum, pos.x);
|
||||
getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
|
||||
mViewPortHandler.contentBottom(), posForGetLowestVisibleX);
|
||||
float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.x);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected PointD posForGetHighestVisibleX = PointD.getInstance(0,0);
|
||||
/**
|
||||
* Returns the highest x-index (value on the x-axis) that is still visible
|
||||
* on the chart.
|
||||
|
@ -1271,9 +1304,10 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
*/
|
||||
@Override
|
||||
public float getHighestVisibleX() {
|
||||
PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentRight(),
|
||||
mViewPortHandler.contentBottom());
|
||||
return (float) Math.min(mXAxis.mAxisMaximum, pos.x);
|
||||
getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentRight(),
|
||||
mViewPortHandler.contentBottom(), posForGetHighestVisibleX);
|
||||
float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.x);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1508,11 +1542,13 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
|
|||
return null;
|
||||
}
|
||||
|
||||
protected float[] mOnSizeChangedBuffer = new float[2];
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
|
||||
// Saving current position of chart.
|
||||
float[] pts = new float[2];
|
||||
float[] pts = mOnSizeChangedBuffer;
|
||||
pts[0] = pts[1] = 0;
|
||||
if (mKeepPositionOnRotation) {
|
||||
pts[0] = mViewPortHandler.contentLeft();
|
||||
pts[1] = mViewPortHandler.contentTop();
|
||||
|
|
|
@ -45,6 +45,7 @@ import com.github.mikephil.charting.listener.OnChartGestureListener;
|
|||
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
|
||||
import com.github.mikephil.charting.renderer.DataRenderer;
|
||||
import com.github.mikephil.charting.renderer.LegendRenderer;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -305,7 +306,11 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
|
|||
// calculate how many digits are needed
|
||||
setupDefaultFormatter(data.getYMin(), data.getYMax());
|
||||
|
||||
for (IDataSet set : mData.getDataSets()) {
|
||||
IDataSet set;
|
||||
final List sets = mData.getDataSets();
|
||||
final int count = sets.size();
|
||||
for(int i = 0 ; i < count ; i++){
|
||||
set = (IDataSet)sets.get(i);
|
||||
if (set.needsFormatter() || set.getValueFormatter() == mDefaultFormatter)
|
||||
set.setValueFormatter(mDefaultFormatter);
|
||||
}
|
||||
|
@ -450,7 +455,7 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
|
|||
/**
|
||||
* the custom position of the description text
|
||||
*/
|
||||
private PointF mDescriptionPosition;
|
||||
private MPPointF mDescriptionPosition;
|
||||
|
||||
/**
|
||||
* draws the description text in the bottom right corner of the chart
|
||||
|
@ -1056,22 +1061,24 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable MPPointF instance.
|
||||
* Returns the center point of the chart (the whole View) in pixels.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public PointF getCenter() {
|
||||
return new PointF(getWidth() / 2f, getHeight() / 2f);
|
||||
public MPPointF getCenter() {
|
||||
return MPPointF.getInstance(getWidth() / 2f, getHeight() / 2f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable MPPointF instance.
|
||||
* Returns the center of the chart taking offsets under consideration.
|
||||
* (returns the center of the content rectangle)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public PointF getCenterOffsets() {
|
||||
public MPPointF getCenterOffsets() {
|
||||
return mViewPortHandler.getContentCenter();
|
||||
}
|
||||
|
||||
|
@ -1094,7 +1101,12 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
|
|||
* @param y - ycoordinate
|
||||
*/
|
||||
public void setDescriptionPosition(float x, float y) {
|
||||
mDescriptionPosition = new PointF(x, y);
|
||||
if(mDescriptionPosition == null){
|
||||
mDescriptionPosition = MPPointF.getInstance(x,y);
|
||||
}else {
|
||||
mDescriptionPosition.x = x;
|
||||
mDescriptionPosition.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1443,6 +1455,24 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
|
|||
return vals;
|
||||
}
|
||||
|
||||
public void getEntriesAtIndex(int xIndex, List<Entry> entriesOutput){
|
||||
|
||||
List<Entry> vals = entriesOutput;
|
||||
vals.clear();
|
||||
|
||||
for (int i = 0; i < mData.getDataSetCount(); i++) {
|
||||
|
||||
IDataSet set = mData.getDataSetByIndex(i);
|
||||
|
||||
Entry e = set.getEntryForXPos(xIndex);
|
||||
|
||||
if (e != null) {
|
||||
vals.add(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ChartData object that has been set for the chart.
|
||||
*
|
||||
|
@ -1497,8 +1527,12 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
|
|||
mHighlighter = highlighter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable MPPointF instance.
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public PointF getCenterOfView() {
|
||||
public MPPointF getCenterOfView() {
|
||||
return getCenter();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer;
|
|||
import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart;
|
||||
import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart;
|
||||
import com.github.mikephil.charting.utils.HorizontalViewPortHandler;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.PointD;
|
||||
import com.github.mikephil.charting.utils.TransformerHorizontalBarChart;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
@ -140,12 +141,15 @@ public class HorizontalBarChart extends BarChart {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RectF getBarBounds(BarEntry e) {
|
||||
public void getBarBounds(BarEntry e, RectF outputRect) {
|
||||
|
||||
RectF bounds = outputRect;
|
||||
IBarDataSet set = mData.getDataSetForEntry(e);
|
||||
|
||||
if (set == null)
|
||||
return null;
|
||||
if (set == null){
|
||||
outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
float y = e.getY();
|
||||
float x = e.getX();
|
||||
|
@ -157,24 +161,33 @@ public class HorizontalBarChart extends BarChart {
|
|||
float left = y >= 0 ? y : 0;
|
||||
float right = y <= 0 ? y : 0;
|
||||
|
||||
RectF bounds = new RectF(left, top, right, bottom);
|
||||
bounds.set(left, top, right, bottom);
|
||||
|
||||
getTransformer(set.getAxisDependency()).rectValueToPixel(bounds);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
protected float[] mGetPositionBuffer = new float[2];
|
||||
/**
|
||||
* Returns a recyclable MPPointF instance.
|
||||
*
|
||||
* @param e
|
||||
* @param axis
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public PointF getPosition(Entry e, AxisDependency axis) {
|
||||
public MPPointF getPosition(Entry e, AxisDependency axis) {
|
||||
|
||||
if (e == null)
|
||||
return null;
|
||||
|
||||
float[] vals = new float[]{e.getY(), e.getX()};
|
||||
float[] vals = mGetPositionBuffer;
|
||||
vals[0] = e.getY();
|
||||
vals[1] = e.getX();
|
||||
|
||||
getTransformer(axis).pointValuesToPixel(vals);
|
||||
|
||||
return new PointF(vals[0], vals[1]);
|
||||
return MPPointF.getInstance(vals[0], vals[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,18 +209,22 @@ public class HorizontalBarChart extends BarChart {
|
|||
return getHighlighter().getHighlight(y, x); // switch x and y
|
||||
}
|
||||
|
||||
protected PointD posForGetLowestVisibleX = PointD.getInstance(0,0);
|
||||
@Override
|
||||
public float getLowestVisibleX() {
|
||||
PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
|
||||
mViewPortHandler.contentBottom());
|
||||
return (float) Math.max(mXAxis.mAxisMinimum, pos.y);
|
||||
getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
|
||||
mViewPortHandler.contentBottom(), posForGetLowestVisibleX);
|
||||
float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected PointD posForGetHighestVisibleX = PointD.getInstance(0,0);
|
||||
@Override
|
||||
public float getHighestVisibleX() {
|
||||
PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
|
||||
mViewPortHandler.contentTop());
|
||||
return (float) Math.min(mXAxis.mAxisMaximum, pos.y);
|
||||
getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
|
||||
mViewPortHandler.contentTop(), posForGetHighestVisibleX);
|
||||
float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.github.mikephil.charting.highlight.Highlight;
|
|||
import com.github.mikephil.charting.highlight.PieHighlighter;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
|
||||
import com.github.mikephil.charting.renderer.PieChartRenderer;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -40,12 +41,12 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
/**
|
||||
* array that holds the width of each pie-slice in degrees
|
||||
*/
|
||||
private float[] mDrawAngles;
|
||||
private float[] mDrawAngles = new float[1];
|
||||
|
||||
/**
|
||||
* array that holds the absolute angle in degrees of each slice
|
||||
*/
|
||||
private float[] mAbsoluteAngles;
|
||||
private float[] mAbsoluteAngles = new float[1];
|
||||
|
||||
/**
|
||||
* if true, the white hole inside the chart will be drawn
|
||||
|
@ -72,7 +73,7 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
*/
|
||||
private CharSequence mCenterText = "";
|
||||
|
||||
private PointF mCenterTextOffset = new PointF(0, 0);
|
||||
private MPPointF mCenterTextOffset = MPPointF.getInstance(0, 0);
|
||||
|
||||
/**
|
||||
* indicates the size of the hole in the center of the piechart, default:
|
||||
|
@ -150,7 +151,7 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
float diameter = getDiameter();
|
||||
float radius = diameter / 2f;
|
||||
|
||||
PointF c = getCenterOffsets();
|
||||
MPPointF c = getCenterOffsets();
|
||||
|
||||
float shift = mData.getDataSet().getSelectionShift();
|
||||
|
||||
|
@ -160,6 +161,8 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
c.y - radius + shift,
|
||||
c.x + radius - shift,
|
||||
c.y + radius - shift);
|
||||
|
||||
MPPointF.recycleInstance(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -170,7 +173,7 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
@Override
|
||||
protected float[] getMarkerPosition(Highlight highlight) {
|
||||
|
||||
PointF center = getCenterCircleBox();
|
||||
MPPointF center = getCenterCircleBox();
|
||||
float r = getRadius();
|
||||
|
||||
float off = r / 10f * 3.6f;
|
||||
|
@ -196,6 +199,7 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
* Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
|
||||
* mAnimator.getPhaseY())) + center.y);
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
return new float[]{x, y};
|
||||
}
|
||||
|
||||
|
@ -206,8 +210,20 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
|
||||
int entryCount = mData.getEntryCount();
|
||||
|
||||
mDrawAngles = new float[entryCount];
|
||||
mAbsoluteAngles = new float[entryCount];
|
||||
if(mDrawAngles.length != entryCount) {
|
||||
mDrawAngles = new float[entryCount];
|
||||
}else{
|
||||
for(int i = 0 ; i < entryCount ; i++){
|
||||
mDrawAngles[i] = 0;
|
||||
}
|
||||
}
|
||||
if(mAbsoluteAngles.length != entryCount) {
|
||||
mAbsoluteAngles = new float[entryCount];
|
||||
}else{
|
||||
for(int i = 0 ; i < entryCount ; i++){
|
||||
mAbsoluteAngles[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
float yValueSum = mData.getYValueSum();
|
||||
|
||||
|
@ -459,8 +475,8 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
public PointF getCenterCircleBox() {
|
||||
return new PointF(mCircleBox.centerX(), mCircleBox.centerY());
|
||||
public MPPointF getCenterCircleBox() {
|
||||
return MPPointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -498,7 +514,8 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
* @param y
|
||||
*/
|
||||
public void setCenterTextOffset(float x, float y) {
|
||||
mCenterTextOffset = new PointF(Utils.convertDpToPixel(x), Utils.convertDpToPixel(y));
|
||||
mCenterTextOffset.x = Utils.convertDpToPixel(x);
|
||||
mCenterTextOffset.y = Utils.convertDpToPixel(y);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -506,8 +523,8 @@ public class PieChart extends PieRadarChartBase<PieData> {
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
public PointF getCenterTextOffset() {
|
||||
return mCenterTextOffset;
|
||||
public MPPointF getCenterTextOffset() {
|
||||
return MPPointF.getInstance(mCenterTextOffset.x, mCenterTextOffset.y);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.github.mikephil.charting.data.Entry;
|
|||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
|
||||
import com.github.mikephil.charting.listener.PieRadarChartTouchListener;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
||||
/**
|
||||
|
@ -138,7 +139,7 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
|
|||
float legendWidth = fullLegendWidth + spacing;
|
||||
float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax;
|
||||
|
||||
PointF c = getCenter();
|
||||
MPPointF center = getCenter();
|
||||
|
||||
float bottomX = mLegend.getHorizontalAlignment() ==
|
||||
Legend.LegendHorizontalAlignment.RIGHT
|
||||
|
@ -147,19 +148,22 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
|
|||
float bottomY = legendHeight + 15.f;
|
||||
float distLegend = distanceToCenter(bottomX, bottomY);
|
||||
|
||||
PointF reference = getPosition(c, getRadius(),
|
||||
MPPointF reference = getPosition(center, getRadius(),
|
||||
getAngleForPoint(bottomX, bottomY));
|
||||
|
||||
float distReference = distanceToCenter(reference.x, reference.y);
|
||||
float minOffset = Utils.convertDpToPixel(5f);
|
||||
|
||||
if (bottomY >= c.y && getHeight() - legendWidth > getWidth()) {
|
||||
if (bottomY >= center.y && getHeight() - legendWidth > getWidth()) {
|
||||
xLegendOffset = legendWidth;
|
||||
} else if (distLegend < distReference) {
|
||||
|
||||
float diff = distReference - distLegend;
|
||||
xLegendOffset = minOffset + diff;
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
MPPointF.recycleInstance(reference);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +262,7 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
|
|||
*/
|
||||
public float getAngleForPoint(float x, float y) {
|
||||
|
||||
PointF c = getCenterOffsets();
|
||||
MPPointF c = getCenterOffsets();
|
||||
|
||||
double tx = x - c.x, ty = y - c.y;
|
||||
double length = Math.sqrt(tx * tx + ty * ty);
|
||||
|
@ -276,10 +280,13 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
|
|||
if (angle > 360f)
|
||||
angle = angle - 360f;
|
||||
|
||||
MPPointF.recycleInstance(c);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable MPPointF instance.
|
||||
* Calculates the position around a center point, depending on the distance
|
||||
* from the center, and the angle of the position around the center.
|
||||
*
|
||||
|
@ -288,13 +295,18 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
|
|||
* @param angle in degrees, converted to radians internally
|
||||
* @return
|
||||
*/
|
||||
public PointF getPosition(PointF center, float dist, float angle) {
|
||||
public MPPointF getPosition(MPPointF center, float dist, float angle) {
|
||||
|
||||
PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))),
|
||||
(float) (center.y + dist * Math.sin(Math.toRadians(angle))));
|
||||
MPPointF p = MPPointF.getInstance(0,0);
|
||||
getPosition(center, dist, angle, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
public void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint){
|
||||
outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle)));
|
||||
outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance of a certain point on the chart to the center of the
|
||||
* chart.
|
||||
|
@ -305,7 +317,7 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
|
|||
*/
|
||||
public float distanceToCenter(float x, float y) {
|
||||
|
||||
PointF c = getCenterOffsets();
|
||||
MPPointF c = getCenterOffsets();
|
||||
|
||||
float dist = 0f;
|
||||
|
||||
|
@ -327,6 +339,8 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
|
|||
// pythagoras
|
||||
dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0));
|
||||
|
||||
MPPointF.recycleInstance(c);
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
|
|
|
@ -171,8 +171,8 @@ public class Legend extends ComponentBase {
|
|||
"colors array and labels array need to be of same size");
|
||||
}
|
||||
|
||||
this.mColors = Utils.convertIntegers(colors);
|
||||
this.mLabels = Utils.convertStrings(labels);
|
||||
this.setComputedColors(colors);
|
||||
this.setComputedLabels(labels);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,7 +180,11 @@ public class Legend extends ComponentBase {
|
|||
* @param colors
|
||||
*/
|
||||
public void setComputedColors(List<Integer> colors) {
|
||||
mColors = Utils.convertIntegers(colors);
|
||||
if(mColors != null && colors.size() == mColors.length){
|
||||
Utils.copyIntegers(colors, mColors);
|
||||
}else {
|
||||
mColors = Utils.convertIntegers(colors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,7 +192,11 @@ public class Legend extends ComponentBase {
|
|||
* @param labels
|
||||
*/
|
||||
public void setComputedLabels(List<String> labels) {
|
||||
mLabels = Utils.convertStrings(labels);
|
||||
if(mLabels != null && mLabels.length == labels.size()){
|
||||
Utils.copyStrings(labels, mLabels);
|
||||
}else {
|
||||
mLabels = Utils.convertStrings(labels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,8 +299,17 @@ public class Legend extends ComponentBase {
|
|||
* let the changes take effect)
|
||||
*/
|
||||
public void setExtra(List<Integer> colors, List<String> labels) {
|
||||
this.mExtraColors = Utils.convertIntegers(colors);
|
||||
this.mExtraLabels = Utils.convertStrings(labels);
|
||||
if(mExtraColors != null && mExtraColors.length == colors.size()){
|
||||
Utils.copyIntegers(colors, mExtraColors);
|
||||
}else {
|
||||
this.mExtraColors = Utils.convertIntegers(colors);
|
||||
}
|
||||
|
||||
if(mExtraLabels != null && mExtraLabels.length == labels.size()){
|
||||
Utils.copyStrings(labels, mExtraLabels);
|
||||
}else {
|
||||
this.mExtraLabels = Utils.convertStrings(labels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -343,8 +360,8 @@ public class Legend extends ComponentBase {
|
|||
"colors array and labels array need to be of same size");
|
||||
}
|
||||
|
||||
mColors = Utils.convertIntegers(colors);
|
||||
mLabels = Utils.convertStrings(labels);
|
||||
this.setComputedColors(colors);
|
||||
this.setComputedLabels(labels);
|
||||
mIsLegendCustom = true;
|
||||
}
|
||||
|
||||
|
@ -649,7 +666,7 @@ public class Legend extends ComponentBase {
|
|||
* sets the space between the form and the actual label/text, converts to dp
|
||||
* internally
|
||||
*
|
||||
* @param mFormToTextSpace
|
||||
* @param space
|
||||
*/
|
||||
public void setFormToTextSpace(float space) {
|
||||
this.mFormToTextSpace = Utils.convertDpToPixel(space);
|
||||
|
@ -708,7 +725,7 @@ public class Legend extends ComponentBase {
|
|||
/**
|
||||
* Calculates the full height of the drawn legend.
|
||||
*
|
||||
* @param mLegendLabelPaint
|
||||
* @param labelpaint
|
||||
* @return
|
||||
*/
|
||||
public float getFullHeight(Paint labelpaint) {
|
||||
|
@ -791,6 +808,7 @@ public class Legend extends ComponentBase {
|
|||
public void setMaxSizePercent(float maxSize) {
|
||||
mMaxSizePercent = maxSize;
|
||||
}
|
||||
private boolean isCalculatedLineSizesArrayListResized = true;
|
||||
|
||||
private FSize[] mCalculatedLabelSizes = new FSize[] {};
|
||||
private Boolean[] mCalculatedLabelBreakPoints = new Boolean[] {};
|
||||
|
@ -805,9 +823,19 @@ public class Legend extends ComponentBase {
|
|||
}
|
||||
|
||||
public FSize[] getCalculatedLineSizes() {
|
||||
if(mCalculatedLineSizes == null || isCalculatedLineSizesArrayListResized){
|
||||
|
||||
mCalculatedLineSizes = calculatedLineSizesForCalculateDimensions
|
||||
.toArray(new FSize[calculatedLineSizesForCalculateDimensions.size()]);
|
||||
|
||||
isCalculatedLineSizesArrayListResized = false;
|
||||
|
||||
}
|
||||
return mCalculatedLineSizes;
|
||||
}
|
||||
|
||||
protected Paint.FontMetrics fontMetricsForCalculateDimensions = new Paint.FontMetrics();
|
||||
protected ArrayList<FSize> calculatedLineSizesForCalculateDimensions = new ArrayList<>();
|
||||
/**
|
||||
* Calculates the dimensions of the Legend. This includes the maximum width
|
||||
* and height of a single entry, as well as the total width and height of
|
||||
|
@ -824,7 +852,7 @@ public class Legend extends ComponentBase {
|
|||
case VERTICAL: {
|
||||
|
||||
float maxWidth = 0f, maxHeight = 0f, width = 0f;
|
||||
float labelLineHeight = Utils.getLineHeight(labelpaint);
|
||||
float labelLineHeight = Utils.getLineHeight(labelpaint, fontMetricsForCalculateDimensions);
|
||||
final int count = mLabels.length;
|
||||
boolean wasStacked = false;
|
||||
|
||||
|
@ -877,14 +905,36 @@ public class Legend extends ComponentBase {
|
|||
case HORIZONTAL: {
|
||||
|
||||
int labelCount = mLabels.length;
|
||||
float labelLineHeight = Utils.getLineHeight(labelpaint);
|
||||
float labelLineSpacing = Utils.getLineSpacing(labelpaint) + mYEntrySpace;
|
||||
float labelLineHeight = Utils.getLineHeight(labelpaint, fontMetricsForCalculateDimensions);
|
||||
float labelLineSpacing = Utils.getLineSpacing(labelpaint, fontMetricsForCalculateDimensions) + mYEntrySpace;
|
||||
float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent;
|
||||
|
||||
// Prepare arrays for calculated layout
|
||||
ArrayList<FSize> calculatedLabelSizes = new ArrayList<FSize>(labelCount);
|
||||
ArrayList<Boolean> calculatedLabelBreakPoints = new ArrayList<Boolean>(labelCount);
|
||||
ArrayList<FSize> calculatedLineSizes = new ArrayList<FSize>();
|
||||
|
||||
//ArrayList<Boolean> calculatedLabelBreakPoints = new ArrayList<Boolean>(labelCount);
|
||||
|
||||
if(mCalculatedLabelSizes.length != labelCount){
|
||||
FSize[] temp = new FSize[labelCount];
|
||||
int count = mCalculatedLabelSizes.length;
|
||||
for(int i = 0 ; i < count && i < labelCount ; i++){
|
||||
temp[i] = mCalculatedLabelSizes[i];
|
||||
}
|
||||
while(count > labelCount){
|
||||
count--;
|
||||
FSize.recycleInstance(mCalculatedLabelSizes[count]);
|
||||
}
|
||||
mCalculatedLabelSizes = temp;
|
||||
}
|
||||
int calculatedLabelSizesIndex = 0;
|
||||
|
||||
if(mCalculatedLabelBreakPoints.length != labelCount){
|
||||
mCalculatedLabelBreakPoints = new Boolean[labelCount];
|
||||
}
|
||||
int calculatedLabelBreakIndex = 0;
|
||||
|
||||
ArrayList<FSize> calculatedLineSizes = calculatedLineSizesForCalculateDimensions;
|
||||
FSize.recycleInstances(calculatedLineSizes);
|
||||
calculatedLineSizes.clear();
|
||||
|
||||
// Start calculating layout
|
||||
float maxLineWidth = 0.f;
|
||||
|
@ -896,7 +946,8 @@ public class Legend extends ComponentBase {
|
|||
|
||||
boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP;
|
||||
|
||||
calculatedLabelBreakPoints.add(false);
|
||||
mCalculatedLabelBreakPoints[calculatedLabelBreakIndex] = false;
|
||||
calculatedLabelBreakIndex++;
|
||||
|
||||
if (stackedStartIndex == -1) {
|
||||
// we are not stacking, so required width is for this label
|
||||
|
@ -910,14 +961,25 @@ public class Legend extends ComponentBase {
|
|||
|
||||
// grouped forms have null labels
|
||||
if (mLabels[i] != null) {
|
||||
|
||||
calculatedLabelSizes.add(Utils.calcTextSize(labelpaint, mLabels[i]));
|
||||
if(mCalculatedLabelSizes[calculatedLabelSizesIndex] == null){
|
||||
mCalculatedLabelSizes[calculatedLabelSizesIndex] = Utils.calcTextSize(labelpaint, mLabels[i]);
|
||||
}else{
|
||||
Utils.calcTextSize(labelpaint, mLabels[i], mCalculatedLabelSizes[calculatedLabelSizesIndex]);
|
||||
}
|
||||
FSize labelSize = mCalculatedLabelSizes[calculatedLabelSizesIndex];
|
||||
calculatedLabelSizesIndex++;
|
||||
requiredWidth += drawingForm ? mFormToTextSpace + mFormSize : 0.f;
|
||||
requiredWidth += calculatedLabelSizes.get(i).width;
|
||||
requiredWidth += labelSize.width;
|
||||
}
|
||||
else {
|
||||
|
||||
calculatedLabelSizes.add(new FSize(0.f, 0.f));
|
||||
if(mCalculatedLabelSizes[calculatedLabelSizesIndex] == null){
|
||||
mCalculatedLabelSizes[calculatedLabelSizesIndex] = FSize.getInstance(0.f, 0.f);
|
||||
}else{
|
||||
mCalculatedLabelSizes[calculatedLabelSizesIndex].width = 0.f;
|
||||
mCalculatedLabelSizes[calculatedLabelSizesIndex].height = 0.f;
|
||||
}
|
||||
calculatedLabelSizesIndex++;
|
||||
requiredWidth += drawingForm ? mFormSize : 0.f;
|
||||
|
||||
if (stackedStartIndex == -1) {
|
||||
|
@ -942,19 +1004,19 @@ public class Legend extends ComponentBase {
|
|||
else { // It doesn't fit, we need to wrap a line
|
||||
|
||||
// Add current line size to array
|
||||
calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight));
|
||||
calculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight));
|
||||
maxLineWidth = Math.max(maxLineWidth, currentLineWidth);
|
||||
|
||||
// Start a new line
|
||||
calculatedLabelBreakPoints.set(
|
||||
mCalculatedLabelBreakPoints[
|
||||
stackedStartIndex > -1 ? stackedStartIndex
|
||||
: i, true);
|
||||
: i] = true;
|
||||
currentLineWidth = requiredWidth;
|
||||
}
|
||||
|
||||
if (i == labelCount - 1) {
|
||||
// Add last line size to array
|
||||
calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight));
|
||||
calculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight));
|
||||
maxLineWidth = Math.max(maxLineWidth, currentLineWidth);
|
||||
}
|
||||
}
|
||||
|
@ -962,12 +1024,13 @@ public class Legend extends ComponentBase {
|
|||
stackedStartIndex = mLabels[i] != null ? -1 : stackedStartIndex;
|
||||
}
|
||||
|
||||
mCalculatedLabelSizes = calculatedLabelSizes.toArray(
|
||||
new FSize[calculatedLabelSizes.size()]);
|
||||
mCalculatedLabelBreakPoints = calculatedLabelBreakPoints
|
||||
.toArray(new Boolean[calculatedLabelBreakPoints.size()]);
|
||||
mCalculatedLineSizes = calculatedLineSizes
|
||||
.toArray(new FSize[calculatedLineSizes.size()]);
|
||||
if(calculatedLineSizes.size() != mCalculatedLineSizes.length) {
|
||||
isCalculatedLineSizesArrayListResized = true;
|
||||
}else{
|
||||
for(int i = 0 ; i < mCalculatedLineSizes.length ; i++){
|
||||
mCalculatedLineSizes[i] = calculatedLineSizes.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
mNeededWidth = maxLineWidth;
|
||||
mNeededHeight = labelLineHeight
|
||||
|
|
|
@ -74,7 +74,10 @@ public class BarData extends BarLineScatterCandleBubbleData<IBarDataSet> {
|
|||
float start = fromX;
|
||||
fromX += groupSpaceWidthHalf;
|
||||
|
||||
for (IBarDataSet set : mDataSets) {
|
||||
IBarDataSet set;
|
||||
final int setCountJ = mDataSets.size();
|
||||
for(int j = 0 ; j < setCountJ ; j++){
|
||||
set = mDataSets.get(j);
|
||||
|
||||
fromX += barSpaceHalf;
|
||||
fromX += barWidthHalf;
|
||||
|
|
|
@ -51,10 +51,12 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet<BarEntry> impl
|
|||
calcEntryCountIncludingStacks(yVals);
|
||||
}
|
||||
|
||||
protected List<BarEntry> barEntriesForCopy = new ArrayList<>();
|
||||
@Override
|
||||
public DataSet<BarEntry> copy() {
|
||||
|
||||
List<BarEntry> yVals = new ArrayList<BarEntry>();
|
||||
List<BarEntry> yVals = barEntriesForCopy;
|
||||
yVals.clear();
|
||||
|
||||
for (int i = 0; i < mValues.size(); i++) {
|
||||
yVals.add(mValues.get(i).copy());
|
||||
|
|
|
@ -166,7 +166,11 @@ public abstract class BaseDataSet<T extends Entry> implements IDataSet<T> {
|
|||
*/
|
||||
public void setColors(int[] colors, Context c) {
|
||||
|
||||
List<Integer> clrs = new ArrayList<Integer>();
|
||||
if(mColors == null){
|
||||
mColors = new ArrayList<>();
|
||||
}
|
||||
List<Integer> clrs = mColors;
|
||||
clrs.clear();
|
||||
|
||||
for (int color : colors) {
|
||||
clrs.add(c.getResources().getColor(color));
|
||||
|
@ -224,7 +228,10 @@ public abstract class BaseDataSet<T extends Entry> implements IDataSet<T> {
|
|||
* Resets all colors of this DataSet and recreates the colors array.
|
||||
*/
|
||||
public void resetColors() {
|
||||
mColors = new ArrayList<Integer>();
|
||||
if(mColors == null) {
|
||||
mColors = new ArrayList<Integer>();
|
||||
}
|
||||
mColors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,7 +28,10 @@ public class BubbleData extends BarLineScatterCandleBubbleData<IBubbleDataSet> {
|
|||
* @param width
|
||||
*/
|
||||
public void setHighlightCircleWidth(float width) {
|
||||
for (IBubbleDataSet set : mDataSets) {
|
||||
IBubbleDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
set.setHighlightCircleWidth(width);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,10 +51,12 @@ public class BubbleDataSet extends BarLineScatterCandleBubbleDataSet<BubbleEntry
|
|||
}
|
||||
}
|
||||
|
||||
protected ArrayList<BubbleEntry> bubbleEntriesForCopy = new ArrayList<>();
|
||||
@Override
|
||||
public DataSet<BubbleEntry> copy() {
|
||||
|
||||
List<BubbleEntry> yVals = new ArrayList<BubbleEntry>();
|
||||
List<BubbleEntry> yVals = bubbleEntriesForCopy;
|
||||
yVals.clear();
|
||||
|
||||
for (int i = 0; i < mValues.size(); i++) {
|
||||
yVals.add(mValues.get(i).copy());
|
||||
|
|
|
@ -77,10 +77,12 @@ public class CandleDataSet extends LineScatterCandleRadarDataSet<CandleEntry> im
|
|||
super(yVals, label);
|
||||
}
|
||||
|
||||
protected ArrayList<CandleEntry> candleEntriesForCopy = new ArrayList<>();
|
||||
@Override
|
||||
public DataSet<CandleEntry> copy() {
|
||||
|
||||
List<CandleEntry> yVals = new ArrayList<CandleEntry>();
|
||||
List<CandleEntry> yVals = candleEntriesForCopy;
|
||||
yVals.clear();
|
||||
|
||||
for (int i = 0; i < mValues.size(); i++) {
|
||||
yVals.add(((CandleEntry) mValues.get(i)).copy());
|
||||
|
|
|
@ -80,9 +80,12 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
*/
|
||||
private List<T> arrayToList(T[] array) {
|
||||
|
||||
List<T> list = new ArrayList<>();
|
||||
int setsCount = array.length;
|
||||
List<T> list = new ArrayList<>(setsCount);
|
||||
|
||||
for (T set : array) {
|
||||
T set = null;
|
||||
for(int i = 0 ; i < setsCount ; i++){
|
||||
set = array[i];
|
||||
list.add(set);
|
||||
}
|
||||
|
||||
|
@ -129,7 +132,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
mXMax = -Float.MAX_VALUE;
|
||||
mXMin = Float.MAX_VALUE;
|
||||
|
||||
for (T set : mDataSets) {
|
||||
T set;
|
||||
int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
calcMinMax(set);
|
||||
}
|
||||
|
||||
|
@ -146,7 +152,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
mLeftAxisMax = firstLeft.getYMax();
|
||||
mLeftAxisMin = firstLeft.getYMin();
|
||||
|
||||
for (IDataSet dataSet : mDataSets) {
|
||||
IDataSet dataSet;
|
||||
int dataSetsCount = mDataSets.size();
|
||||
for(int i = 0 ; i < dataSetsCount ; i++){
|
||||
dataSet = mDataSets.get(i);
|
||||
if (dataSet.getAxisDependency() == AxisDependency.LEFT) {
|
||||
if (dataSet.getYMin() < mLeftAxisMin)
|
||||
mLeftAxisMin = dataSet.getYMin();
|
||||
|
@ -165,7 +174,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
mRightAxisMax = firstRight.getYMax();
|
||||
mRightAxisMin = firstRight.getYMin();
|
||||
|
||||
for (IDataSet dataSet : mDataSets) {
|
||||
IDataSet dataSet;
|
||||
int dataSetsCount = mDataSets.size();
|
||||
for(int i = 0 ; i < dataSetsCount ; i++){
|
||||
dataSet = mDataSets.get(i);
|
||||
if (dataSet.getAxisDependency() == AxisDependency.RIGHT) {
|
||||
if (dataSet.getYMin() < mRightAxisMin)
|
||||
mRightAxisMin = dataSet.getYMin();
|
||||
|
@ -614,7 +626,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* @return
|
||||
*/
|
||||
protected T getFirstLeft(List<T> sets) {
|
||||
for (T dataSet : sets) {
|
||||
T dataSet;
|
||||
int setCount = sets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
dataSet = sets.get(i);
|
||||
if (dataSet.getAxisDependency() == AxisDependency.LEFT)
|
||||
return dataSet;
|
||||
}
|
||||
|
@ -628,7 +643,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* @return
|
||||
*/
|
||||
public T getFirstRight(List<T> sets) {
|
||||
for (T dataSet : sets) {
|
||||
T dataSet;
|
||||
int setCount = sets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
dataSet = sets.get(i);
|
||||
if (dataSet.getAxisDependency() == AxisDependency.RIGHT)
|
||||
return dataSet;
|
||||
}
|
||||
|
@ -644,7 +662,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
if (f == null)
|
||||
return;
|
||||
else {
|
||||
for (IDataSet set : mDataSets) {
|
||||
IDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
set.setValueFormatter(f);
|
||||
}
|
||||
}
|
||||
|
@ -657,7 +678,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* @param color
|
||||
*/
|
||||
public void setValueTextColor(int color) {
|
||||
for (IDataSet set : mDataSets) {
|
||||
IDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
set.setValueTextColor(color);
|
||||
}
|
||||
}
|
||||
|
@ -669,7 +693,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* @param colors
|
||||
*/
|
||||
public void setValueTextColors(List<Integer> colors) {
|
||||
for (IDataSet set : mDataSets) {
|
||||
IDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
set.setValueTextColors(colors);
|
||||
}
|
||||
}
|
||||
|
@ -681,7 +708,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* @param tf
|
||||
*/
|
||||
public void setValueTypeface(Typeface tf) {
|
||||
for (IDataSet set : mDataSets) {
|
||||
IDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
set.setValueTypeface(tf);
|
||||
}
|
||||
}
|
||||
|
@ -693,7 +723,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* @param size
|
||||
*/
|
||||
public void setValueTextSize(float size) {
|
||||
for (IDataSet set : mDataSets) {
|
||||
IDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
set.setValueTextSize(size);
|
||||
}
|
||||
}
|
||||
|
@ -705,7 +738,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* @param enabled
|
||||
*/
|
||||
public void setDrawValues(boolean enabled) {
|
||||
for (IDataSet set : mDataSets) {
|
||||
IDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
set.setDrawValues(enabled);
|
||||
}
|
||||
}
|
||||
|
@ -716,7 +752,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* be highlighted programmatically or by touch gesture.
|
||||
*/
|
||||
public void setHighlightEnabled(boolean enabled) {
|
||||
for (IDataSet set : mDataSets) {
|
||||
IDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
set.setHighlightEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
@ -728,7 +767,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
* @return
|
||||
*/
|
||||
public boolean isHighlightEnabled() {
|
||||
for (IDataSet set : mDataSets) {
|
||||
IDataSet set;
|
||||
final int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
if (!set.isHighlightEnabled())
|
||||
return false;
|
||||
}
|
||||
|
@ -753,7 +795,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
*/
|
||||
public boolean contains(T dataSet) {
|
||||
|
||||
for (T set : mDataSets) {
|
||||
T set;
|
||||
int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
if (set.equals(dataSet))
|
||||
return true;
|
||||
}
|
||||
|
@ -770,7 +815,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
|
||||
int count = 0;
|
||||
|
||||
for (T set : mDataSets) {
|
||||
T set;
|
||||
int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
count += set.getEntryCount();
|
||||
}
|
||||
|
||||
|
@ -789,7 +837,10 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
|
|||
|
||||
T max = mDataSets.get(0);
|
||||
|
||||
for (T set : mDataSets) {
|
||||
T set;
|
||||
int setCount = mDataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = mDataSets.get(i);
|
||||
|
||||
if (set.getEntryCount() > max.getEntryCount())
|
||||
max = set;
|
||||
|
|
|
@ -57,7 +57,10 @@ public class CombinedData extends BarLineScatterCandleBubbleData<IBarLineScatter
|
|||
@Override
|
||||
public void calcMinMax() {
|
||||
|
||||
mDataSets = new ArrayList<>();
|
||||
if(mDataSets == null){
|
||||
mDataSets = new ArrayList<>();
|
||||
}
|
||||
mDataSets.clear();
|
||||
|
||||
mYMax = -Float.MAX_VALUE;
|
||||
mYMin = Float.MAX_VALUE;
|
||||
|
|
|
@ -295,6 +295,7 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
|
|||
return high;
|
||||
}
|
||||
|
||||
protected ArrayList<T> entriesForGetEntriesForXPos = new ArrayList<>(2);
|
||||
/**
|
||||
* Returns all Entry objects at the given xIndex. INFORMATION: This method
|
||||
* does calculations at runtime. Do not over-use in performance critical
|
||||
|
@ -306,7 +307,8 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
|
|||
@Override
|
||||
public List<T> getEntriesForXPos(float xVal) {
|
||||
|
||||
List<T> entries = new ArrayList<T>();
|
||||
List<T> entries = entriesForGetEntriesForXPos;
|
||||
entries.clear();
|
||||
|
||||
int low = 0;
|
||||
int high = mValues.size() - 1;
|
||||
|
|
|
@ -52,8 +52,11 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
|
|||
// mCircleRadius = Utils.convertDpToPixel(4f);
|
||||
// mLineWidth = Utils.convertDpToPixel(1f);
|
||||
|
||||
mCircleColors = new ArrayList<Integer>();
|
||||
|
||||
if(mCircleColors == null) {
|
||||
mCircleColors = new ArrayList<Integer>();
|
||||
}
|
||||
mCircleColors.clear();
|
||||
|
||||
// default colors
|
||||
// mColors.add(Color.rgb(192, 255, 140));
|
||||
// mColors.add(Color.rgb(255, 247, 140));
|
||||
|
@ -252,6 +255,10 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
|
|||
return mCircleColors.get(index % mCircleColors.size());
|
||||
}
|
||||
|
||||
public int getCircleColorCount(){
|
||||
return mCircleColors.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the colors that should be used for the circles of this DataSet.
|
||||
* Colors are reused as soon as the number of Entries the DataSet represents
|
||||
|
@ -290,7 +297,11 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
|
|||
*/
|
||||
public void setCircleColors(int[] colors, Context c) {
|
||||
|
||||
List<Integer> clrs = new ArrayList<Integer>();
|
||||
List<Integer> clrs = mCircleColors;
|
||||
if(clrs == null){
|
||||
clrs = new ArrayList<>();
|
||||
}
|
||||
clrs.clear();
|
||||
|
||||
for (int color : colors) {
|
||||
clrs.add(c.getResources().getColor(color));
|
||||
|
@ -314,7 +325,10 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
|
|||
* resets the circle-colors array and creates a new one
|
||||
*/
|
||||
public void resetCircleColors() {
|
||||
mCircleColors = new ArrayList<Integer>();
|
||||
if(mCircleColors == null) {
|
||||
mCircleColors = new ArrayList<Integer>();
|
||||
}
|
||||
mCircleColors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,7 +29,11 @@ public class ScatterData extends BarLineScatterCandleBubbleData<IScatterDataSet>
|
|||
|
||||
float max = 0f;
|
||||
|
||||
for (IScatterDataSet set : mDataSets) {
|
||||
|
||||
IScatterDataSet set;
|
||||
final int count = mDataSets.size();
|
||||
for(int i = 0 ; i < count ; i++){
|
||||
set = mDataSets.get(i);
|
||||
float size = set.getScatterShapeSize();
|
||||
|
||||
if (size > max)
|
||||
|
|
|
@ -10,9 +10,9 @@ import java.text.DecimalFormat;
|
|||
public class DefaultAxisValueFormatter implements AxisValueFormatter {
|
||||
|
||||
/**
|
||||
* decimalformat for formatting
|
||||
* FormattedStringFormat for formatting and caching
|
||||
*/
|
||||
protected DecimalFormat mFormat;
|
||||
protected FormattedStringCache.PrimFloat mFormattedStringCache;
|
||||
|
||||
/**
|
||||
* the number of decimal digits this formatter uses
|
||||
|
@ -35,13 +35,15 @@ public class DefaultAxisValueFormatter implements AxisValueFormatter {
|
|||
b.append("0");
|
||||
}
|
||||
|
||||
mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
|
||||
mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value, AxisBase axis) {
|
||||
// avoid memory allocations here (for performance)
|
||||
return mFormat.format(value);
|
||||
|
||||
// TODO: There should be a better way to do this. Floats are not the best keys...
|
||||
return mFormattedStringCache.getFormattedValue(value);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,9 +15,9 @@ import java.text.DecimalFormat;
|
|||
public class DefaultValueFormatter implements ValueFormatter {
|
||||
|
||||
/**
|
||||
* DecimalFormat for formatting
|
||||
* FormattedStringCache for formatting and caching.
|
||||
*/
|
||||
protected DecimalFormat mFormat;
|
||||
protected FormattedStringCache.Generic<Integer, Float> mFormattedStringCache;
|
||||
|
||||
protected int mDecimalDigits;
|
||||
|
||||
|
@ -47,16 +47,15 @@ public class DefaultValueFormatter implements ValueFormatter {
|
|||
b.append("0");
|
||||
}
|
||||
|
||||
mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
|
||||
mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
|
||||
|
||||
// put more logic here ...
|
||||
// avoid memory allocations here (for performance reasons)
|
||||
return mFormattedStringCache.getFormattedValue(value, dataSetIndex);
|
||||
|
||||
return mFormat.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
package com.github.mikephil.charting.formatter;
|
||||
|
||||
import java.text.Format;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by Tony Patino on 6/29/16.
|
||||
*
|
||||
* COST : Frequently the V type, and the K type will often be passed as primitives (float / int / double)
|
||||
* This will incur an auto-boxing penalty, and an instantiation on each call.
|
||||
*
|
||||
* BENEFIT : Formatting of Strings is one of the costliest operations remaining, and it is larger than the boxing penalty.
|
||||
* Eliminating redundant formats helps more than boxing hurts.
|
||||
*
|
||||
* Possibly want to make some explicit primitive enabled caches, though they can be ugly.
|
||||
*
|
||||
*/
|
||||
public class FormattedStringCache {
|
||||
|
||||
protected Format mFormat;
|
||||
|
||||
public Format getFormat(){
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
public FormattedStringCache(Format format){
|
||||
this.mFormat = format;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cache a Formatted String, derived from formatting value V in the Format passed to the constructor.
|
||||
* Use the object K as a way to quickly look up the string in the cache.
|
||||
* If value of V has changed since the last time the cache was accessed, re-format the string.
|
||||
*
|
||||
* NOTE: This version relies on auto-boxing of primitive values. As a result, it has worse memory performance
|
||||
* than the Prim versions.
|
||||
*
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
*/
|
||||
public static class Generic<K,V> extends FormattedStringCache{
|
||||
|
||||
private HashMap<K, String> mCachedStringsHashMap = new HashMap<>();
|
||||
private HashMap<K, V> mCachedValuesHashMap = new HashMap<>();
|
||||
|
||||
public Generic(Format format){
|
||||
super(format);
|
||||
}
|
||||
|
||||
public String getFormattedValue(V value, K key){
|
||||
|
||||
// If we can't find the value at all, create an entry for it, and format the string.
|
||||
if(!mCachedValuesHashMap.containsKey(key)){
|
||||
mCachedStringsHashMap.put(key, mFormat.format(value));
|
||||
mCachedValuesHashMap.put(key, value);
|
||||
}
|
||||
|
||||
// If the old value and the new one don't match, format the string and store it, because the string's value will be different now.
|
||||
if(!value.equals(mCachedValuesHashMap.get(key))){
|
||||
mCachedStringsHashMap.put(key, mFormat.format(value));
|
||||
mCachedValuesHashMap.put(key, value);
|
||||
}
|
||||
|
||||
String result = mCachedStringsHashMap.get(key);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache a Formatted String, derived from formatting float value in the Format passed to the constructor.
|
||||
* Use the integer as a way to quickly look up the string in the cache.
|
||||
* If value of V has changed since the last time the cache was accessed, re-format the string.
|
||||
*
|
||||
*/
|
||||
public static class PrimIntFloat extends FormattedStringCache{
|
||||
|
||||
public PrimIntFloat(Format format){
|
||||
super(format);
|
||||
}
|
||||
|
||||
private ArrayList<Float> cachedValues = new ArrayList<>();
|
||||
private ArrayList<String> cachedStrings = new ArrayList<>();
|
||||
|
||||
public String getFormattedValue(float value, int key) {
|
||||
|
||||
boolean hasValueAtdataSetIndex = true;
|
||||
if(cachedValues.size() <= key){
|
||||
int p = key;
|
||||
while(p >= 0){
|
||||
if(p == 0){
|
||||
cachedValues.add(value);
|
||||
cachedStrings.add("");
|
||||
}else{
|
||||
cachedValues.add(Float.NaN);
|
||||
cachedStrings.add("");
|
||||
}
|
||||
p--;
|
||||
}
|
||||
hasValueAtdataSetIndex = false;
|
||||
}
|
||||
|
||||
if(hasValueAtdataSetIndex) {
|
||||
Float cachedValue = cachedValues.get(key);
|
||||
hasValueAtdataSetIndex = !(cachedValue == null || cachedValue != value);
|
||||
}
|
||||
|
||||
if(!hasValueAtdataSetIndex){
|
||||
cachedValues.set(key, value);
|
||||
cachedStrings.set(key, mFormat.format(value));
|
||||
}
|
||||
|
||||
return cachedStrings.get(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache a Formatted String, derived from formatting float value in the Format passed to the constructor.
|
||||
*/
|
||||
public static class PrimFloat extends FormattedStringCache{
|
||||
|
||||
public PrimFloat(Format format){
|
||||
super(format);
|
||||
}
|
||||
|
||||
|
||||
private ArrayList<Float> cachedValues = new ArrayList<>();
|
||||
private ArrayList<String> cachedStrings = new ArrayList<>();
|
||||
|
||||
public String getFormattedValue(float value) {
|
||||
|
||||
boolean alreadyHasValue = false;
|
||||
int vCount = cachedValues.size();
|
||||
int sIndex = -1;
|
||||
for(int i = 0 ; i < vCount ; i++){
|
||||
if(cachedValues.get(i) == value){
|
||||
sIndex = i;
|
||||
alreadyHasValue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!alreadyHasValue) {
|
||||
cachedValues.add(value);
|
||||
cachedStrings.add(mFormat.format(value));
|
||||
sIndex = cachedValues.size() - 1;
|
||||
}
|
||||
|
||||
return cachedStrings.get(sIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache a Formatted String, derived from formatting double value in the Format passed to the constructor.
|
||||
*/
|
||||
public static class PrimDouble extends FormattedStringCache{
|
||||
|
||||
public PrimDouble(Format format){
|
||||
super(format);
|
||||
}
|
||||
|
||||
|
||||
private ArrayList<Double> cachedValues = new ArrayList<>();
|
||||
private ArrayList<String> cachedStrings = new ArrayList<>();
|
||||
|
||||
public String getFormattedValue(double value) {
|
||||
|
||||
boolean alreadyHasValue = false;
|
||||
int vCount = cachedValues.size();
|
||||
int sIndex = -1;
|
||||
for(int i = 0 ; i < vCount ; i++){
|
||||
if(cachedValues.get(i) == value){
|
||||
sIndex = i;
|
||||
alreadyHasValue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!alreadyHasValue) {
|
||||
cachedValues.add(value);
|
||||
cachedStrings.add(mFormat.format(value));
|
||||
sIndex = cachedValues.size() - 1;
|
||||
}
|
||||
|
||||
return cachedStrings.get(sIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -23,11 +23,16 @@ public class LargeValueFormatter implements ValueFormatter, AxisValueFormatter {
|
|||
"", "k", "m", "b", "t"
|
||||
};
|
||||
private static final int MAX_LENGTH = 5;
|
||||
private DecimalFormat mFormat;
|
||||
private String mText = "";
|
||||
|
||||
|
||||
/**
|
||||
* FormattedStringCache for formatting and caching.
|
||||
*/
|
||||
protected FormattedStringCache.PrimDouble mFormattedStringCache;
|
||||
|
||||
public LargeValueFormatter() {
|
||||
mFormat = new DecimalFormat("###E00");
|
||||
mFormattedStringCache = new FormattedStringCache.PrimDouble(new DecimalFormat("###E00"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,7 +82,8 @@ public class LargeValueFormatter implements ValueFormatter, AxisValueFormatter {
|
|||
*/
|
||||
private String makePretty(double number) {
|
||||
|
||||
String r = mFormat.format(number);
|
||||
// TODO : Should be better way to do this. Double isn't the best key...
|
||||
String r = mFormattedStringCache.getFormattedValue(number);
|
||||
|
||||
int numericValue1 = Character.getNumericValue(r.charAt(r.length() - 1));
|
||||
int numericValue2 = Character.getNumericValue(r.charAt(r.length() - 2));
|
||||
|
|
|
@ -15,10 +15,12 @@ import java.text.DecimalFormat;
|
|||
*/
|
||||
public class PercentFormatter implements ValueFormatter, AxisValueFormatter {
|
||||
|
||||
protected DecimalFormat mFormat;
|
||||
protected FormattedStringCache.Generic<Integer, Float> mFormattedStringCache;
|
||||
protected FormattedStringCache.PrimFloat mFormattedStringCacheAxis;
|
||||
|
||||
public PercentFormatter() {
|
||||
mFormat = new DecimalFormat("###,###,##0.0");
|
||||
mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,##0.0"));
|
||||
mFormattedStringCacheAxis = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,##0.0"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,19 +29,21 @@ public class PercentFormatter implements ValueFormatter, AxisValueFormatter {
|
|||
* @param format
|
||||
*/
|
||||
public PercentFormatter(DecimalFormat format) {
|
||||
this.mFormat = format;
|
||||
mFormattedStringCache = new FormattedStringCache.Generic<>(format);
|
||||
mFormattedStringCacheAxis = new FormattedStringCache.PrimFloat(format);
|
||||
}
|
||||
|
||||
// ValueFormatter
|
||||
@Override
|
||||
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
|
||||
return mFormat.format(value) + " %";
|
||||
return mFormattedStringCache.getFormattedValue(value, dataSetIndex) + " %";
|
||||
}
|
||||
|
||||
// AxisValueFormatter
|
||||
@Override
|
||||
public String getFormattedValue(float value, AxisBase axis) {
|
||||
return mFormat.format(value) + " %";
|
||||
// TODO: Find a better way to do this. Float isn't the best key...
|
||||
return mFormattedStringCacheAxis.getFormattedValue(value) + " %";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,9 @@ public class StackedValueFormatter implements ValueFormatter {
|
|||
* if true, all stack values of the stacked bar entry are drawn, else only top
|
||||
*/
|
||||
private boolean mDrawWholeStack;
|
||||
private FormattedStringCache.Generic mFormattedStringCacheWholeStack;
|
||||
private FormattedStringCache.Generic mFormattedStringCache;
|
||||
|
||||
|
||||
/**
|
||||
* a string that should be appended behind the value
|
||||
|
@ -44,12 +47,16 @@ public class StackedValueFormatter implements ValueFormatter {
|
|||
b.append("0");
|
||||
}
|
||||
|
||||
this.mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
|
||||
this.mFormattedStringCache = new FormattedStringCache.Generic(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
this.mFormattedStringCacheWholeStack = new FormattedStringCache.Generic(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
|
||||
|
||||
FormattedStringCache.Generic chosenCache = mFormattedStringCache;
|
||||
int chosenIndex = dataSetIndex;
|
||||
float chosenValue = value;
|
||||
if (!mDrawWholeStack && entry instanceof BarEntry) {
|
||||
|
||||
BarEntry barEntry = (BarEntry) entry;
|
||||
|
@ -59,16 +66,19 @@ public class StackedValueFormatter implements ValueFormatter {
|
|||
|
||||
// find out if we are on top of the stack
|
||||
if (vals[vals.length - 1] == value) {
|
||||
|
||||
// return the "sum" across all stack values
|
||||
return mFormat.format(barEntry.getY()) + mAppendix;
|
||||
chosenCache = mFormattedStringCacheWholeStack;
|
||||
chosenValue = barEntry.getY();
|
||||
} else {
|
||||
return ""; // return empty
|
||||
chosenCache = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(chosenCache == null){
|
||||
return "";
|
||||
}
|
||||
|
||||
// return the "proposed" value
|
||||
return mFormat.format(value) + mAppendix;
|
||||
return chosenCache.getFormattedValue(chosenValue, chosenIndex) + mAppendix;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ public class BarHighlighter extends ChartHighlighter<BarDataProvider> {
|
|||
(float) pos.y);
|
||||
}
|
||||
|
||||
PointD.recycleInstance(pos);
|
||||
|
||||
return high;
|
||||
}
|
||||
|
||||
|
@ -78,6 +80,8 @@ public class BarHighlighter extends ChartHighlighter<BarDataProvider> {
|
|||
high.getAxis()
|
||||
);
|
||||
|
||||
PointD.recycleInstance(pixels);
|
||||
|
||||
return stackedHigh;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,15 +28,20 @@ public class ChartHighlighter<T extends BarLineScatterCandleBubbleDataProvider>
|
|||
@Override
|
||||
public Highlight getHighlight(float x, float y) {
|
||||
|
||||
float xVal = (float) getValsForTouch(x, y).x;
|
||||
PointD pos = getValsForTouch(x, y);
|
||||
float xVal = (float) pos.x;
|
||||
PointD.recycleInstance(pos);
|
||||
|
||||
Highlight high = getHighlightForX(xVal, x, y);
|
||||
return high;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable PointD instance.
|
||||
* Returns the corresponding xPos for a given touch-position in pixels.
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
protected PointD getValsForTouch(float x, float y) {
|
||||
|
@ -105,6 +110,8 @@ public class ChartHighlighter<T extends BarLineScatterCandleBubbleDataProvider>
|
|||
return h.getYPx();
|
||||
}
|
||||
|
||||
|
||||
protected ArrayList<Highlight> highlightsForGetHighlightsAtXPos = new ArrayList<>(2);
|
||||
/**
|
||||
* Returns a list of Highlight objects representing the entries closest to the given xVal.
|
||||
* The returned list contains two objects per DataSet (closest rounding up, closest rounding down).
|
||||
|
@ -116,7 +123,8 @@ public class ChartHighlighter<T extends BarLineScatterCandleBubbleDataProvider>
|
|||
*/
|
||||
protected List<Highlight> getHighlightsAtXPos(float xVal, float x, float y) {
|
||||
|
||||
List<Highlight> vals = new ArrayList<Highlight>();
|
||||
List<Highlight> vals = highlightsForGetHighlightsAtXPos;
|
||||
vals.clear();
|
||||
|
||||
BarLineScatterCandleBubbleData data = getData();
|
||||
|
||||
|
|
|
@ -28,10 +28,13 @@ public class CombinedHighlighter extends ChartHighlighter<CombinedDataProvider>
|
|||
barHighlighter = barChart.getBarData() == null ? null : new BarHighlighter(barChart);
|
||||
}
|
||||
|
||||
|
||||
protected ArrayList<Highlight> highlightsForGetHighlightsAtXPos = new ArrayList<>(2);
|
||||
@Override
|
||||
protected List<Highlight> getHighlightsAtXPos(float xVal, float x, float y) {
|
||||
|
||||
List<Highlight> vals = new ArrayList<Highlight>();
|
||||
List<Highlight> vals = highlightsForGetHighlightsAtXPos;
|
||||
vals.clear();
|
||||
|
||||
List<BarLineScatterCandleBubbleData> dataObjects = mChart.getCombinedData().getAllData();
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ public class HorizontalBarHighlighter extends BarHighlighter {
|
|||
(float) pos.x);
|
||||
}
|
||||
|
||||
PointD.recycleInstance(pos);
|
||||
|
||||
return high;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.graphics.PointF;
|
|||
import com.github.mikephil.charting.charts.RadarChart;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -43,6 +44,7 @@ public class RadarHighlighter extends PieRadarHighlighter<RadarChart> {
|
|||
return closest;
|
||||
}
|
||||
|
||||
protected ArrayList<Highlight> highlightsForGetHighlightsAtIndex = new ArrayList<>(2);
|
||||
/**
|
||||
* Returns an array of Highlight objects for the given index. The Highlight
|
||||
* objects give information about the value at the selected index and the
|
||||
|
@ -54,13 +56,15 @@ public class RadarHighlighter extends PieRadarHighlighter<RadarChart> {
|
|||
*/
|
||||
protected List<Highlight> getHighlightsAtIndex(int index) {
|
||||
|
||||
List<Highlight> vals = new ArrayList<Highlight>();
|
||||
List<Highlight> vals = highlightsForGetHighlightsAtIndex;
|
||||
vals.clear();
|
||||
|
||||
float phaseX = mChart.getAnimator().getPhaseX();
|
||||
float phaseY = mChart.getAnimator().getPhaseY();
|
||||
float sliceangle = mChart.getSliceAngle();
|
||||
float factor = mChart.getFactor();
|
||||
|
||||
MPPointF pOut = MPPointF.getInstance(0,0);
|
||||
for (int i = 0; i < mChart.getData().getDataSetCount(); i++) {
|
||||
|
||||
IDataSet<?> dataSet = mChart.getData().getDataSetByIndex(i);
|
||||
|
@ -69,11 +73,11 @@ public class RadarHighlighter extends PieRadarHighlighter<RadarChart> {
|
|||
|
||||
float y = (entry.getY() - mChart.getYChartMin());
|
||||
|
||||
PointF p = Utils.getPosition(
|
||||
Utils.getPosition(
|
||||
mChart.getCenterOffsets(), y * factor * phaseY,
|
||||
sliceangle * index * phaseX + mChart.getRotationAngle());
|
||||
sliceangle * index * phaseX + mChart.getRotationAngle(), pOut);
|
||||
|
||||
vals.add(new Highlight(index, entry.getY(), p.x, p.y, i, dataSet.getAxisDependency()));
|
||||
vals.add(new Highlight(index, entry.getY(), pOut.x, pOut.y, i, dataSet.getAxisDependency()));
|
||||
}
|
||||
|
||||
return vals;
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.graphics.RectF;
|
|||
|
||||
import com.github.mikephil.charting.data.ChartData;
|
||||
import com.github.mikephil.charting.formatter.ValueFormatter;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
||||
/**
|
||||
|
@ -56,9 +57,9 @@ public interface ChartInterface {
|
|||
|
||||
int getHeight();
|
||||
|
||||
PointF getCenterOfView();
|
||||
MPPointF getCenterOfView();
|
||||
|
||||
PointF getCenterOffsets();
|
||||
MPPointF getCenterOffsets();
|
||||
|
||||
RectF getContentRect();
|
||||
|
||||
|
|
|
@ -52,6 +52,13 @@ public interface ILineDataSet extends ILineRadarDataSet<Entry> {
|
|||
*/
|
||||
int getCircleColor(int index);
|
||||
|
||||
/**
|
||||
* Returns the number of colors in this DataSet's circle-color array.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getCircleColorCount();
|
||||
|
||||
/**
|
||||
* Returns true if drawing circles for this DataSet is enabled, false if not
|
||||
*
|
||||
|
|
|
@ -5,14 +5,42 @@ import android.animation.ValueAnimator;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.view.View;
|
||||
|
||||
import com.github.mikephil.charting.utils.ObjectPool;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
/**
|
||||
* Created by Philipp Jahoda on 19/02/16.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public class AnimatedMoveViewJob extends AnimatedViewPortJob {
|
||||
|
||||
private static ObjectPool<AnimatedMoveViewJob> pool;
|
||||
|
||||
static {
|
||||
pool = ObjectPool.create(4, new AnimatedMoveViewJob(null,0,0,null,null,0,0,0));
|
||||
pool.setReplenishPercentage(0.5f);
|
||||
}
|
||||
|
||||
public static AnimatedMoveViewJob getInstance(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration){
|
||||
AnimatedMoveViewJob result = pool.get();
|
||||
result.mViewPortHandler = viewPortHandler;
|
||||
result.xValue = xValue;
|
||||
result.yValue = yValue;
|
||||
result.mTrans = trans;
|
||||
result.view = v;
|
||||
result.xOrigin = xOrigin;
|
||||
result.yOrigin = yOrigin;
|
||||
result.resetAnimator();
|
||||
result.animator.setDuration(duration);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void recycleInstance(AnimatedMoveViewJob instance){
|
||||
pool.recycle(instance);
|
||||
}
|
||||
|
||||
|
||||
public AnimatedMoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) {
|
||||
super(viewPortHandler, xValue, yValue, trans, v, xOrigin, yOrigin, duration);
|
||||
}
|
||||
|
@ -26,4 +54,13 @@ public class AnimatedMoveViewJob extends AnimatedViewPortJob {
|
|||
mTrans.pointValuesToPixel(pts);
|
||||
mViewPortHandler.centerViewPort(pts, view);
|
||||
}
|
||||
|
||||
public void recycleSelf(){
|
||||
recycleInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectPool.Poolable instantiate() {
|
||||
return new AnimatedMoveViewJob(null,0,0,null,null,0,0,0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.github.mikephil.charting.jobs;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
|
@ -12,7 +13,7 @@ import com.github.mikephil.charting.utils.ViewPortHandler;
|
|||
* Created by Philipp Jahoda on 19/02/16.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAnimator.AnimatorUpdateListener {
|
||||
public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
|
||||
|
||||
protected ObjectAnimator animator;
|
||||
|
||||
|
@ -28,6 +29,7 @@ public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAn
|
|||
animator = ObjectAnimator.ofFloat(this, "phase", 0f, 1f);
|
||||
animator.setDuration(duration);
|
||||
animator.addUpdateListener(this);
|
||||
animator.addListener(this);
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
|
@ -51,4 +53,47 @@ public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAn
|
|||
public float getYOrigin() {
|
||||
return yOrigin;
|
||||
}
|
||||
|
||||
public abstract void recycleSelf();
|
||||
|
||||
protected void resetAnimator(){
|
||||
animator.removeAllListeners();
|
||||
animator.removeAllUpdateListeners();
|
||||
animator.reverse();
|
||||
animator.addUpdateListener(this);
|
||||
animator.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
try{
|
||||
recycleSelf();
|
||||
}catch (IllegalArgumentException e){
|
||||
// don't worry about it.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
try{
|
||||
recycleSelf();
|
||||
}catch (IllegalArgumentException e){
|
||||
// don't worry about it.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.view.View;
|
|||
|
||||
import com.github.mikephil.charting.charts.BarLineChartBase;
|
||||
import com.github.mikephil.charting.components.YAxis;
|
||||
import com.github.mikephil.charting.utils.ObjectPool;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -17,6 +18,26 @@ import com.github.mikephil.charting.utils.ViewPortHandler;
|
|||
@SuppressLint("NewApi")
|
||||
public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.AnimatorListener {
|
||||
|
||||
private static ObjectPool<AnimatedZoomJob> pool;
|
||||
|
||||
static {
|
||||
pool = ObjectPool.create(8, new AnimatedZoomJob(null,null,null,null,0,0,0,0,0,0,0,0,0,0));
|
||||
}
|
||||
|
||||
public static AnimatedZoomJob getInstance(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xAxisRange, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) {
|
||||
AnimatedZoomJob result = pool.get();
|
||||
result.mViewPortHandler = viewPortHandler;
|
||||
result.xValue = scaleX;
|
||||
result.yValue = scaleY;
|
||||
result.mTrans = trans;
|
||||
result.view = v;
|
||||
result.xOrigin = xOrigin;
|
||||
result.yOrigin = yOrigin;
|
||||
result.resetAnimator();
|
||||
result.animator.setDuration(duration);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected float zoomOriginX;
|
||||
protected float zoomOriginY;
|
||||
|
||||
|
@ -40,13 +61,15 @@ public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.Ani
|
|||
this.xAxisRange = xAxisRange;
|
||||
}
|
||||
|
||||
protected Matrix mOnAnimationUpdateMatrixBuffer = new Matrix();
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
|
||||
float scaleX = xOrigin + (xValue - xOrigin) * phase;
|
||||
float scaleY = yOrigin + (yValue - yOrigin) * phase;
|
||||
|
||||
Matrix save = mViewPortHandler.setZoom(scaleX, scaleY);
|
||||
Matrix save = mOnAnimationUpdateMatrixBuffer;
|
||||
mViewPortHandler.setZoom(scaleX, scaleY, save);
|
||||
mViewPortHandler.refresh(save, view, false);
|
||||
|
||||
float valsInView = yAxis.mAxisRange / mViewPortHandler.getScaleY();
|
||||
|
@ -57,7 +80,7 @@ public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.Ani
|
|||
|
||||
mTrans.pointValuesToPixel(pts);
|
||||
|
||||
save = mViewPortHandler.translate(pts);
|
||||
mViewPortHandler.translate(pts, save);
|
||||
mViewPortHandler.refresh(save, view, true);
|
||||
}
|
||||
|
||||
|
@ -77,8 +100,18 @@ public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.Ani
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycleSelf() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectPool.Poolable instantiate() {
|
||||
return new AnimatedZoomJob(null,null,null,null,0,0,0,0,0,0,0,0,0,0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.github.mikephil.charting.jobs;
|
|||
|
||||
import android.view.View;
|
||||
|
||||
import com.github.mikephil.charting.utils.ObjectPool;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -11,6 +12,27 @@ import com.github.mikephil.charting.utils.ViewPortHandler;
|
|||
*/
|
||||
public class MoveViewJob extends ViewPortJob {
|
||||
|
||||
private static ObjectPool<MoveViewJob> pool;
|
||||
|
||||
static {
|
||||
pool = ObjectPool.create(2, new MoveViewJob(null,0,0,null,null));
|
||||
pool.setReplenishPercentage(0.5f);
|
||||
}
|
||||
|
||||
public static MoveViewJob getInstance(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v){
|
||||
MoveViewJob result = pool.get();
|
||||
result.mViewPortHandler = viewPortHandler;
|
||||
result.xValue = xValue;
|
||||
result.yValue = yValue;
|
||||
result.mTrans = trans;
|
||||
result.view = v;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void recycleInstance(MoveViewJob instance){
|
||||
pool.recycle(instance);
|
||||
}
|
||||
|
||||
public MoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v) {
|
||||
super(viewPortHandler, xValue, yValue, trans, v);
|
||||
}
|
||||
|
@ -23,5 +45,12 @@ public class MoveViewJob extends ViewPortJob {
|
|||
|
||||
mTrans.pointValuesToPixel(pts);
|
||||
mViewPortHandler.centerViewPort(pts, view);
|
||||
|
||||
this.recycleInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectPool.Poolable instantiate() {
|
||||
return new MoveViewJob(mViewPortHandler, xValue, yValue, mTrans, view);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.github.mikephil.charting.jobs;
|
|||
|
||||
import android.view.View;
|
||||
|
||||
import com.github.mikephil.charting.utils.ObjectPool;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -15,7 +16,7 @@ import com.github.mikephil.charting.utils.ViewPortHandler;
|
|||
*
|
||||
* @author Philipp Jahoda
|
||||
*/
|
||||
public abstract class ViewPortJob implements Runnable {
|
||||
public abstract class ViewPortJob extends ObjectPool.Poolable implements Runnable {
|
||||
|
||||
protected float[] pts = new float[2];
|
||||
|
||||
|
@ -33,6 +34,7 @@ public abstract class ViewPortJob implements Runnable {
|
|||
this.yValue = yValue;
|
||||
this.mTrans = trans;
|
||||
this.view = v;
|
||||
|
||||
}
|
||||
|
||||
public float getXValue() {
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.view.View;
|
|||
|
||||
import com.github.mikephil.charting.charts.BarLineChartBase;
|
||||
import com.github.mikephil.charting.components.YAxis;
|
||||
import com.github.mikephil.charting.utils.ObjectPool;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -14,6 +15,30 @@ import com.github.mikephil.charting.utils.ViewPortHandler;
|
|||
*/
|
||||
public class ZoomJob extends ViewPortJob {
|
||||
|
||||
private static ObjectPool<ZoomJob> pool;
|
||||
|
||||
static {
|
||||
pool = ObjectPool.create(1, new ZoomJob(null,0,0,0,0,null,null,null));
|
||||
pool.setReplenishPercentage(0.5f);
|
||||
}
|
||||
|
||||
public static ZoomJob getInstance(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, Transformer trans, YAxis.AxisDependency axis, View v) {
|
||||
ZoomJob result = pool.get();
|
||||
result.xValue = xValue;
|
||||
result.yValue = yValue;
|
||||
result.scaleX = scaleX;
|
||||
result.scaleY = scaleY;
|
||||
result.mViewPortHandler = viewPortHandler;
|
||||
result.mTrans = trans;
|
||||
result.axisDependency = axis;
|
||||
result.view = v;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void recycleInstance(ZoomJob instance){
|
||||
pool.recycle(instance);
|
||||
}
|
||||
|
||||
protected float scaleX;
|
||||
protected float scaleY;
|
||||
|
||||
|
@ -27,10 +52,12 @@ public class ZoomJob extends ViewPortJob {
|
|||
this.axisDependency = axis;
|
||||
}
|
||||
|
||||
protected Matrix mRunMatrixBuffer = new Matrix();
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
Matrix save = mViewPortHandler.zoom(scaleX, scaleY);
|
||||
Matrix save = mRunMatrixBuffer;
|
||||
mViewPortHandler.zoom(scaleX, scaleY, save);
|
||||
mViewPortHandler.refresh(save, view, false);
|
||||
|
||||
float valsInView = ((BarLineChartBase) view).getDeltaY(axisDependency) / mViewPortHandler.getScaleY();
|
||||
|
@ -41,10 +68,17 @@ public class ZoomJob extends ViewPortJob {
|
|||
|
||||
mTrans.pointValuesToPixel(pts);
|
||||
|
||||
save = mViewPortHandler.translate(pts);
|
||||
mViewPortHandler.translate(pts, save);
|
||||
mViewPortHandler.refresh(save, view, false);
|
||||
|
||||
((BarLineChartBase) view).calculateOffsets();
|
||||
view.postInvalidate();
|
||||
|
||||
recycleInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectPool.Poolable instantiate() {
|
||||
return new ZoomJob(null,0,0,0,0,null,null,null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.github.mikephil.charting.data.Entry;
|
|||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -41,12 +42,12 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
/**
|
||||
* point where the touch action started
|
||||
*/
|
||||
private PointF mTouchStartPoint = new PointF();
|
||||
private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0);
|
||||
|
||||
/**
|
||||
* center between two pointers (fingers on the display)
|
||||
*/
|
||||
private PointF mTouchPointCenter = new PointF();
|
||||
private MPPointF mTouchPointCenter = MPPointF.getInstance(0,0);
|
||||
|
||||
private float mSavedXDist = 1f;
|
||||
private float mSavedYDist = 1f;
|
||||
|
@ -60,8 +61,8 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
private VelocityTracker mVelocityTracker;
|
||||
|
||||
private long mDecelerationLastTime = 0;
|
||||
private PointF mDecelerationCurrentPoint = new PointF();
|
||||
private PointF mDecelerationVelocity = new PointF();
|
||||
private MPPointF mDecelerationCurrentPoint = MPPointF.getInstance(0,0);
|
||||
private MPPointF mDecelerationVelocity = MPPointF.getInstance(0,0);
|
||||
|
||||
/**
|
||||
* the distance of movement that will be counted as a drag
|
||||
|
@ -212,8 +213,12 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
stopDeceleration();
|
||||
|
||||
mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
mDecelerationCurrentPoint = new PointF(event.getX(), event.getY());
|
||||
mDecelerationVelocity = new PointF(velocityX, velocityY);
|
||||
|
||||
mDecelerationCurrentPoint.x = event.getX();
|
||||
mDecelerationCurrentPoint.y = event.getY();
|
||||
|
||||
mDecelerationVelocity.x = velocityX;
|
||||
mDecelerationVelocity.y = velocityY;
|
||||
|
||||
Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by
|
||||
// Google
|
||||
|
@ -275,7 +280,8 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
private void saveTouchStart(MotionEvent event) {
|
||||
|
||||
mSavedMatrix.set(mMatrix);
|
||||
mTouchStartPoint.set(event.getX(), event.getY());
|
||||
mTouchStartPoint.x = event.getX();
|
||||
mTouchStartPoint.y = event.getY();
|
||||
|
||||
mClosestDataSetToTouch = mChart.getDataSetByTouchPoint(event.getX(), event.getY());
|
||||
}
|
||||
|
@ -334,7 +340,7 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
if (totalDist > mMinScalePointerDistance) {
|
||||
|
||||
// get the translation
|
||||
PointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y);
|
||||
MPPointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y);
|
||||
ViewPortHandler h = mChart.getViewPortHandler();
|
||||
|
||||
// take actions depending on the activated touch mode
|
||||
|
@ -408,6 +414,8 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
l.onChartScale(event, 1f, scaleY);
|
||||
}
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -439,10 +447,11 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
* @param point
|
||||
* @param event
|
||||
*/
|
||||
private static void midPoint(PointF point, MotionEvent event) {
|
||||
private static void midPoint(MPPointF point, MotionEvent event) {
|
||||
float x = event.getX(0) + event.getX(1);
|
||||
float y = event.getY(0) + event.getY(1);
|
||||
point.set(x / 2f, y / 2f);
|
||||
point.x = (x / 2f);
|
||||
point.y = (y / 2f);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -482,6 +491,7 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable MPPointF instance.
|
||||
* returns the correct translation depending on the provided x and y touch
|
||||
* points
|
||||
*
|
||||
|
@ -489,7 +499,7 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
* @param y
|
||||
* @return
|
||||
*/
|
||||
public PointF getTrans(float x, float y) {
|
||||
public MPPointF getTrans(float x, float y) {
|
||||
|
||||
ViewPortHandler vph = mChart.getViewPortHandler();
|
||||
|
||||
|
@ -503,7 +513,7 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
yTrans = -(mChart.getMeasuredHeight() - y - vph.offsetBottom());
|
||||
}
|
||||
|
||||
return new PointF(xTrans, yTrans);
|
||||
return MPPointF.getInstance(xTrans, yTrans);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -554,13 +564,15 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
// check if double-tap zooming is enabled
|
||||
if (mChart.isDoubleTapToZoomEnabled() && mChart.getData().getEntryCount() > 0) {
|
||||
|
||||
PointF trans = getTrans(e.getX(), e.getY());
|
||||
MPPointF trans = getTrans(e.getX(), e.getY());
|
||||
|
||||
mChart.zoom(mChart.isScaleXEnabled() ? 1.4f : 1f, mChart.isScaleYEnabled() ? 1.4f : 1f, trans.x, trans.y);
|
||||
|
||||
if (mChart.isLogEnabled())
|
||||
Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: "
|
||||
+ trans.y);
|
||||
|
||||
MPPointF.recycleInstance(trans);
|
||||
}
|
||||
|
||||
return super.onDoubleTap(e);
|
||||
|
@ -615,7 +627,8 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
|
|||
}
|
||||
|
||||
public void stopDeceleration() {
|
||||
mDecelerationVelocity = new PointF(0.f, 0.f);
|
||||
mDecelerationVelocity.x = 0;
|
||||
mDecelerationVelocity.y = 0;
|
||||
}
|
||||
|
||||
public void computeScroll() {
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.view.animation.AnimationUtils;
|
|||
|
||||
import com.github.mikephil.charting.charts.PieRadarChartBase;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -20,7 +21,7 @@ import java.util.ArrayList;
|
|||
*/
|
||||
public class PieRadarChartTouchListener extends ChartTouchListener<PieRadarChartBase<?>> {
|
||||
|
||||
private PointF mTouchStartPoint = new PointF();
|
||||
private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0);
|
||||
|
||||
/**
|
||||
* the angle where the dragging started
|
||||
|
|
|
@ -133,6 +133,9 @@ public abstract class AxisRenderer extends Renderer {
|
|||
min = (float) p1.y;
|
||||
max = (float) p2.y;
|
||||
}
|
||||
|
||||
PointD.recycleInstance(p1);
|
||||
PointD.recycleInstance(p2);
|
||||
}
|
||||
|
||||
computeAxisValues(min, max);
|
||||
|
|
|
@ -58,17 +58,17 @@ public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer {
|
|||
/**
|
||||
* minimum visible entry index
|
||||
*/
|
||||
public final int min;
|
||||
public int min;
|
||||
|
||||
/**
|
||||
* maximum visible entry index
|
||||
*/
|
||||
public final int max;
|
||||
public int max;
|
||||
|
||||
/**
|
||||
* range of visible entry indices
|
||||
*/
|
||||
public final int range;
|
||||
public int range;
|
||||
|
||||
/**
|
||||
* Calculates the minimum and maximum x values as well as the range between them.
|
||||
|
@ -77,7 +77,16 @@ public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer {
|
|||
* @param dataSet
|
||||
*/
|
||||
public XBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) {
|
||||
this.set(chart, dataSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the minimum and maximum x values as well as the range between them.
|
||||
*
|
||||
* @param chart
|
||||
* @param dataSet
|
||||
*/
|
||||
public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet){
|
||||
float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX()));
|
||||
|
||||
float low = chart.getLowestVisibleX();
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.github.mikephil.charting.data.BubbleData;
|
|||
import com.github.mikephil.charting.data.BubbleEntry;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
@ -46,7 +47,11 @@ public class BubbleChartRenderer extends BarLineScatterCandleBubbleRenderer {
|
|||
|
||||
BubbleData bubbleData = mChart.getBubbleData();
|
||||
|
||||
for (IBubbleDataSet set : bubbleData.getDataSets()) {
|
||||
IBubbleDataSet set;
|
||||
List<IBubbleDataSet> dataSets = bubbleData.getDataSets();
|
||||
int setCount = dataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = dataSets.get(i);
|
||||
|
||||
if (set.isVisible() && set.getEntryCount() > 0)
|
||||
drawDataSet(c, set);
|
||||
|
|
|
@ -44,7 +44,11 @@ public class CandleStickChartRenderer extends LineScatterCandleRadarRenderer {
|
|||
|
||||
CandleData candleData = mChart.getCandleData();
|
||||
|
||||
for (ICandleDataSet set : candleData.getDataSets()) {
|
||||
ICandleDataSet set;
|
||||
List<ICandleDataSet> dataSets = candleData.getDataSets();
|
||||
int setCount = dataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = dataSets.get(i);
|
||||
|
||||
if (set.isVisible() && set.getEntryCount() > 0)
|
||||
drawDataSet(c, set);
|
||||
|
|
|
@ -33,6 +33,7 @@ public class CombinedChartRenderer extends DataRenderer {
|
|||
createRenderers(chart, animator, viewPortHandler);
|
||||
}
|
||||
|
||||
protected ArrayList<DataRenderer> renderersForCreateRenderers = new ArrayList<>(4);
|
||||
/**
|
||||
* Creates the renderers needed for this combined-renderer in the required order. Also takes the DrawOrder into
|
||||
* consideration.
|
||||
|
@ -43,7 +44,8 @@ public class CombinedChartRenderer extends DataRenderer {
|
|||
*/
|
||||
protected void createRenderers(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
|
||||
|
||||
mRenderers = new ArrayList<DataRenderer>();
|
||||
mRenderers = renderersForCreateRenderers;
|
||||
mRenderers.clear();
|
||||
|
||||
DrawOrder[] orders = chart.getDrawOrder();
|
||||
|
||||
|
@ -102,6 +104,7 @@ public class CombinedChartRenderer extends DataRenderer {
|
|||
renderer.drawExtras(c);
|
||||
}
|
||||
|
||||
protected ArrayList<Highlight> highlightsforDrawHighlighted = new ArrayList<>();
|
||||
@Override
|
||||
public void drawHighlighted(Canvas c, Highlight[] indices) {
|
||||
|
||||
|
@ -125,8 +128,11 @@ public class CombinedChartRenderer extends DataRenderer {
|
|||
int dataIndex = data == null ? -1
|
||||
: ((CombinedData)chart.getData()).getAllData().indexOf(data);
|
||||
|
||||
ArrayList<Highlight> dataIndices = new ArrayList<>();
|
||||
for (Highlight h : indices) {
|
||||
ArrayList<Highlight> dataIndices = highlightsforDrawHighlighted;
|
||||
dataIndices.clear();
|
||||
Highlight h;
|
||||
for(int i = 0 ; i < dataIndices.size() ; i++){
|
||||
h = dataIndices.get(i);
|
||||
if (h.getDataIndex() == dataIndex || h.getDataIndex() == -1)
|
||||
dataIndices.add(h);
|
||||
}
|
||||
|
|
|
@ -70,6 +70,8 @@ public class LegendRenderer extends Renderer {
|
|||
return mLegendFormPaint;
|
||||
}
|
||||
|
||||
protected ArrayList<String> labelsForComputeLegend = new ArrayList<>(16);
|
||||
protected ArrayList<Integer> colorsForComputeLegend = new ArrayList<>(16);
|
||||
/**
|
||||
* Prepares the legend and calculates all needed forms, labels and colors.
|
||||
*
|
||||
|
@ -79,8 +81,11 @@ public class LegendRenderer extends Renderer {
|
|||
|
||||
if (!mLegend.isLegendCustom()) {
|
||||
|
||||
List<String> labels = new ArrayList<String>();
|
||||
List<Integer> colors = new ArrayList<Integer>();
|
||||
ArrayList<String> labels = labelsForComputeLegend;
|
||||
ArrayList<Integer> colors = colorsForComputeLegend;
|
||||
|
||||
labels.clear();
|
||||
colors.clear();
|
||||
|
||||
// loop for building up the colors and labels used in the legend
|
||||
for (int i = 0; i < data.getDataSetCount(); i++) {
|
||||
|
@ -177,6 +182,7 @@ public class LegendRenderer extends Renderer {
|
|||
mLegend.calculateDimensions(mLegendLabelPaint, mViewPortHandler);
|
||||
}
|
||||
|
||||
protected Paint.FontMetrics fontMetricsForRenderLegent = new Paint.FontMetrics();
|
||||
public void renderLegend(Canvas c) {
|
||||
|
||||
if (!mLegend.isEnabled())
|
||||
|
@ -190,8 +196,8 @@ public class LegendRenderer extends Renderer {
|
|||
mLegendLabelPaint.setTextSize(mLegend.getTextSize());
|
||||
mLegendLabelPaint.setColor(mLegend.getTextColor());
|
||||
|
||||
float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint);
|
||||
float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint) + mLegend.getYEntrySpace();
|
||||
float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint, fontMetricsForRenderLegent);
|
||||
float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, fontMetricsForRenderLegent) + mLegend.getYEntrySpace();
|
||||
float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f;
|
||||
|
||||
String[] labels = mLegend.getLabels();
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.github.mikephil.charting.data.LineData;
|
|||
import com.github.mikephil.charting.data.LineDataSet;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
|
||||
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
|
||||
import com.github.mikephil.charting.utils.ColorTemplate;
|
||||
import com.github.mikephil.charting.utils.PointD;
|
||||
|
@ -21,10 +22,42 @@ import com.github.mikephil.charting.utils.Transformer;
|
|||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class LineChartRenderer extends LineRadarRenderer {
|
||||
|
||||
private class DataSetImageCache {
|
||||
|
||||
|
||||
private Bitmap[] circleBitmaps;
|
||||
private int[] circleColors;
|
||||
|
||||
private void ensureCircleCache(int size){
|
||||
if(circleBitmaps == null){
|
||||
circleBitmaps = new Bitmap[size];
|
||||
}else if(circleBitmaps.length < size){
|
||||
Bitmap[] tmp = new Bitmap[size];
|
||||
for(int i = 0 ; i < circleBitmaps.length ; i++){
|
||||
tmp[i] = circleBitmaps[size];
|
||||
}
|
||||
circleBitmaps = tmp;
|
||||
}
|
||||
|
||||
if(circleColors == null){
|
||||
circleColors = new int[size];
|
||||
}else if(circleColors.length < size){
|
||||
int[] tmp = new int[size];
|
||||
for(int i = 0 ; i < circleColors.length ; i++){
|
||||
tmp[i] = circleColors[size];
|
||||
}
|
||||
circleColors = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected LineDataProvider mChart;
|
||||
|
||||
/**
|
||||
|
@ -89,7 +122,10 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
|
||||
LineData lineData = mChart.getLineData();
|
||||
|
||||
for (ILineDataSet set : lineData.getDataSets()) {
|
||||
ILineDataSet set;
|
||||
int setCount = lineData.getDataSets().size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = lineData.getDataSets().get(i);
|
||||
|
||||
if (set.isVisible() && set.getEntryCount() > 0)
|
||||
drawDataSet(c, set);
|
||||
|
@ -266,6 +302,7 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
}
|
||||
|
||||
private float[] mLineBuffer = new float[4];
|
||||
private XBounds xBoundsBuffer;
|
||||
|
||||
/**
|
||||
* Draws a normal line.
|
||||
|
@ -295,13 +332,18 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
canvas = c;
|
||||
}
|
||||
|
||||
XBounds bounds = getXBounds(mChart, dataSet);
|
||||
if(xBoundsBuffer == null){
|
||||
xBoundsBuffer = getXBounds(mChart, dataSet);
|
||||
}else{
|
||||
xBoundsBuffer.set(mChart, dataSet);
|
||||
}
|
||||
final XBounds bounds = xBoundsBuffer;
|
||||
|
||||
// more than 1 color
|
||||
if (dataSet.getColors().size() > 1) {
|
||||
|
||||
if (mLineBuffer.length != pointsPerEntryPair * 2)
|
||||
mLineBuffer = new float[pointsPerEntryPair * 2];
|
||||
if (mLineBuffer.length <= pointsPerEntryPair * 2)
|
||||
mLineBuffer = new float[pointsPerEntryPair * 4];
|
||||
|
||||
for (int j = bounds.min; j <= bounds.range + bounds.min; j++) {
|
||||
|
||||
|
@ -356,8 +398,8 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
|
||||
} else { // only one color per dataset
|
||||
|
||||
if (mLineBuffer.length != Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2)
|
||||
mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2];
|
||||
if (mLineBuffer.length < Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2)
|
||||
mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 4];
|
||||
|
||||
Entry e1, e2;
|
||||
|
||||
|
@ -407,6 +449,8 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
protected Path mGenerateFilledPathBuffer = new Path();
|
||||
|
||||
/**
|
||||
* Draws a filled linear path on the canvas.
|
||||
*
|
||||
|
@ -417,58 +461,92 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
*/
|
||||
protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, XBounds bounds) {
|
||||
|
||||
Path filled = generateFilledPath(dataSet, bounds);
|
||||
final Path filled = mGenerateFilledPathBuffer;
|
||||
|
||||
trans.pathValueToPixel(filled);
|
||||
final int startingIndex = bounds.min;
|
||||
final int endingIndex = bounds.range + bounds.min;
|
||||
final int indexInterval = 128;
|
||||
|
||||
final Drawable drawable = dataSet.getFillDrawable();
|
||||
if (drawable != null) {
|
||||
int currentStartIndex = 0;
|
||||
int currentEndIndex = indexInterval;
|
||||
int iterations = 0;
|
||||
|
||||
drawFilledPath(c, filled, drawable);
|
||||
} else {
|
||||
// Doing this iteratively in order to avoid OutOfMemory errors that can happen on large bounds sets.
|
||||
do{
|
||||
currentStartIndex = startingIndex + (iterations * indexInterval);
|
||||
currentEndIndex = currentStartIndex + indexInterval;
|
||||
currentEndIndex = currentEndIndex > endingIndex ? endingIndex : currentEndIndex;
|
||||
|
||||
if(currentStartIndex <= currentEndIndex) {
|
||||
generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled);
|
||||
|
||||
|
||||
|
||||
trans.pathValueToPixel(filled);
|
||||
|
||||
final Drawable drawable = dataSet.getFillDrawable();
|
||||
if (drawable != null) {
|
||||
|
||||
drawFilledPath(c, filled, drawable);
|
||||
} else {
|
||||
|
||||
drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
iterations++;
|
||||
|
||||
}while(currentStartIndex <= currentEndIndex);
|
||||
|
||||
drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the path that is used for filled drawing.
|
||||
* Generates a path that is used for filled drawing.
|
||||
*
|
||||
* @param dataSet The dataset from which to read the entries.
|
||||
* @param startIndex The index from which to start reading the dataset
|
||||
* @param endIndex The index from which to stop reading the dataset
|
||||
* @param outputPath The path object that will be assigned the chart data.
|
||||
*
|
||||
* @param dataSet
|
||||
* @return
|
||||
*/
|
||||
private Path generateFilledPath(ILineDataSet dataSet, XBounds bounds) {
|
||||
private void generateFilledPath(final ILineDataSet dataSet, final int startIndex, final int endIndex, final Path outputPath) {
|
||||
|
||||
float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart);
|
||||
float phaseY = mAnimator.getPhaseY();
|
||||
final float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart);
|
||||
final float phaseY = mAnimator.getPhaseY();
|
||||
final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED;
|
||||
|
||||
Path filled = new Path();
|
||||
Entry entry = dataSet.getEntryForIndex(bounds.min);
|
||||
final Path filled = outputPath;
|
||||
filled.reset();
|
||||
|
||||
final Entry entry = dataSet.getEntryForIndex(startIndex);
|
||||
|
||||
filled.moveTo(entry.getX(), fillMin);
|
||||
filled.lineTo(entry.getX(), entry.getY() * phaseY);
|
||||
|
||||
// create a new path
|
||||
for (int x = bounds.min + 1; x <= bounds.range + bounds.min; x++) {
|
||||
Entry currentEntry = null;
|
||||
Entry previousEntry = null;
|
||||
for (int x = startIndex + 1 ; x <= endIndex ; x++) {
|
||||
|
||||
Entry e = dataSet.getEntryForIndex(x);
|
||||
currentEntry = dataSet.getEntryForIndex(x);
|
||||
|
||||
if (isDrawSteppedEnabled) {
|
||||
final Entry ePrev = dataSet.getEntryForIndex(x - 1);
|
||||
if (ePrev == null) continue;
|
||||
|
||||
filled.lineTo(e.getX(), ePrev.getY() * phaseY);
|
||||
if (isDrawSteppedEnabled && previousEntry != null) {
|
||||
filled.lineTo(currentEntry.getX(), previousEntry.getY() * phaseY);
|
||||
}
|
||||
|
||||
filled.lineTo(e.getX(), e.getY() * phaseY);
|
||||
filled.lineTo(currentEntry.getX(), currentEntry.getY() * phaseY);
|
||||
|
||||
previousEntry = currentEntry;
|
||||
}
|
||||
|
||||
// close up
|
||||
filled.lineTo(dataSet.getEntryForIndex(bounds.range + bounds.min).getX(), fillMin);
|
||||
if(currentEntry != null) {
|
||||
filled.lineTo(currentEntry.getX(), fillMin);
|
||||
}
|
||||
|
||||
filled.close();
|
||||
|
||||
return filled;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -527,21 +605,35 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
}
|
||||
|
||||
private Path mCirclePathBuffer = new Path();
|
||||
|
||||
private float[] mCirclesBuffer = new float[2];
|
||||
private HashMap<IDataSet, DataSetImageCache> mImageCaches = new HashMap<>();
|
||||
private XBounds mXBoundsBuffer;
|
||||
protected void drawCircles(Canvas c) {
|
||||
|
||||
mRenderPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
float phaseY = mAnimator.getPhaseY();
|
||||
float[] circlesBuffer = mCirclesBuffer;
|
||||
|
||||
float[] circlesBuffer = new float[2];
|
||||
circlesBuffer[0] = 0;
|
||||
circlesBuffer[1] = 0;
|
||||
|
||||
List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();
|
||||
|
||||
for (int i = 0; i < dataSets.size(); i++) {
|
||||
final int dataSetCount = dataSets.size();
|
||||
for (int i = 0; i < dataSetCount ; i++) {
|
||||
|
||||
ILineDataSet dataSet = dataSets.get(i);
|
||||
|
||||
DataSetImageCache imageCache;
|
||||
|
||||
if(mImageCaches.containsKey(dataSet)){
|
||||
imageCache = mImageCaches.get(dataSet);
|
||||
}else{
|
||||
imageCache = new DataSetImageCache();
|
||||
mImageCaches.put(dataSet, imageCache);
|
||||
}
|
||||
imageCache.ensureCircleCache(dataSet.getCircleColorCount());
|
||||
|
||||
if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() ||
|
||||
dataSet.getEntryCount() == 0)
|
||||
continue;
|
||||
|
@ -550,7 +642,13 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
|
||||
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
|
||||
|
||||
XBounds bounds = getXBounds(mChart, dataSet);
|
||||
if(mXBoundsBuffer == null) {
|
||||
mXBoundsBuffer = getXBounds(mChart, dataSet);
|
||||
}else{
|
||||
mXBoundsBuffer.set(mChart,dataSet);
|
||||
}
|
||||
|
||||
XBounds bounds = mXBoundsBuffer;
|
||||
|
||||
float circleRadius = dataSet.getCircleRadius();
|
||||
float circleHoleRadius = dataSet.getCircleHoleRadius();
|
||||
|
@ -560,8 +658,8 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
boolean drawTransparentCircleHole = drawCircleHole &&
|
||||
dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE;
|
||||
|
||||
for (int j = bounds.min; j <= bounds.range + bounds.min; j++) {
|
||||
|
||||
int boundsRangeCount = bounds.range + bounds.min;
|
||||
for (int j = bounds.min; j <= boundsRangeCount; j++) {
|
||||
Entry e = dataSet.getEntryForIndex(j);
|
||||
|
||||
if (e == null) break;
|
||||
|
@ -580,37 +678,74 @@ public class LineChartRenderer extends LineRadarRenderer {
|
|||
!mViewPortHandler.isInBoundsY(circlesBuffer[1]))
|
||||
continue;
|
||||
|
||||
mRenderPaint.setColor(dataSet.getCircleColor(j));
|
||||
final int circleColor = dataSet.getCircleColor(j);
|
||||
mRenderPaint.setColor(circleColor);
|
||||
|
||||
if (drawTransparentCircleHole) {
|
||||
Bitmap circleBitmap = null;
|
||||
|
||||
// Begin path for circle with hole
|
||||
mCirclePathBuffer.reset();
|
||||
|
||||
mCirclePathBuffer.addCircle(circlesBuffer[0], circlesBuffer[1],
|
||||
circleRadius,
|
||||
Path.Direction.CW);
|
||||
|
||||
// Cut hole in path
|
||||
mCirclePathBuffer.addCircle(circlesBuffer[0], circlesBuffer[1],
|
||||
circleHoleRadius,
|
||||
Path.Direction.CCW);
|
||||
|
||||
// Fill in-between
|
||||
c.drawPath(mCirclePathBuffer, mRenderPaint);
|
||||
|
||||
} else {
|
||||
|
||||
c.drawCircle(circlesBuffer[0], circlesBuffer[1],
|
||||
circleRadius,
|
||||
mRenderPaint);
|
||||
|
||||
if (drawCircleHole) {
|
||||
c.drawCircle(circlesBuffer[0], circlesBuffer[1],
|
||||
circleHoleRadius,
|
||||
mCirclePaintInner);
|
||||
final int dataSetColorCount = imageCache.circleColors.length;
|
||||
int colorIndex;
|
||||
for(colorIndex = 0 ; colorIndex < dataSetColorCount ; colorIndex++) {
|
||||
int tempColor = imageCache.circleColors[colorIndex];
|
||||
Bitmap tempBitmap = imageCache.circleBitmaps[colorIndex];
|
||||
if(tempColor == circleColor) {
|
||||
circleBitmap = tempBitmap;
|
||||
break;
|
||||
}else if(tempBitmap == null){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(circleBitmap == null){
|
||||
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
|
||||
circleBitmap = Bitmap.createBitmap((int)(circleRadius * 2.1), (int)(circleRadius * 2.1), conf);
|
||||
Canvas canvas = new Canvas(circleBitmap);
|
||||
imageCache.circleBitmaps[colorIndex] = circleBitmap;
|
||||
imageCache.circleColors[colorIndex] = circleColor;
|
||||
|
||||
if(drawTransparentCircleHole){
|
||||
// Begin path for circle with hole
|
||||
mCirclePathBuffer.reset();
|
||||
|
||||
mCirclePathBuffer.addCircle(
|
||||
circleRadius,
|
||||
circleRadius,
|
||||
circleRadius,
|
||||
Path.Direction.CW);
|
||||
|
||||
// Cut hole in path
|
||||
mCirclePathBuffer.addCircle(
|
||||
circleRadius,
|
||||
circleRadius,
|
||||
circleHoleRadius,
|
||||
Path.Direction.CCW);
|
||||
|
||||
// Fill in-between
|
||||
canvas.drawPath(mCirclePathBuffer, mRenderPaint);
|
||||
}else{
|
||||
|
||||
canvas.drawCircle(
|
||||
circleRadius,
|
||||
circleRadius,
|
||||
circleRadius,
|
||||
mRenderPaint);
|
||||
|
||||
if (drawCircleHole) {
|
||||
canvas.drawCircle(
|
||||
circleRadius,
|
||||
circleRadius,
|
||||
circleHoleRadius,
|
||||
mCirclePaintInner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(circleBitmap != null){
|
||||
|
||||
c.drawBitmap(circleBitmap, circlesBuffer[0] - circleRadius, circlesBuffer[1] - circleRadius, mRenderPaint);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.github.mikephil.charting.formatter.ValueFormatter;
|
|||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
|
||||
import com.github.mikephil.charting.utils.ColorTemplate;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -141,8 +142,11 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
PieData pieData = mChart.getData();
|
||||
|
||||
for (IPieDataSet set : pieData.getDataSets()) {
|
||||
|
||||
IPieDataSet set;
|
||||
int setCount = pieData.getDataSets().size();
|
||||
List<IPieDataSet> dataSet = pieData.getDataSets();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = dataSet.get(i);
|
||||
if (set.isVisible() && set.getEntryCount() > 0)
|
||||
drawDataSet(c, set);
|
||||
}
|
||||
|
@ -152,7 +156,7 @@ public class PieChartRenderer extends DataRenderer {
|
|||
private RectF mInnerRectBuffer = new RectF();
|
||||
|
||||
protected float calculateMinimumRadiusForSpacedSlice(
|
||||
PointF center,
|
||||
MPPointF center,
|
||||
float radius,
|
||||
float angle,
|
||||
float arcStartPointX,
|
||||
|
@ -219,7 +223,7 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
final int entryCount = dataSet.getEntryCount();
|
||||
final float[] drawAngles = mChart.getDrawAngles();
|
||||
final PointF center = mChart.getCenterCircleBox();
|
||||
final MPPointF center = mChart.getCenterCircleBox();
|
||||
final float radius = mChart.getRadius();
|
||||
final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled();
|
||||
final float userInnerRadius = drawInnerArc
|
||||
|
@ -375,12 +379,14 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
angle += sliceAngle * phaseX;
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawValues(Canvas c) {
|
||||
|
||||
PointF center = mChart.getCenterCircleBox();
|
||||
MPPointF center = mChart.getCenterCircleBox();
|
||||
|
||||
// get whole the radius
|
||||
float radius = mChart.getRadius();
|
||||
|
@ -579,7 +585,7 @@ public class PieChartRenderer extends DataRenderer {
|
|||
xIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
c.restore();
|
||||
}
|
||||
|
||||
|
@ -615,7 +621,7 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
float radius = mChart.getRadius();
|
||||
float holeRadius = radius * (mChart.getHoleRadius() / 100);
|
||||
PointF center = mChart.getCenterCircleBox();
|
||||
MPPointF center = mChart.getCenterCircleBox();
|
||||
|
||||
if (Color.alpha(mHolePaint.getColor()) > 0) {
|
||||
// draw the hole-circle
|
||||
|
@ -642,9 +648,11 @@ public class PieChartRenderer extends DataRenderer {
|
|||
// reset alpha
|
||||
mTransparentCirclePaint.setAlpha(alpha);
|
||||
}
|
||||
MPPointF.recycleInstance(center);
|
||||
}
|
||||
}
|
||||
|
||||
protected Path mDrawCenterTextPathBuffer = new Path();
|
||||
/**
|
||||
* draws the description text in the center of the pie chart makes most
|
||||
* sense when center-hole is enabled
|
||||
|
@ -655,8 +663,8 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
if (mChart.isDrawCenterTextEnabled() && centerText != null) {
|
||||
|
||||
PointF center = mChart.getCenterCircleBox();
|
||||
PointF offset = mChart.getCenterTextOffset();
|
||||
MPPointF center = mChart.getCenterCircleBox();
|
||||
MPPointF offset = mChart.getCenterTextOffset();
|
||||
|
||||
float x = center.x + offset.x;
|
||||
float y = center.y + offset.y;
|
||||
|
@ -701,7 +709,8 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
c.save();
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
Path path = new Path();
|
||||
Path path = mDrawCenterTextPathBuffer;
|
||||
path.reset();
|
||||
path.addOval(holeRect, Path.Direction.CW);
|
||||
c.clipPath(path);
|
||||
}
|
||||
|
@ -710,9 +719,13 @@ public class PieChartRenderer extends DataRenderer {
|
|||
mCenterTextLayout.draw(c);
|
||||
|
||||
c.restore();
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
MPPointF.recycleInstance(offset);
|
||||
}
|
||||
}
|
||||
|
||||
protected RectF mDrawHighlightedRectF = new RectF();
|
||||
@Override
|
||||
public void drawHighlighted(Canvas c, Highlight[] indices) {
|
||||
|
||||
|
@ -724,14 +737,15 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
float[] drawAngles = mChart.getDrawAngles();
|
||||
float[] absoluteAngles = mChart.getAbsoluteAngles();
|
||||
final PointF center = mChart.getCenterCircleBox();
|
||||
final MPPointF center = mChart.getCenterCircleBox();
|
||||
final float radius = mChart.getRadius();
|
||||
final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled();
|
||||
final float userInnerRadius = drawInnerArc
|
||||
? radius * (mChart.getHoleRadius() / 100.f)
|
||||
: 0.f;
|
||||
|
||||
final RectF highlightedCircleBox = new RectF();
|
||||
final RectF highlightedCircleBox = mDrawHighlightedRectF;
|
||||
highlightedCircleBox.set(0,0,0,0);
|
||||
|
||||
for (int i = 0; i < indices.length; i++) {
|
||||
|
||||
|
@ -901,6 +915,8 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint);
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -921,7 +937,7 @@ public class PieChartRenderer extends DataRenderer {
|
|||
float phaseX = mAnimator.getPhaseX();
|
||||
float phaseY = mAnimator.getPhaseY();
|
||||
|
||||
PointF center = mChart.getCenterCircleBox();
|
||||
MPPointF center = mChart.getCenterCircleBox();
|
||||
float r = mChart.getRadius();
|
||||
|
||||
// calculate the radius of the "slice-circle"
|
||||
|
@ -952,6 +968,7 @@ public class PieChartRenderer extends DataRenderer {
|
|||
|
||||
angle += sliceAngle * phaseX;
|
||||
}
|
||||
MPPointF.recycleInstance(center);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,9 +15,12 @@ import com.github.mikephil.charting.data.RadarEntry;
|
|||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet;
|
||||
import com.github.mikephil.charting.utils.ColorTemplate;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RadarChartRenderer extends LineRadarRenderer {
|
||||
|
||||
protected RadarChart mChart;
|
||||
|
@ -61,7 +64,11 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
|
||||
int mostEntries = radarData.getMaxEntryCountSet().getEntryCount();
|
||||
|
||||
for (IRadarDataSet set : radarData.getDataSets()) {
|
||||
IRadarDataSet set;
|
||||
List<IRadarDataSet> dataSets = radarData.getDataSets();
|
||||
int setCount = dataSets.size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = dataSets.get(i);
|
||||
|
||||
if (set.isVisible() && set.getEntryCount() > 0) {
|
||||
drawDataSet(c, set, mostEntries);
|
||||
|
@ -69,6 +76,7 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
protected Path mDrawDataSetSurfacePathBuffer = new Path();
|
||||
/**
|
||||
* Draws the RadarDataSet
|
||||
*
|
||||
|
@ -87,9 +95,10 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
// pixels
|
||||
float factor = mChart.getFactor();
|
||||
|
||||
PointF center = mChart.getCenterOffsets();
|
||||
|
||||
Path surface = new Path();
|
||||
MPPointF center = mChart.getCenterOffsets();
|
||||
MPPointF pOut = MPPointF.getInstance(0,0);
|
||||
Path surface = mDrawDataSetSurfacePathBuffer;
|
||||
surface.reset();
|
||||
|
||||
boolean hasMovedToPoint = false;
|
||||
|
||||
|
@ -99,19 +108,19 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
|
||||
RadarEntry e = dataSet.getEntryForIndex(j);
|
||||
|
||||
PointF p = Utils.getPosition(
|
||||
Utils.getPosition(
|
||||
center,
|
||||
(e.getY() - mChart.getYChartMin()) * factor * phaseY,
|
||||
sliceangle * j * phaseX + mChart.getRotationAngle());
|
||||
sliceangle * j * phaseX + mChart.getRotationAngle(), pOut);
|
||||
|
||||
if (Float.isNaN(p.x))
|
||||
if (Float.isNaN(pOut.x))
|
||||
continue;
|
||||
|
||||
if (!hasMovedToPoint) {
|
||||
surface.moveTo(p.x, p.y);
|
||||
surface.moveTo(pOut.x, pOut.y);
|
||||
hasMovedToPoint = true;
|
||||
} else
|
||||
surface.lineTo(p.x, p.y);
|
||||
surface.lineTo(pOut.x, pOut.y);
|
||||
}
|
||||
|
||||
if (dataSet.getEntryCount() > mostEntries) {
|
||||
|
@ -139,6 +148,9 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
// draw the line (only if filled is disabled or alpha is below 255)
|
||||
if (!dataSet.isDrawFilledEnabled() || dataSet.getFillAlpha() < 255)
|
||||
c.drawPath(surface, mRenderPaint);
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
MPPointF.recycleInstance(pOut);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,7 +165,8 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
// pixels
|
||||
float factor = mChart.getFactor();
|
||||
|
||||
PointF center = mChart.getCenterOffsets();
|
||||
MPPointF center = mChart.getCenterOffsets();
|
||||
MPPointF pOut = MPPointF.getInstance(0,0);
|
||||
|
||||
float yoffset = Utils.convertDpToPixel(5f);
|
||||
|
||||
|
@ -171,15 +184,19 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
|
||||
RadarEntry entry = dataSet.getEntryForIndex(j);
|
||||
|
||||
PointF p = Utils.getPosition(
|
||||
center,
|
||||
(entry.getY() - mChart.getYChartMin()) * factor * phaseY,
|
||||
sliceangle * j * phaseX + mChart.getRotationAngle());
|
||||
Utils.getPosition(
|
||||
center,
|
||||
(entry.getY() - mChart.getYChartMin()) * factor * phaseY,
|
||||
sliceangle * j * phaseX + mChart.getRotationAngle(),
|
||||
pOut);
|
||||
|
||||
drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, p.x, p.y - yoffset, dataSet.getValueTextColor
|
||||
drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, pOut.x, pOut.y - yoffset, dataSet.getValueTextColor
|
||||
(j));
|
||||
}
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
MPPointF.recycleInstance(pOut);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -196,7 +213,7 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
float factor = mChart.getFactor();
|
||||
float rotationangle = mChart.getRotationAngle();
|
||||
|
||||
PointF center = mChart.getCenterOffsets();
|
||||
MPPointF center = mChart.getCenterOffsets();
|
||||
|
||||
// draw the web lines that come from the center
|
||||
mWebPaint.setStrokeWidth(mChart.getWebLineWidth());
|
||||
|
@ -206,15 +223,18 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
final int xIncrements = 1 + mChart.getSkipWebLineCount();
|
||||
int maxEntryCount = mChart.getData().getMaxEntryCountSet().getEntryCount();
|
||||
|
||||
MPPointF p = MPPointF.getInstance(0,0);
|
||||
for (int i = 0; i < maxEntryCount; i += xIncrements) {
|
||||
|
||||
PointF p = Utils.getPosition(
|
||||
Utils.getPosition(
|
||||
center,
|
||||
mChart.getYRange() * factor,
|
||||
sliceangle * i + rotationangle);
|
||||
sliceangle * i + rotationangle,
|
||||
p);
|
||||
|
||||
c.drawLine(center.x, center.y, p.x, p.y, mWebPaint);
|
||||
}
|
||||
MPPointF.recycleInstance(p);
|
||||
|
||||
// draw the inner-web
|
||||
mWebPaint.setStrokeWidth(mChart.getWebLineWidthInner());
|
||||
|
@ -223,18 +243,24 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
|
||||
int labelCount = mChart.getYAxis().mEntryCount;
|
||||
|
||||
MPPointF p1out = MPPointF.getInstance(0,0);
|
||||
MPPointF p2out = MPPointF.getInstance(0,0);
|
||||
for (int j = 0; j < labelCount; j++) {
|
||||
|
||||
for (int i = 0; i < mChart.getData().getEntryCount(); i++) {
|
||||
|
||||
float r = (mChart.getYAxis().mEntries[j] - mChart.getYChartMin()) * factor;
|
||||
|
||||
PointF p1 = Utils.getPosition(center, r, sliceangle * i + rotationangle);
|
||||
PointF p2 = Utils.getPosition(center, r, sliceangle * (i + 1) + rotationangle);
|
||||
Utils.getPosition(center, r, sliceangle * i + rotationangle, p1out);
|
||||
Utils.getPosition(center, r, sliceangle * (i + 1) + rotationangle, p2out);
|
||||
|
||||
c.drawLine(p1out.x, p1out.y, p2out.x, p2out.y, mWebPaint);
|
||||
|
||||
|
||||
c.drawLine(p1.x, p1.y, p2.x, p2.y, mWebPaint);
|
||||
}
|
||||
}
|
||||
MPPointF.recycleInstance(p1out);
|
||||
MPPointF.recycleInstance(p2out);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -246,7 +272,8 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
// pixels
|
||||
float factor = mChart.getFactor();
|
||||
|
||||
PointF center = mChart.getCenterOffsets();
|
||||
MPPointF center = mChart.getCenterOffsets();
|
||||
MPPointF pOut = MPPointF.getInstance(0,0);
|
||||
|
||||
RadarData radarData = mChart.getData();
|
||||
|
||||
|
@ -264,17 +291,19 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
|
||||
float y = (e.getY() - mChart.getYChartMin());
|
||||
|
||||
PointF p = Utils.getPosition(center, y * factor * mAnimator.getPhaseY(),
|
||||
sliceangle * high.getX() * mAnimator.getPhaseX() + mChart.getRotationAngle());
|
||||
Utils.getPosition(center,
|
||||
y * factor * mAnimator.getPhaseY(),
|
||||
sliceangle * high.getX() * mAnimator.getPhaseX() + mChart.getRotationAngle(),
|
||||
pOut);
|
||||
|
||||
high.setDraw(p.x, p.y);
|
||||
high.setDraw(pOut.x, pOut.y);
|
||||
|
||||
// draw the lines
|
||||
drawHighlightLines(c, p.x, p.y, set);
|
||||
drawHighlightLines(c, pOut.x, pOut.y, set);
|
||||
|
||||
if (set.isDrawHighlightCircleEnabled()) {
|
||||
|
||||
if (!Float.isNaN(p.x) && !Float.isNaN(p.y)) {
|
||||
if (!Float.isNaN(pOut.x) && !Float.isNaN(pOut.y)) {
|
||||
|
||||
int strokeColor = set.getHighlightCircleStrokeColor();
|
||||
if (strokeColor == ColorTemplate.COLOR_NONE) {
|
||||
|
@ -286,7 +315,7 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
}
|
||||
|
||||
drawHighlightCircle(c,
|
||||
p,
|
||||
pOut,
|
||||
set.getHighlightCircleInnerRadius(),
|
||||
set.getHighlightCircleOuterRadius(),
|
||||
set.getHighlightCircleFillColor(),
|
||||
|
@ -295,10 +324,14 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
MPPointF.recycleInstance(pOut);
|
||||
}
|
||||
|
||||
protected Path mDrawHighlightCirclePathBuffer = new Path();
|
||||
public void drawHighlightCircle(Canvas c,
|
||||
PointF point,
|
||||
MPPointF point,
|
||||
float innerRadius,
|
||||
float outerRadius,
|
||||
int fillColor,
|
||||
|
@ -310,7 +343,8 @@ public class RadarChartRenderer extends LineRadarRenderer {
|
|||
innerRadius = Utils.convertDpToPixel(innerRadius);
|
||||
|
||||
if (fillColor != ColorTemplate.COLOR_NONE) {
|
||||
Path p = new Path();
|
||||
Path p = mDrawHighlightCirclePathBuffer;
|
||||
p.reset();
|
||||
p.addCircle(point.x, point.y, outerRadius, Path.Direction.CW);
|
||||
if (innerRadius > 0.f) {
|
||||
p.addCircle(point.x, point.y, innerRadius, Path.Direction.CCW);
|
||||
|
|
|
@ -47,7 +47,10 @@ public class ScatterChartRenderer extends LineScatterCandleRadarRenderer {
|
|||
|
||||
ScatterData scatterData = mChart.getScatterData();
|
||||
|
||||
for (IScatterDataSet set : scatterData.getDataSets()) {
|
||||
IScatterDataSet set;
|
||||
int setCount = scatterData.getDataSets().size();
|
||||
for(int i = 0 ; i < setCount ; i++){
|
||||
set = scatterData.getDataSets().get(i);
|
||||
|
||||
if (set.isVisible())
|
||||
drawDataSet(c, set);
|
||||
|
|
|
@ -6,12 +6,12 @@ import android.graphics.Color;
|
|||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PointF;
|
||||
|
||||
import com.github.mikephil.charting.components.LimitLine;
|
||||
import com.github.mikephil.charting.components.XAxis;
|
||||
import com.github.mikephil.charting.components.XAxis.XAxisPosition;
|
||||
import com.github.mikephil.charting.utils.FSize;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.PointD;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
@ -58,6 +58,9 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
min = (float) p1.x;
|
||||
max = (float) p2.x;
|
||||
}
|
||||
|
||||
PointD.recycleInstance(p1);
|
||||
PointD.recycleInstance(p2);
|
||||
}
|
||||
|
||||
computeAxisValues(min, max);
|
||||
|
@ -92,6 +95,9 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
mXAxis.mLabelHeight = Math.round(labelHeight);
|
||||
mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width);
|
||||
mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height);
|
||||
|
||||
FSize.recycleInstance(labelRotatedSize);
|
||||
FSize.recycleInstance(labelSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,33 +112,36 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
|
||||
mAxisLabelPaint.setColor(mXAxis.getTextColor());
|
||||
|
||||
MPPointF pointF = MPPointF.getInstance(0,0);
|
||||
if (mXAxis.getPosition() == XAxisPosition.TOP) {
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentTop() - yoffset,
|
||||
new PointF(0.5f, 0.9f));
|
||||
pointF.x = 0.5f;
|
||||
pointF.y = 0.9f;
|
||||
drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF);
|
||||
|
||||
} else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) {
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight,
|
||||
new PointF(0.5f, 1.0f));
|
||||
pointF.x = 0.5f;
|
||||
pointF.y = 1.0f;
|
||||
drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight, pointF);
|
||||
|
||||
} else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) {
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentBottom() + yoffset,
|
||||
new PointF(0.5f, 0.0f));
|
||||
pointF.x = 0.5f;
|
||||
pointF.y = 0.0f;
|
||||
drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF);
|
||||
|
||||
} else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) {
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight,
|
||||
new PointF(0.5f, 0.0f));
|
||||
pointF.x = 0.5f;
|
||||
pointF.y = 0.0f;
|
||||
drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight, pointF);
|
||||
|
||||
} else { // BOTH SIDED
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentTop() - yoffset,
|
||||
new PointF(0.5f, 1.0f));
|
||||
drawLabels(c, mViewPortHandler.contentBottom() + yoffset,
|
||||
new PointF(0.5f, 0.0f));
|
||||
pointF.x = 0.5f;
|
||||
pointF.y = 1.0f;
|
||||
drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF);
|
||||
pointF.x = 0.5f;
|
||||
pointF.y = 0.0f;
|
||||
drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF);
|
||||
}
|
||||
MPPointF.recycleInstance(pointF);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -161,19 +170,23 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
protected float[] mDrawLabelsBuffer = new float[2];
|
||||
/**
|
||||
* draws the x-labels on the specified y-position
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
protected void drawLabels(Canvas c, float pos, PointF anchor) {
|
||||
protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
|
||||
|
||||
final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
|
||||
boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled();
|
||||
|
||||
float[] positions = new float[mXAxis.mEntryCount * 2];
|
||||
if(mDrawLabelsBuffer.length < mAxis.mEntryCount * 2){
|
||||
mDrawLabelsBuffer = new float[mXAxis.mEntryCount * 2];
|
||||
}
|
||||
float[] positions = mDrawLabelsBuffer;
|
||||
|
||||
for (int i = 0; i < positions.length; i += 2) {
|
||||
for (int i = 0; i < positions.length && i < mXAxis.mEntries.length / 2 && i < mXAxis.mCenteredEntries.length / 2 ; i += 2) {
|
||||
|
||||
// only fill x values
|
||||
if (centeringEnabled) {
|
||||
|
@ -181,11 +194,13 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
} else {
|
||||
positions[i] = mXAxis.mEntries[i / 2];
|
||||
}
|
||||
// init to 0
|
||||
positions[i+1] = 0;
|
||||
}
|
||||
|
||||
mTrans.pointValuesToPixel(positions);
|
||||
|
||||
for (int i = 0; i < positions.length; i += 2) {
|
||||
for (int i = 0; i < positions.length && i < mXAxis.mEntries.length / 2 && i < mXAxis.mCenteredEntries.length / 2 ; i += 2) {
|
||||
|
||||
float x = positions[i];
|
||||
|
||||
|
@ -216,17 +231,21 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
protected void drawLabel(Canvas c, String formattedLabel, float x, float y, PointF anchor, float angleDegrees) {
|
||||
protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) {
|
||||
Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees);
|
||||
}
|
||||
|
||||
protected Path mRenderGridLinesPath = new Path();
|
||||
protected float[] mRenderGridLinesBuffer = new float[2];
|
||||
@Override
|
||||
public void renderGridLines(Canvas c) {
|
||||
|
||||
if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled())
|
||||
return;
|
||||
|
||||
float[] positions = new float[mXAxis.mEntryCount * 2];
|
||||
if(mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2){
|
||||
mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2];
|
||||
}
|
||||
float[] positions = mRenderGridLinesBuffer;
|
||||
|
||||
for (int i = 0; i < positions.length; i += 2) {
|
||||
positions[i] = mXAxis.mEntries[i / 2];
|
||||
|
@ -237,7 +256,8 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
|
||||
setupGridPaint();
|
||||
|
||||
Path gridLinePath = new Path();
|
||||
Path gridLinePath = mRenderGridLinesPath;
|
||||
gridLinePath.reset();
|
||||
|
||||
for (int i = 0; i < positions.length; i += 2) {
|
||||
|
||||
|
@ -264,6 +284,7 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
gridLinePath.reset();
|
||||
}
|
||||
|
||||
protected float[] mRenderLimitLinesBuffer = new float[2];
|
||||
/**
|
||||
* Draws the LimitLines associated with this axis to the screen.
|
||||
*
|
||||
|
@ -277,7 +298,9 @@ public class XAxisRenderer extends AxisRenderer {
|
|||
if (limitLines == null || limitLines.size() <= 0)
|
||||
return;
|
||||
|
||||
float[] position = new float[2];
|
||||
float[] position = mRenderLimitLinesBuffer;
|
||||
position[0] = 0;
|
||||
position[1] = 0;
|
||||
|
||||
for (int i = 0; i < limitLines.size(); i++) {
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@ import android.graphics.Canvas;
|
|||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PointF;
|
||||
|
||||
import com.github.mikephil.charting.charts.BarChart;
|
||||
import com.github.mikephil.charting.components.LimitLine;
|
||||
import com.github.mikephil.charting.components.XAxis;
|
||||
import com.github.mikephil.charting.components.XAxis.XAxisPosition;
|
||||
import com.github.mikephil.charting.utils.FSize;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.PointD;
|
||||
import com.github.mikephil.charting.utils.Transformer;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
|
@ -49,6 +49,9 @@ public class XAxisRendererHorizontalBarChart extends XAxisRenderer {
|
|||
min = (float) p1.y;
|
||||
max = (float) p2.y;
|
||||
}
|
||||
|
||||
PointD.recycleInstance(p1);
|
||||
PointD.recycleInstance(p2);
|
||||
}
|
||||
|
||||
computeAxisValues(min, max);
|
||||
|
@ -76,6 +79,8 @@ public class XAxisRendererHorizontalBarChart extends XAxisRenderer {
|
|||
mXAxis.mLabelHeight = Math.round(labelHeight);
|
||||
mXAxis.mLabelRotatedWidth = (int)(labelRotatedSize.width + mXAxis.getXOffset() * 3.5f);
|
||||
mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height);
|
||||
|
||||
FSize.recycleInstance(labelRotatedSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,42 +95,51 @@ public class XAxisRendererHorizontalBarChart extends XAxisRenderer {
|
|||
mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
|
||||
mAxisLabelPaint.setColor(mXAxis.getTextColor());
|
||||
|
||||
if (mXAxis.getPosition() == XAxisPosition.TOP) {
|
||||
MPPointF pointF = MPPointF.getInstance(0,0);
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentRight() + xoffset,
|
||||
new PointF(0.0f, 0.5f));
|
||||
if (mXAxis.getPosition() == XAxisPosition.TOP) {
|
||||
pointF.x = 0.0f;
|
||||
pointF.y = 0.5f;
|
||||
drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF);
|
||||
|
||||
} else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) {
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentRight() - xoffset,
|
||||
new PointF(1.0f, 0.5f));
|
||||
pointF.x = 1.0f;
|
||||
pointF.y = 0.5f;
|
||||
drawLabels(c, mViewPortHandler.contentRight() - xoffset, pointF);
|
||||
|
||||
} else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) {
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentLeft() - xoffset,
|
||||
new PointF(1.0f, 0.5f));
|
||||
pointF.x = 1.0f;
|
||||
pointF.y = 0.5f;
|
||||
drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF);
|
||||
|
||||
} else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) {
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentLeft() + xoffset,
|
||||
new PointF(0.0f, 0.5f));
|
||||
pointF.x = 1.0f;
|
||||
pointF.y = 0.5f;
|
||||
drawLabels(c, mViewPortHandler.contentLeft() + xoffset, pointF);
|
||||
|
||||
} else { // BOTH SIDED
|
||||
|
||||
drawLabels(c, mViewPortHandler.contentRight() + xoffset,
|
||||
new PointF(0.0f, 0.5f));
|
||||
drawLabels(c, mViewPortHandler.contentLeft() - xoffset,
|
||||
new PointF(1.0f, 0.5f));
|
||||
pointF.x = 0.0f;
|
||||
pointF.y = 0.5f;
|
||||
drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF);
|
||||
pointF.x = 1.0f;
|
||||
pointF.y = 0.5f;
|
||||
drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF);
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(pointF);
|
||||
}
|
||||
|
||||
protected float[] mDrawLabelsBuffer = new float[2];
|
||||
@Override
|
||||
protected void drawLabels(Canvas c, float pos, PointF anchor) {
|
||||
protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
|
||||
|
||||
final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
|
||||
boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled();
|
||||
|
||||
float[] positions = new float[mXAxis.mEntryCount * 2];
|
||||
if(mDrawLabelsBuffer.length != mAxis.mEntryCount * 2){
|
||||
mDrawLabelsBuffer = new float[mXAxis.mEntryCount * 2];
|
||||
}
|
||||
float[] positions = mDrawLabelsBuffer;
|
||||
|
||||
for (int i = 0; i < positions.length; i += 2) {
|
||||
|
||||
|
@ -189,7 +203,8 @@ public class XAxisRendererHorizontalBarChart extends XAxisRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
protected Path mRenderLimitLinesPathBuffer = new Path();
|
||||
/**
|
||||
* Draws the LimitLines associated with this axis to the screen.
|
||||
* This is the standard YAxis renderer using the XAxis limit lines.
|
||||
*
|
||||
|
@ -203,8 +218,12 @@ public class XAxisRendererHorizontalBarChart extends XAxisRenderer {
|
|||
if (limitLines == null || limitLines.size() <= 0)
|
||||
return;
|
||||
|
||||
float[] pts = new float[2];
|
||||
Path limitLinePath = new Path();
|
||||
float[] pts = mRenderLimitLinesBuffer;
|
||||
pts[0] = 0;
|
||||
pts[1] = 0;
|
||||
|
||||
Path limitLinePath = mRenderLimitLinesPathBuffer;
|
||||
limitLinePath.reset();
|
||||
|
||||
for (int i = 0; i < limitLines.size(); i++) {
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.graphics.PointF;
|
|||
|
||||
import com.github.mikephil.charting.charts.RadarChart;
|
||||
import com.github.mikephil.charting.components.XAxis;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -26,7 +27,7 @@ public class XAxisRendererRadarChart extends XAxisRenderer {
|
|||
return;
|
||||
|
||||
final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
|
||||
final PointF drawLabelAnchor = new PointF(0.5f, 0.25f);
|
||||
final MPPointF drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f);
|
||||
|
||||
mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
|
||||
mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
|
||||
|
@ -38,20 +39,24 @@ public class XAxisRendererRadarChart extends XAxisRenderer {
|
|||
// pixels
|
||||
float factor = mChart.getFactor();
|
||||
|
||||
PointF center = mChart.getCenterOffsets();
|
||||
|
||||
MPPointF center = mChart.getCenterOffsets();
|
||||
MPPointF pOut = MPPointF.getInstance(0,0);
|
||||
for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) {
|
||||
|
||||
String label = mXAxis.getValueFormatter().getFormattedValue(i, mXAxis);
|
||||
|
||||
float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f;
|
||||
|
||||
PointF p = Utils.getPosition(center, mChart.getYRange() * factor
|
||||
+ mXAxis.mLabelRotatedWidth / 2f, angle);
|
||||
Utils.getPosition(center, mChart.getYRange() * factor
|
||||
+ mXAxis.mLabelRotatedWidth / 2f, angle, pOut);
|
||||
|
||||
drawLabel(c, label, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f,
|
||||
drawLabel(c, label, pOut.x, pOut.y - mXAxis.mLabelRotatedHeight / 2.f,
|
||||
drawLabelAnchor, labelRotationAngleDegrees);
|
||||
}
|
||||
|
||||
MPPointF.recycleInstance(center);
|
||||
MPPointF.recycleInstance(pOut);
|
||||
MPPointF.recycleInstance(drawLabelAnchor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -125,6 +125,7 @@ public class YAxisRenderer extends AxisRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
protected Path mRenderGridLinesPath = new Path();
|
||||
@Override
|
||||
public void renderGridLines(Canvas c) {
|
||||
|
||||
|
@ -139,7 +140,8 @@ public class YAxisRenderer extends AxisRenderer {
|
|||
mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth());
|
||||
mGridPaint.setPathEffect(mYAxis.getGridDashPathEffect());
|
||||
|
||||
Path gridLinePath = new Path();
|
||||
Path gridLinePath = mRenderGridLinesPath;
|
||||
gridLinePath.reset();
|
||||
|
||||
// draw the grid
|
||||
for (int i = 0; i < positions.length; i += 2) {
|
||||
|
@ -171,6 +173,7 @@ public class YAxisRenderer extends AxisRenderer {
|
|||
return p;
|
||||
}
|
||||
|
||||
protected float[] mGetTransformedPositionsBuffer = new float[2];
|
||||
/**
|
||||
* Transforms the values contained in the axis entries to screen pixels and returns them in form of a float array
|
||||
* of x- and y-coordinates.
|
||||
|
@ -179,7 +182,10 @@ public class YAxisRenderer extends AxisRenderer {
|
|||
*/
|
||||
protected float[] getTransformedPositions() {
|
||||
|
||||
float[] positions = new float[mYAxis.mEntryCount * 2];
|
||||
if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2){
|
||||
mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2];
|
||||
}
|
||||
float[] positions = mGetTransformedPositionsBuffer;
|
||||
|
||||
for (int i = 0; i < positions.length; i += 2) {
|
||||
// only fill y values, x values are not needed for y-labels
|
||||
|
@ -190,6 +196,7 @@ public class YAxisRenderer extends AxisRenderer {
|
|||
return positions;
|
||||
}
|
||||
|
||||
protected Path mDrawZeroLinePath = new Path();
|
||||
/**
|
||||
* Draws the zero line.
|
||||
*/
|
||||
|
@ -201,7 +208,8 @@ public class YAxisRenderer extends AxisRenderer {
|
|||
mZeroLinePaint.setColor(mYAxis.getZeroLineColor());
|
||||
mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth());
|
||||
|
||||
Path zeroLinePath = new Path();
|
||||
Path zeroLinePath = mDrawZeroLinePath;
|
||||
zeroLinePath.reset();
|
||||
|
||||
zeroLinePath.moveTo(mViewPortHandler.contentLeft(), (float) pos.y - 1);
|
||||
zeroLinePath.lineTo(mViewPortHandler.contentRight(), (float) pos.y - 1);
|
||||
|
@ -210,6 +218,8 @@ public class YAxisRenderer extends AxisRenderer {
|
|||
c.drawPath(zeroLinePath, mZeroLinePaint);
|
||||
}
|
||||
|
||||
protected Path mRenderLimitLines = new Path();
|
||||
protected float[] mRenderLimitLinesBuffer = new float[2];
|
||||
/**
|
||||
* Draws the LimitLines associated with this axis to the screen.
|
||||
*
|
||||
|
@ -223,8 +233,11 @@ public class YAxisRenderer extends AxisRenderer {
|
|||
if (limitLines == null || limitLines.size() <= 0)
|
||||
return;
|
||||
|
||||
float[] pts = new float[2];
|
||||
Path limitLinePath = new Path();
|
||||
float[] pts = mRenderLimitLinesBuffer;
|
||||
pts[0] = 0;
|
||||
pts[1] = 0;
|
||||
Path limitLinePath = mRenderLimitLines;
|
||||
limitLinePath.reset();
|
||||
|
||||
for (int i = 0; i < limitLines.size(); i++) {
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ public class YAxisRendererHorizontalBarChart extends YAxisRenderer {
|
|||
yMin = (float) p2.x;
|
||||
yMax = (float) p1.x;
|
||||
}
|
||||
|
||||
PointD.recycleInstance(p1);
|
||||
PointD.recycleInstance(p2);
|
||||
}
|
||||
|
||||
computeAxisValues(yMin, yMax);
|
||||
|
@ -147,7 +150,10 @@ public class YAxisRendererHorizontalBarChart extends YAxisRenderer {
|
|||
@Override
|
||||
protected float[] getTransformedPositions() {
|
||||
|
||||
float[] positions = new float[mYAxis.mEntryCount * 2];
|
||||
if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2) {
|
||||
mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2];
|
||||
}
|
||||
float[] positions = mGetTransformedPositionsBuffer;
|
||||
|
||||
for (int i = 0; i < positions.length; i += 2) {
|
||||
// only fill x values, y values are not needed for x-labels
|
||||
|
@ -167,6 +173,8 @@ public class YAxisRendererHorizontalBarChart extends YAxisRenderer {
|
|||
return p;
|
||||
}
|
||||
|
||||
protected Path mDrawZeroLinePathBuffer = new Path();
|
||||
|
||||
@Override
|
||||
protected void drawZeroLine(Canvas c) {
|
||||
|
||||
|
@ -176,7 +184,8 @@ public class YAxisRendererHorizontalBarChart extends YAxisRenderer {
|
|||
mZeroLinePaint.setColor(mYAxis.getZeroLineColor());
|
||||
mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth());
|
||||
|
||||
Path zeroLinePath = new Path();
|
||||
Path zeroLinePath = mDrawZeroLinePathBuffer;
|
||||
zeroLinePath.reset();
|
||||
|
||||
zeroLinePath.moveTo((float) pos.x - 1, mViewPortHandler.contentTop());
|
||||
zeroLinePath.lineTo((float) pos.x - 1, mViewPortHandler.contentBottom());
|
||||
|
@ -185,6 +194,8 @@ public class YAxisRendererHorizontalBarChart extends YAxisRenderer {
|
|||
c.drawPath(zeroLinePath, mZeroLinePaint);
|
||||
}
|
||||
|
||||
protected Path mRenderLimitLinesPathBuffer = new Path();
|
||||
protected float[] mRenderLimitLinesBuffer = new float[4];
|
||||
/**
|
||||
* Draws the LimitLines associated with this axis to the screen.
|
||||
* This is the standard XAxis renderer using the YAxis limit lines.
|
||||
|
@ -199,8 +210,13 @@ public class YAxisRendererHorizontalBarChart extends YAxisRenderer {
|
|||
if (limitLines == null || limitLines.size() <= 0)
|
||||
return;
|
||||
|
||||
float[] pts = new float[4];
|
||||
Path limitLinePath = new Path();
|
||||
float[] pts = mRenderLimitLinesBuffer;
|
||||
pts[0] = 0;
|
||||
pts[1] = 0;
|
||||
pts[2] = 0;
|
||||
pts[3] = 0;
|
||||
Path limitLinePath = mRenderLimitLinesPathBuffer;
|
||||
limitLinePath.reset();
|
||||
|
||||
for (int i = 0; i < limitLines.size(); i++) {
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.graphics.PointF;
|
|||
import com.github.mikephil.charting.charts.RadarChart;
|
||||
import com.github.mikephil.charting.components.LimitLine;
|
||||
import com.github.mikephil.charting.components.YAxis;
|
||||
import com.github.mikephil.charting.utils.MPPointF;
|
||||
import com.github.mikephil.charting.utils.Utils;
|
||||
import com.github.mikephil.charting.utils.ViewPortHandler;
|
||||
|
||||
|
@ -150,7 +151,8 @@ public class YAxisRendererRadarChart extends YAxisRenderer {
|
|||
mAxisLabelPaint.setTextSize(mYAxis.getTextSize());
|
||||
mAxisLabelPaint.setColor(mYAxis.getTextColor());
|
||||
|
||||
PointF center = mChart.getCenterOffsets();
|
||||
MPPointF center = mChart.getCenterOffsets();
|
||||
MPPointF pOut = MPPointF.getInstance(0,0);
|
||||
float factor = mChart.getFactor();
|
||||
|
||||
int labelCount = mYAxis.mEntryCount;
|
||||
|
@ -162,14 +164,17 @@ public class YAxisRendererRadarChart extends YAxisRenderer {
|
|||
|
||||
float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor;
|
||||
|
||||
PointF p = Utils.getPosition(center, r, mChart.getRotationAngle());
|
||||
Utils.getPosition(center, r, mChart.getRotationAngle(), pOut);
|
||||
|
||||
String label = mYAxis.getFormattedLabel(j);
|
||||
|
||||
c.drawText(label, p.x + 10, p.y, mAxisLabelPaint);
|
||||
c.drawText(label, pOut.x + 10, pOut.y, mAxisLabelPaint);
|
||||
}
|
||||
MPPointF.recycleInstance(center);
|
||||
MPPointF.recycleInstance(pOut);
|
||||
}
|
||||
|
||||
private Path mRenderLimitLinesPathBuffer = new Path();
|
||||
@Override
|
||||
public void renderLimitLines(Canvas c) {
|
||||
|
||||
|
@ -184,8 +189,8 @@ public class YAxisRendererRadarChart extends YAxisRenderer {
|
|||
// pixels
|
||||
float factor = mChart.getFactor();
|
||||
|
||||
PointF center = mChart.getCenterOffsets();
|
||||
|
||||
MPPointF center = mChart.getCenterOffsets();
|
||||
MPPointF pOut = MPPointF.getInstance(0,0);
|
||||
for (int i = 0; i < limitLines.size(); i++) {
|
||||
|
||||
LimitLine l = limitLines.get(i);
|
||||
|
@ -199,21 +204,24 @@ public class YAxisRendererRadarChart extends YAxisRenderer {
|
|||
|
||||
float r = (l.getLimit() - mChart.getYChartMin()) * factor;
|
||||
|
||||
Path limitPath = new Path();
|
||||
Path limitPath = mRenderLimitLinesPathBuffer;
|
||||
limitPath.reset();
|
||||
|
||||
|
||||
for (int j = 0; j < mChart.getData().getMaxEntryCountSet().getEntryCount(); j++) {
|
||||
|
||||
PointF p = Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle());
|
||||
Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle(), pOut);
|
||||
|
||||
if (j == 0)
|
||||
limitPath.moveTo(p.x, p.y);
|
||||
limitPath.moveTo(pOut.x, pOut.y);
|
||||
else
|
||||
limitPath.lineTo(p.x, p.y);
|
||||
limitPath.lineTo(pOut.x, pOut.y);
|
||||
}
|
||||
|
||||
limitPath.close();
|
||||
|
||||
c.drawPath(limitPath, mLimitLinePaint);
|
||||
}
|
||||
MPPointF.recycleInstance(center);
|
||||
MPPointF.recycleInstance(pOut);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.github.mikephil.charting.utils.ViewPortHandler;
|
|||
*/
|
||||
public class TriangleShapeRenderer implements ShapeRenderer {
|
||||
|
||||
protected Path mTrianglePathBuffer = new Path();
|
||||
|
||||
@Override
|
||||
public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint
|
||||
|
@ -31,7 +32,8 @@ public class TriangleShapeRenderer implements ShapeRenderer {
|
|||
renderPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
// create a triangle path
|
||||
Path tri = new Path();
|
||||
Path tri = mTrianglePathBuffer;
|
||||
tri.reset();
|
||||
|
||||
for (int i = 0; i < buffer.size(); i += 2) {
|
||||
|
||||
|
|
|
@ -1,16 +1,47 @@
|
|||
|
||||
package com.github.mikephil.charting.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Immutable class for describing width and height dimensions in some arbitrary
|
||||
* Class for describing width and height dimensions in some arbitrary
|
||||
* unit. Replacement for the android.Util.SizeF which is available only on API >= 21.
|
||||
*/
|
||||
public final class FSize {
|
||||
public final class FSize extends ObjectPool.Poolable{
|
||||
|
||||
public final float width;
|
||||
public final float height;
|
||||
// TODO : Encapsulate width & height
|
||||
|
||||
public FSize(final float width, final float height) {
|
||||
public float width;
|
||||
public float height;
|
||||
|
||||
private static ObjectPool<FSize> pool;
|
||||
|
||||
static {
|
||||
pool = ObjectPool.create(256, new FSize(0,0));
|
||||
pool.setReplenishPercentage(0.5f);
|
||||
}
|
||||
|
||||
|
||||
protected ObjectPool.Poolable instantiate(){
|
||||
return new FSize(0,0);
|
||||
}
|
||||
|
||||
public static FSize getInstance(final float width, final float height){
|
||||
FSize result = pool.get();
|
||||
result.width = width;
|
||||
result.height = height;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void recycleInstance(FSize instance){
|
||||
pool.recycle(instance);
|
||||
}
|
||||
|
||||
public static void recycleInstances(List<FSize> instances){
|
||||
pool.recycle(instances);
|
||||
}
|
||||
|
||||
private FSize(final float width, final float height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
@ -42,4 +73,5 @@ public final class FSize {
|
|||
public int hashCode() {
|
||||
return Float.floatToIntBits(width) ^ Float.floatToIntBits(height);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package com.github.mikephil.charting.utils;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Tony Patino on 6/24/16.
|
||||
*/
|
||||
public class MPPointF extends ObjectPool.Poolable {
|
||||
private static ObjectPool<MPPointF> pool;
|
||||
|
||||
public float x;
|
||||
public float y;
|
||||
|
||||
static {
|
||||
pool = ObjectPool.create(32, new MPPointF(0,0));
|
||||
pool.setReplenishPercentage(0.5f);
|
||||
}
|
||||
|
||||
private MPPointF(float x, float y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public static MPPointF getInstance(float x, float y){
|
||||
MPPointF result = pool.get();
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void recycleInstance(MPPointF instance){
|
||||
pool.recycle(instance);
|
||||
}
|
||||
|
||||
public static void recycleInstances(List<MPPointF> instances){
|
||||
pool.recycle(instances);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<MPPointF> CREATOR = new Parcelable.Creator<MPPointF>() {
|
||||
/**
|
||||
* Return a new point from the data in the specified parcel.
|
||||
*/
|
||||
public MPPointF createFromParcel(Parcel in) {
|
||||
MPPointF r = new MPPointF(0,0);
|
||||
r.my_readFromParcel(in);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of rectangles of the specified size.
|
||||
*/
|
||||
public MPPointF[] newArray(int size) {
|
||||
return new MPPointF[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the point's coordinates from the data stored in the specified
|
||||
* parcel. To write a point to a parcel, call writeToParcel().
|
||||
* Provided to support older Android devices.
|
||||
*
|
||||
* @param in The parcel to read the point's coordinates from
|
||||
*/
|
||||
public void my_readFromParcel(Parcel in) {
|
||||
x = in.readFloat();
|
||||
y = in.readFloat();
|
||||
}
|
||||
|
||||
public float getX(){
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public float getY(){
|
||||
return this.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectPool.Poolable instantiate() {
|
||||
return new MPPointF(0,0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
package com.github.mikephil.charting.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An object pool for recycling of object instances extending Poolable.
|
||||
*
|
||||
*
|
||||
* Cost/Benefit :
|
||||
* Cost - The pool can only contain objects extending Poolable.
|
||||
* Benefit - The pool can very quickly determine if an object is elligable for storage without iteration.
|
||||
* Benefit - The pool can also know if an instance of Poolable is already stored in a different pool instance.
|
||||
* Benefit - The pool can grow as needed, if it is empty
|
||||
* Cost - However, refilling the pool when it is empty might incur a time cost with sufficiently large capacity. Set the replenishPercentage to a lower number if this is a concern.
|
||||
*
|
||||
* Created by Tony Patino on 6/20/16.
|
||||
*/
|
||||
public class ObjectPool<T extends ObjectPool.Poolable> {
|
||||
|
||||
private static int ids = 0;
|
||||
|
||||
private int poolId;
|
||||
private int desiredCapacity;
|
||||
private Object[] objects;
|
||||
private int objectsPointer;
|
||||
private T modelObject;
|
||||
private float replenishPercentage;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the id of the given pool instance.
|
||||
*
|
||||
* @return an integer ID belonging to this pool instance.
|
||||
*/
|
||||
public int getPoolId(){
|
||||
return poolId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ObjectPool instance, of a given starting capacity, that recycles instances of a given Poolable object.
|
||||
*
|
||||
* @param withCapacity A positive integer value.
|
||||
* @param object An instance of the object that the pool should recycle.
|
||||
* @return
|
||||
*/
|
||||
public static synchronized ObjectPool create(int withCapacity, Poolable object){
|
||||
ObjectPool result = new ObjectPool(withCapacity, object);
|
||||
result.poolId = ids;
|
||||
ids++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ObjectPool(int withCapacity, T object){
|
||||
if(withCapacity <= 0){
|
||||
throw new IllegalArgumentException("Object Pool must be instantiated with a capacity greater than 0!");
|
||||
}
|
||||
this.desiredCapacity = withCapacity;
|
||||
this.objects = new Object[this.desiredCapacity];
|
||||
this.objectsPointer = 0;
|
||||
this.modelObject = object;
|
||||
this.replenishPercentage = 1.0f;
|
||||
this.refillPool();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the percentage of the pool to replenish on empty. Valid values are between
|
||||
* 0.00f and 1.00f
|
||||
*
|
||||
* @param percentage a value between 0 and 1, representing the percentage of the pool to replenish.
|
||||
*/
|
||||
public void setReplenishPercentage(float percentage){
|
||||
float p = percentage;
|
||||
if(p > 1){
|
||||
p = 1;
|
||||
}
|
||||
else if(p < 0f){
|
||||
p = 0f;
|
||||
}
|
||||
this.replenishPercentage = p;
|
||||
}
|
||||
|
||||
public float getReplenishPercentage(){
|
||||
return replenishPercentage;
|
||||
}
|
||||
|
||||
private void refillPool(){
|
||||
this.refillPool(this.replenishPercentage);
|
||||
}
|
||||
|
||||
private void refillPool(float percentage){
|
||||
int portionOfCapacity = (int) (desiredCapacity * percentage);
|
||||
|
||||
if(portionOfCapacity < 1){
|
||||
portionOfCapacity = 1;
|
||||
}else if(portionOfCapacity > desiredCapacity){
|
||||
portionOfCapacity = desiredCapacity;
|
||||
}
|
||||
|
||||
for(int i = 0 ; i < portionOfCapacity ; i++){
|
||||
this.objects[i] = modelObject.instantiate();
|
||||
}
|
||||
objectsPointer = portionOfCapacity - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of Poolable. If get() is called with an empty pool, the pool will be
|
||||
* replenished. If the pool capacity is sufficiently large, this could come at a performance
|
||||
* cost.
|
||||
*
|
||||
* @return An instance of Poolable object T
|
||||
*/
|
||||
public synchronized T get(){
|
||||
|
||||
if(this.objectsPointer == -1 && this.replenishPercentage > 0.0f){
|
||||
this.refillPool();
|
||||
}
|
||||
|
||||
T result = (T)objects[this.objectsPointer];
|
||||
result.currentOwnerId = Poolable.NO_OWNER;
|
||||
this.objectsPointer--;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recycle an instance of Poolable that this pool is capable of generating.
|
||||
* The T instance passed must not already exist inside this or any other ObjectPool instance.
|
||||
*
|
||||
* @param object An object of type T to recycle
|
||||
*/
|
||||
public synchronized void recycle(T object){
|
||||
if(object.currentOwnerId != Poolable.NO_OWNER){
|
||||
if(object.currentOwnerId == this.poolId){
|
||||
throw new IllegalArgumentException("The object passed is already stored in this pool!");
|
||||
}else {
|
||||
throw new IllegalArgumentException("The object to recycle already belongs to poolId " + object.currentOwnerId + ". Object cannot belong to two different pool instances simultaneously!");
|
||||
}
|
||||
}
|
||||
|
||||
this.objectsPointer++;
|
||||
if(this.objectsPointer >= objects.length){
|
||||
this.resizePool();
|
||||
}
|
||||
|
||||
object.currentOwnerId = this.poolId;
|
||||
objects[this.objectsPointer] = object;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Recycle a List of Poolables that this pool is capable of generating.
|
||||
* The T instances passed must not already exist inside this or any other ObjectPool instance.
|
||||
*
|
||||
* @param objects A list of objects of type T to recycle
|
||||
*/
|
||||
public synchronized void recycle(List<T> objects){
|
||||
while(objects.size() + this.objectsPointer + 1 > this.desiredCapacity){
|
||||
this.resizePool();
|
||||
}
|
||||
final int objectsListSize = objects.size();
|
||||
|
||||
// Not relying on recycle(T object) because this is more performant.
|
||||
for(int i = 0 ; i < objectsListSize ; i++){
|
||||
T object = objects.get(i);
|
||||
if(object.currentOwnerId != Poolable.NO_OWNER){
|
||||
if(object.currentOwnerId == this.poolId){
|
||||
throw new IllegalArgumentException("The object passed is already stored in this pool!");
|
||||
}else {
|
||||
throw new IllegalArgumentException("The object to recycle already belongs to poolId " + object.currentOwnerId + ". Object cannot belong to two different pool instances simultaneously!");
|
||||
}
|
||||
}
|
||||
object.currentOwnerId = this.poolId;
|
||||
this.objects[this.objectsPointer + 1 + i] = object;
|
||||
}
|
||||
this.objectsPointer += objectsListSize;
|
||||
}
|
||||
|
||||
private void resizePool() {
|
||||
final int oldCapacity = this.desiredCapacity;
|
||||
this.desiredCapacity *= 2;
|
||||
Object[] temp = new Object[this.desiredCapacity];
|
||||
for(int i = 0 ; i < oldCapacity ; i++){
|
||||
temp[i] = this.objects[i];
|
||||
}
|
||||
this.objects = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the capacity of this object pool. Note : The pool will automatically resize
|
||||
* to contain additional objects if the user tries to add more objects than the pool's
|
||||
* capacity allows, but this comes at a performance cost.
|
||||
*
|
||||
* @return The capacity of the pool.
|
||||
*/
|
||||
public int getPoolCapacity(){
|
||||
return this.objects.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of objects remaining in the pool, for diagnostic purposes.
|
||||
*
|
||||
* @return The number of objects remaining in the pool.
|
||||
*/
|
||||
public int getPoolCount(){
|
||||
return this.objectsPointer + 1;
|
||||
}
|
||||
|
||||
|
||||
public static abstract class Poolable{
|
||||
|
||||
public static int NO_OWNER = -1;
|
||||
int currentOwnerId = NO_OWNER;
|
||||
|
||||
protected abstract Poolable instantiate();
|
||||
|
||||
}
|
||||
}
|
|
@ -1,17 +1,45 @@
|
|||
|
||||
package com.github.mikephil.charting.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Point encapsulating two double values.
|
||||
*
|
||||
* @author Philipp Jahoda
|
||||
*/
|
||||
public class PointD {
|
||||
public class PointD extends ObjectPool.Poolable {
|
||||
|
||||
private static ObjectPool<PointD> pool;
|
||||
|
||||
static {
|
||||
pool = ObjectPool.create(64, new PointD(0,0));
|
||||
pool.setReplenishPercentage(0.5f);
|
||||
}
|
||||
|
||||
public static PointD getInstance(double x, double y){
|
||||
PointD result = pool.get();
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void recycleInstance(PointD instance){
|
||||
pool.recycle(instance);
|
||||
}
|
||||
|
||||
public static void recycleInstances(List<PointD> instances){
|
||||
pool.recycle(instances);
|
||||
}
|
||||
|
||||
public double x;
|
||||
public double y;
|
||||
|
||||
public PointD(double x, double y) {
|
||||
protected ObjectPool.Poolable instantiate(){
|
||||
return new PointD(0,0);
|
||||
}
|
||||
|
||||
private PointD(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
@ -22,4 +50,4 @@ public class PointD {
|
|||
public String toString() {
|
||||
return "PointD, x: " + x + ", y: " + y;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -93,6 +93,7 @@ public class Transformer {
|
|||
// mOffsetBottom);
|
||||
}
|
||||
|
||||
protected float[] valuePointsForGenerateTransformedValuesScatter = new float[1];
|
||||
/**
|
||||
* Transforms an List of Entry into a float array containing the x and
|
||||
* y values transformed with all matrices for the SCATTERCHART.
|
||||
|
@ -105,7 +106,10 @@ public class Transformer {
|
|||
|
||||
final int count = (int) ((to - from) * phaseX + 1) * 2;
|
||||
|
||||
float[] valuePoints = new float[count];
|
||||
if(valuePointsForGenerateTransformedValuesScatter.length != count){
|
||||
valuePointsForGenerateTransformedValuesScatter = new float[count];
|
||||
}
|
||||
float[] valuePoints = valuePointsForGenerateTransformedValuesScatter;
|
||||
|
||||
for (int j = 0; j < count; j += 2) {
|
||||
|
||||
|
@ -114,6 +118,9 @@ public class Transformer {
|
|||
if (e != null) {
|
||||
valuePoints[j] = e.getX();
|
||||
valuePoints[j + 1] = e.getY() * phaseY;
|
||||
}else{
|
||||
valuePoints[j] = 0;
|
||||
valuePoints[j + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,6 +129,7 @@ public class Transformer {
|
|||
return valuePoints;
|
||||
}
|
||||
|
||||
protected float[] valuePointsForGenerateTransformedValuesBubble = new float[1];
|
||||
/**
|
||||
* Transforms an List of Entry into a float array containing the x and
|
||||
* y values transformed with all matrices for the BUBBLECHART.
|
||||
|
@ -133,7 +141,10 @@ public class Transformer {
|
|||
|
||||
final int count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2;
|
||||
|
||||
float[] valuePoints = new float[count];
|
||||
if(valuePointsForGenerateTransformedValuesBubble.length != count){
|
||||
valuePointsForGenerateTransformedValuesBubble = new float[count];
|
||||
}
|
||||
float[] valuePoints = valuePointsForGenerateTransformedValuesBubble;
|
||||
|
||||
for (int j = 0; j < count; j += 2) {
|
||||
|
||||
|
@ -142,6 +153,9 @@ public class Transformer {
|
|||
if (e != null) {
|
||||
valuePoints[j] = e.getX();
|
||||
valuePoints[j + 1] = e.getY() * phaseY;
|
||||
}else{
|
||||
valuePoints[j] = 0;
|
||||
valuePoints[j + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,6 +164,7 @@ public class Transformer {
|
|||
return valuePoints;
|
||||
}
|
||||
|
||||
protected float[] valuePointsForGenerateTransformedValuesLine = new float[1];
|
||||
/**
|
||||
* Transforms an List of Entry into a float array containing the x and
|
||||
* y values transformed with all matrices for the LINECHART.
|
||||
|
@ -162,7 +177,10 @@ public class Transformer {
|
|||
|
||||
final int count = (int) ((to - from) * phaseX + 1) * 2;
|
||||
|
||||
float[] valuePoints = new float[count];
|
||||
if(valuePointsForGenerateTransformedValuesLine.length != count){
|
||||
valuePointsForGenerateTransformedValuesLine = new float[count];
|
||||
}
|
||||
float[] valuePoints = valuePointsForGenerateTransformedValuesLine;
|
||||
|
||||
for (int j = 0; j < count; j += 2) {
|
||||
|
||||
|
@ -171,6 +189,9 @@ public class Transformer {
|
|||
if (e != null) {
|
||||
valuePoints[j] = e.getX();
|
||||
valuePoints[j + 1] = e.getY() * phaseY;
|
||||
}else{
|
||||
valuePoints[j] = 0;
|
||||
valuePoints[j + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,6 +200,7 @@ public class Transformer {
|
|||
return valuePoints;
|
||||
}
|
||||
|
||||
protected float[] valuePointsForGenerateTransformedValuesCandle = new float[1];
|
||||
/**
|
||||
* Transforms an List of Entry into a float array containing the x and
|
||||
* y values transformed with all matrices for the CANDLESTICKCHART.
|
||||
|
@ -191,7 +213,10 @@ public class Transformer {
|
|||
|
||||
final int count = (int) ((to - from) * phaseX + 1) * 2;
|
||||
|
||||
float[] valuePoints = new float[count];
|
||||
if(valuePointsForGenerateTransformedValuesCandle.length != count){
|
||||
valuePointsForGenerateTransformedValuesCandle = new float[count];
|
||||
}
|
||||
float[] valuePoints = valuePointsForGenerateTransformedValuesCandle;
|
||||
|
||||
for (int j = 0; j < count; j += 2) {
|
||||
|
||||
|
@ -200,6 +225,9 @@ public class Transformer {
|
|||
if (e != null) {
|
||||
valuePoints[j] = e.getX();
|
||||
valuePoints[j + 1] = e.getHigh() * phaseY;
|
||||
}else{
|
||||
valuePoints[j] = 0;
|
||||
valuePoints[j + 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,6 +356,7 @@ public class Transformer {
|
|||
m.mapRect(rects.get(i));
|
||||
}
|
||||
|
||||
protected Matrix mPixelsToValueMatrixBuffer = new Matrix();
|
||||
/**
|
||||
* Transforms the given array of touch positions (pixels) (x, y, x, y, ...)
|
||||
* into values on the chart.
|
||||
|
@ -336,7 +365,8 @@ public class Transformer {
|
|||
*/
|
||||
public void pixelsToValue(float[] pixels) {
|
||||
|
||||
Matrix tmp = new Matrix();
|
||||
Matrix tmp = mPixelsToValueMatrixBuffer;
|
||||
tmp.reset();
|
||||
|
||||
// invert all matrixes to convert back to the original value
|
||||
mMatrixOffset.invert(tmp);
|
||||
|
@ -355,7 +385,8 @@ public class Transformer {
|
|||
float[] ptsBuffer = new float[2];
|
||||
|
||||
/**
|
||||
* Returns the x and y values in the chart at the given touch point
|
||||
* Returns a recyclable PointD instance.
|
||||
* returns the x and y values in the chart at the given touch point
|
||||
* (encapsulated in a PointD). This method transforms pixel coordinates to
|
||||
* coordinates / values in the chart. This is the opposite method to
|
||||
* getPixelsForValues(...).
|
||||
|
@ -366,18 +397,24 @@ public class Transformer {
|
|||
*/
|
||||
public PointD getValuesByTouchPoint(float x, float y) {
|
||||
|
||||
PointD result = PointD.getInstance(0,0);
|
||||
getValuesByTouchPoint(x,y,result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void getValuesByTouchPoint(float x, float y, PointD outputPoint){
|
||||
|
||||
ptsBuffer[0] = x;
|
||||
ptsBuffer[1] = y;
|
||||
|
||||
pixelsToValue(ptsBuffer);
|
||||
|
||||
double xTouchVal = ptsBuffer[0];
|
||||
double yTouchVal = ptsBuffer[1];
|
||||
|
||||
return new PointD(xTouchVal, yTouchVal);
|
||||
outputPoint.x = ptsBuffer[0];
|
||||
outputPoint.y = ptsBuffer[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable PointD instance.
|
||||
* Returns the x and y coordinates (pixels) for a given x and y value in the chart.
|
||||
*
|
||||
* @param x
|
||||
|
@ -394,7 +431,7 @@ public class Transformer {
|
|||
double xPx = ptsBuffer[0];
|
||||
double yPx = ptsBuffer[1];
|
||||
|
||||
return new PointD(xPx, yPx);
|
||||
return PointD.getInstance(xPx, yPx);
|
||||
}
|
||||
|
||||
public Matrix getValueMatrix() {
|
||||
|
|
|
@ -147,6 +147,7 @@ public abstract class Utils {
|
|||
return (int) paint.measureText(demoText);
|
||||
}
|
||||
|
||||
private static Rect mCalcTextHeightRect = new Rect();
|
||||
/**
|
||||
* calculates the approximate height of a text, depending on a demo text
|
||||
* avoid repeated calls (e.g. inside drawing methods)
|
||||
|
@ -157,36 +158,68 @@ public abstract class Utils {
|
|||
*/
|
||||
public static int calcTextHeight(Paint paint, String demoText) {
|
||||
|
||||
Rect r = new Rect();
|
||||
Rect r = mCalcTextHeightRect;
|
||||
r.set(0,0,0,0);
|
||||
paint.getTextBounds(demoText, 0, demoText.length(), r);
|
||||
return r.height();
|
||||
}
|
||||
|
||||
public static float getLineHeight(Paint paint) {
|
||||
Paint.FontMetrics metrics = paint.getFontMetrics();
|
||||
return metrics.descent - metrics.ascent;
|
||||
Paint.FontMetrics metrics = new Paint.FontMetrics();
|
||||
return getLineHeight(paint, metrics);
|
||||
}
|
||||
|
||||
public static float getLineHeight(Paint paint, Paint.FontMetrics fontMetrics){
|
||||
paint.getFontMetrics(fontMetrics);
|
||||
return fontMetrics.descent - fontMetrics.ascent;
|
||||
}
|
||||
|
||||
public static float getLineSpacing(Paint paint) {
|
||||
Paint.FontMetrics metrics = paint.getFontMetrics();
|
||||
return metrics.ascent - metrics.top + metrics.bottom;
|
||||
Paint.FontMetrics metrics = new Paint.FontMetrics();
|
||||
return getLineSpacing(paint, metrics);
|
||||
}
|
||||
|
||||
public static float getLineSpacing(Paint paint, Paint.FontMetrics fontMetrics){
|
||||
paint.getFontMetrics(fontMetrics);
|
||||
return fontMetrics.ascent - fontMetrics.top + fontMetrics.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable FSize instance.
|
||||
* calculates the approximate size of a text, depending on a demo text
|
||||
* avoid repeated calls (e.g. inside drawing methods)
|
||||
*
|
||||
* @param paint
|
||||
* @param demoText
|
||||
* @return A Recyclable FSize instance
|
||||
*/
|
||||
public static FSize calcTextSize(Paint paint, String demoText) {
|
||||
|
||||
FSize result = FSize.getInstance(0,0);
|
||||
calcTextSize(paint, demoText, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Rect mCalcTextSizeRect = new Rect();
|
||||
/**
|
||||
* calculates the approximate size of a text, depending on a demo text
|
||||
* avoid repeated calls (e.g. inside drawing methods)
|
||||
*
|
||||
* @param paint
|
||||
* @param demoText
|
||||
* @return
|
||||
* @param outputFSize An output variable, modified by the function.
|
||||
*/
|
||||
public static FSize calcTextSize(Paint paint, String demoText) {
|
||||
public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) {
|
||||
|
||||
Rect r = new Rect();
|
||||
Rect r = mCalcTextSizeRect;
|
||||
r.set(0,0,0,0);
|
||||
paint.getTextBounds(demoText, 0, demoText.length(), r);
|
||||
return new FSize(r.width(), r.height());
|
||||
outputFSize.width = r.width();
|
||||
outputFSize.height = r.height();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Math.pow(...) is very expensive, so avoid calling it and create it
|
||||
* yourself.
|
||||
|
@ -336,13 +369,18 @@ public abstract class Utils {
|
|||
|
||||
int[] ret = new int[integers.size()];
|
||||
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = integers.get(i).intValue();
|
||||
}
|
||||
copyIntegers(integers, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void copyIntegers(List<Integer> from, int[] to){
|
||||
int count = to.length < from.size() ? to.length : from.size();
|
||||
for(int i = 0 ; i < count ; i++){
|
||||
to[i] = from.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the provided String List to a String array.
|
||||
*
|
||||
|
@ -360,6 +398,13 @@ public abstract class Utils {
|
|||
return ret;
|
||||
}
|
||||
|
||||
public static void copyStrings(List<String> from, String[] to){
|
||||
int count = to.length < from.size() ? to.length : from.size();
|
||||
for(int i = 0 ; i < count ; i++){
|
||||
to[i] = from.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement for the Math.nextUp(...) method that is only available in
|
||||
* HONEYCOMB and higher. Dat's some seeeeek sheeet.
|
||||
|
@ -378,6 +423,7 @@ public abstract class Utils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable MPPointF instance.
|
||||
* Calculates the position around a center point, depending on the distance
|
||||
* from the center, and the angle of the position around the center.
|
||||
*
|
||||
|
@ -386,13 +432,18 @@ public abstract class Utils {
|
|||
* @param angle in degrees, converted to radians internally
|
||||
* @return
|
||||
*/
|
||||
public static PointF getPosition(PointF center, float dist, float angle) {
|
||||
public static MPPointF getPosition(MPPointF center, float dist, float angle) {
|
||||
|
||||
PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))),
|
||||
(float) (center.y + dist * Math.sin(Math.toRadians(angle))));
|
||||
MPPointF p = MPPointF.getInstance(0,0);
|
||||
getPosition(center, dist, angle, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
public static void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint){
|
||||
outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle)));
|
||||
outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle)));
|
||||
}
|
||||
|
||||
public static void velocityTrackerPointerUpCleanUpIfNecessary(MotionEvent ev,
|
||||
VelocityTracker tracker) {
|
||||
|
||||
|
@ -456,7 +507,7 @@ public abstract class Utils {
|
|||
|
||||
public static void drawXAxisValue(Canvas c, String text, float x, float y,
|
||||
Paint paint,
|
||||
PointF anchor, float angleDegrees) {
|
||||
MPPointF anchor, float angleDegrees) {
|
||||
|
||||
float drawOffsetX = 0.f;
|
||||
float drawOffsetY = 0.f;
|
||||
|
@ -494,6 +545,7 @@ public abstract class Utils {
|
|||
|
||||
translateX -= rotatedSize.width * (anchor.x - 0.5f);
|
||||
translateY -= rotatedSize.height * (anchor.y - 0.5f);
|
||||
FSize.recycleInstance(rotatedSize);
|
||||
}
|
||||
|
||||
c.save();
|
||||
|
@ -522,7 +574,7 @@ public abstract class Utils {
|
|||
public static void drawMultilineText(Canvas c, StaticLayout textLayout,
|
||||
float x, float y,
|
||||
TextPaint paint,
|
||||
PointF anchor, float angleDegrees) {
|
||||
MPPointF anchor, float angleDegrees) {
|
||||
|
||||
float drawOffsetX = 0.f;
|
||||
float drawOffsetY = 0.f;
|
||||
|
@ -564,6 +616,7 @@ public abstract class Utils {
|
|||
|
||||
translateX -= rotatedSize.width * (anchor.x - 0.5f);
|
||||
translateY -= rotatedSize.height * (anchor.y - 0.5f);
|
||||
FSize.recycleInstance(rotatedSize);
|
||||
}
|
||||
|
||||
c.save();
|
||||
|
@ -599,7 +652,7 @@ public abstract class Utils {
|
|||
float x, float y,
|
||||
TextPaint paint,
|
||||
FSize constrainedToSize,
|
||||
PointF anchor, float angleDegrees) {
|
||||
MPPointF anchor, float angleDegrees) {
|
||||
|
||||
StaticLayout textLayout = new StaticLayout(
|
||||
text, 0, text.length(),
|
||||
|
@ -611,26 +664,60 @@ public abstract class Utils {
|
|||
drawMultilineText(c, textLayout, x, y, paint, anchor, angleDegrees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable FSize instance.
|
||||
* Represents size of a rotated rectangle by degrees.
|
||||
*
|
||||
* @param rectangleSize
|
||||
* @param degrees
|
||||
* @return A Recyclable FSize instance
|
||||
*/
|
||||
public static FSize getSizeOfRotatedRectangleByDegrees(FSize rectangleSize, float degrees) {
|
||||
final float radians = degrees * FDEG2RAD;
|
||||
return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height,
|
||||
radians);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable FSize instance.
|
||||
* Represents size of a rotated rectangle by radians.
|
||||
*
|
||||
* @param rectangleSize
|
||||
* @param radians
|
||||
* @return A Recyclable FSize instance
|
||||
*/
|
||||
public static FSize getSizeOfRotatedRectangleByRadians(FSize rectangleSize, float radians) {
|
||||
return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height,
|
||||
radians);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable FSize instance.
|
||||
* Represents size of a rotated rectangle by degrees.
|
||||
*
|
||||
* @param rectangleWidth
|
||||
* @param rectangleHeight
|
||||
* @param degrees
|
||||
* @return A Recyclable FSize instance
|
||||
*/
|
||||
public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float
|
||||
rectangleHeight, float degrees) {
|
||||
final float radians = degrees * FDEG2RAD;
|
||||
return getSizeOfRotatedRectangleByRadians(rectangleWidth, rectangleHeight, radians);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recyclable FSize instance.
|
||||
* Represents size of a rotated rectangle by radians.
|
||||
*
|
||||
* @param rectangleWidth
|
||||
* @param rectangleHeight
|
||||
* @param radians
|
||||
* @return A Recyclable FSize instance
|
||||
*/
|
||||
public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float
|
||||
rectangleHeight, float radians) {
|
||||
return new FSize(
|
||||
return FSize.getInstance(
|
||||
Math.abs(rectangleWidth * (float) Math.cos(radians)) + Math.abs(rectangleHeight *
|
||||
(float) Math.sin(radians)),
|
||||
Math.abs(rectangleWidth * (float) Math.sin(radians)) + Math.abs(rectangleHeight *
|
||||
|
|
|
@ -162,8 +162,8 @@ public class ViewPortHandler {
|
|||
return mContentRect;
|
||||
}
|
||||
|
||||
public PointF getContentCenter() {
|
||||
return new PointF(mContentRect.centerX(), mContentRect.centerY());
|
||||
public MPPointF getContentCenter() {
|
||||
return MPPointF.getInstance(mContentRect.centerX(), mContentRect.centerY());
|
||||
}
|
||||
|
||||
public float getChartHeight() {
|
||||
|
@ -198,13 +198,16 @@ public class ViewPortHandler {
|
|||
public Matrix zoomIn(float x, float y) {
|
||||
|
||||
Matrix save = new Matrix();
|
||||
save.set(mMatrixTouch);
|
||||
|
||||
save.postScale(1.4f, 1.4f, x, y);
|
||||
|
||||
zoomIn(x,y,save);
|
||||
return save;
|
||||
}
|
||||
|
||||
public void zoomIn(float x, float y, Matrix outputMatrix){
|
||||
outputMatrix.reset();
|
||||
outputMatrix.set(mMatrixTouch);
|
||||
outputMatrix.postScale(1.4f, 1.4f, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom
|
||||
* center.
|
||||
|
@ -212,13 +215,16 @@ public class ViewPortHandler {
|
|||
public Matrix zoomOut(float x, float y) {
|
||||
|
||||
Matrix save = new Matrix();
|
||||
save.set(mMatrixTouch);
|
||||
|
||||
save.postScale(0.7f, 0.7f, x, y);
|
||||
|
||||
zoomOut(x,y,save);
|
||||
return save;
|
||||
}
|
||||
|
||||
public void zoomOut(float x, float y, Matrix outputMatrix){
|
||||
outputMatrix.reset();
|
||||
outputMatrix.set(mMatrixTouch);
|
||||
outputMatrix.postScale(0.7f, 0.7f, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-scales by the specified scale factors.
|
||||
*
|
||||
|
@ -229,13 +235,16 @@ public class ViewPortHandler {
|
|||
public Matrix zoom(float scaleX, float scaleY) {
|
||||
|
||||
Matrix save = new Matrix();
|
||||
save.set(mMatrixTouch);
|
||||
|
||||
save.postScale(scaleX, scaleY);
|
||||
|
||||
zoom(scaleX,scaleY,save);
|
||||
return save;
|
||||
}
|
||||
|
||||
public void zoom(float scaleX, float scaleY, Matrix outputMatrix){
|
||||
outputMatrix.reset();
|
||||
outputMatrix.set(mMatrixTouch);
|
||||
outputMatrix.postScale(scaleX, scaleY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-scales by the specified scale factors. x and y is pivot.
|
||||
*
|
||||
|
@ -248,13 +257,16 @@ public class ViewPortHandler {
|
|||
public Matrix zoom(float scaleX, float scaleY, float x, float y) {
|
||||
|
||||
Matrix save = new Matrix();
|
||||
save.set(mMatrixTouch);
|
||||
|
||||
save.postScale(scaleX, scaleY, x, y);
|
||||
|
||||
zoom(scaleX,scaleY,x,y,save);
|
||||
return save;
|
||||
}
|
||||
|
||||
public void zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatrix){
|
||||
outputMatrix.reset();
|
||||
outputMatrix.set(mMatrixTouch);
|
||||
outputMatrix.postScale(scaleX,scaleY,x,y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scale factor to the specified values.
|
||||
*
|
||||
|
@ -265,13 +277,16 @@ public class ViewPortHandler {
|
|||
public Matrix setZoom(float scaleX, float scaleY) {
|
||||
|
||||
Matrix save = new Matrix();
|
||||
save.set(mMatrixTouch);
|
||||
|
||||
save.setScale(scaleX, scaleY);
|
||||
|
||||
setZoom(scaleX,scaleY,save);
|
||||
return save;
|
||||
}
|
||||
|
||||
public void setZoom(float scaleX, float scaleY, Matrix outputMatrix){
|
||||
outputMatrix.reset();
|
||||
outputMatrix.set(mMatrixTouch);
|
||||
outputMatrix.setScale(scaleX,scaleY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scale factor to the specified values. x and y is pivot.
|
||||
*
|
||||
|
@ -291,21 +306,35 @@ public class ViewPortHandler {
|
|||
return save;
|
||||
}
|
||||
|
||||
protected float[] valsBufferForFitScreen = new float[9];
|
||||
|
||||
/**
|
||||
* Resets all zooming and dragging and makes the chart fit exactly it's
|
||||
* bounds.
|
||||
*/
|
||||
public Matrix fitScreen() {
|
||||
|
||||
Matrix save = new Matrix();
|
||||
fitScreen(save);
|
||||
return save;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all zooming and dragging and makes the chart fit exactly it's
|
||||
* bounds. Output Matrix is available for those who wish to cache the object.
|
||||
*/
|
||||
public void fitScreen(Matrix outputMatrix){
|
||||
mMinScaleX = 1f;
|
||||
mMinScaleY = 1f;
|
||||
|
||||
Matrix save = new Matrix();
|
||||
save.set(mMatrixTouch);
|
||||
outputMatrix.set(mMatrixTouch);
|
||||
|
||||
float[] vals = new float[9];
|
||||
float[] vals = valsBufferForFitScreen;
|
||||
for(int i = 0 ; i < 9 ; i++){
|
||||
vals[i] = 0;
|
||||
}
|
||||
|
||||
save.getValues(vals);
|
||||
outputMatrix.getValues(vals);
|
||||
|
||||
// reset all translations and scaling
|
||||
vals[Matrix.MTRANS_X] = 0f;
|
||||
|
@ -313,13 +342,11 @@ public class ViewPortHandler {
|
|||
vals[Matrix.MSCALE_X] = 1f;
|
||||
vals[Matrix.MSCALE_Y] = 1f;
|
||||
|
||||
save.setValues(vals);
|
||||
|
||||
return save;
|
||||
outputMatrix.setValues(vals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-translates to the specified points.
|
||||
* Post-translates to the specified points. Less Performant.
|
||||
*
|
||||
* @param transformedPts
|
||||
* @return
|
||||
|
@ -327,16 +354,25 @@ public class ViewPortHandler {
|
|||
public Matrix translate(final float[] transformedPts) {
|
||||
|
||||
Matrix save = new Matrix();
|
||||
save.set(mMatrixTouch);
|
||||
|
||||
final float x = transformedPts[0] - offsetLeft();
|
||||
final float y = transformedPts[1] - offsetTop();
|
||||
|
||||
save.postTranslate(-x, -y);
|
||||
|
||||
translate(transformedPts, save);
|
||||
return save;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-translates to the specified points. Output matrix allows for caching objects.
|
||||
*
|
||||
* @param transformedPts
|
||||
* @return
|
||||
*/
|
||||
public void translate(final float[] transformedPts, Matrix outputMatrix){
|
||||
outputMatrix.reset();
|
||||
outputMatrix.set(mMatrixTouch);
|
||||
final float x = transformedPts[0] - offsetLeft();
|
||||
final float y = transformedPts[1] - offsetTop();
|
||||
outputMatrix.postTranslate(-x, -y);
|
||||
}
|
||||
|
||||
protected Matrix mCenterViewPortMatrixBuffer = new Matrix();
|
||||
/**
|
||||
* Centers the viewport around the specified position (x-index and y-value)
|
||||
* in the chart. Centering the viewport outside the bounds of the chart is
|
||||
|
@ -349,7 +385,8 @@ public class ViewPortHandler {
|
|||
*/
|
||||
public void centerViewPort(final float[] transformedPts, final View view) {
|
||||
|
||||
Matrix save = new Matrix();
|
||||
Matrix save = mCenterViewPortMatrixBuffer;
|
||||
save.reset();
|
||||
save.set(mMatrixTouch);
|
||||
|
||||
final float x = transformedPts[0] - offsetLeft();
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
package com.github.mikephil.charting.test;
|
||||
|
||||
import com.github.mikephil.charting.formatter.FormattedStringCache;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
* Created by Tony Patino on 6/30/16.
|
||||
*/
|
||||
public class FormattedStringCacheTest {
|
||||
|
||||
@Test
|
||||
public void testPrimFloat(){
|
||||
int digits = 2;
|
||||
|
||||
StringBuffer b = new StringBuffer();
|
||||
for (int i = 0; i < digits; i++) {
|
||||
if (i == 0)
|
||||
b.append(".");
|
||||
b.append("0");
|
||||
}
|
||||
|
||||
FormattedStringCache.PrimFloat cache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
|
||||
String s = null;
|
||||
|
||||
s = cache.getFormattedValue(1.0f);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
s = cache.getFormattedValue(1.0f);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.0f);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
for(int i = 0 ; i < 100 ; i++){
|
||||
float f = 0.75f + i;
|
||||
s = cache.getFormattedValue(f);
|
||||
Assert.assertEquals(i+".75", s);
|
||||
}
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.5323234f);
|
||||
Assert.assertEquals("1.53", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimDouble(){
|
||||
int digits = 2;
|
||||
|
||||
StringBuffer b = new StringBuffer();
|
||||
for (int i = 0; i < digits; i++) {
|
||||
if (i == 0)
|
||||
b.append(".");
|
||||
b.append("0");
|
||||
}
|
||||
|
||||
FormattedStringCache.PrimDouble cache = new FormattedStringCache.PrimDouble(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
|
||||
String s = null;
|
||||
|
||||
s = cache.getFormattedValue(1.0d);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
s = cache.getFormattedValue(1.0d);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3d);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3d);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.0d);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
for(int i = 0 ; i < 100 ; i++){
|
||||
double f = 0.75f + i;
|
||||
s = cache.getFormattedValue(f);
|
||||
Assert.assertEquals(i+".75", s);
|
||||
}
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.5323234d);
|
||||
Assert.assertEquals("1.53", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
s = cache.getFormattedValue(1.3111111d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimIntFloat(){
|
||||
|
||||
int digits = 2;
|
||||
|
||||
StringBuffer b = new StringBuffer();
|
||||
for (int i = 0; i < digits; i++) {
|
||||
if (i == 0)
|
||||
b.append(".");
|
||||
b.append("0");
|
||||
}
|
||||
|
||||
FormattedStringCache.PrimIntFloat cache = new FormattedStringCache.PrimIntFloat(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
|
||||
String s = null;
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 0);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 0);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f ,1);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f, 1);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f, 0);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 1);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
for(int i = 0 ; i < 100 ; i++){
|
||||
float f = 0.75f + i;
|
||||
s = cache.getFormattedValue(f, i);
|
||||
Assert.assertEquals(i+".75", s);
|
||||
}
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.5323234f, 200);
|
||||
Assert.assertEquals("1.53", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 300);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 300);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 400);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 500);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 5000);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 0);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 1);
|
||||
Assert.assertEquals("1.31", s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericKV(){
|
||||
|
||||
this.genericIntFloat();
|
||||
this.genericDoubleFloat();
|
||||
this.genericObjectFloat();
|
||||
}
|
||||
|
||||
private void genericObjectFloat() {
|
||||
|
||||
|
||||
int digits = 2;
|
||||
|
||||
StringBuffer b = new StringBuffer();
|
||||
for (int i = 0; i < digits; i++) {
|
||||
if (i == 0)
|
||||
b.append(".");
|
||||
b.append("0");
|
||||
}
|
||||
|
||||
FormattedStringCache.Generic<Object, Float> cache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
|
||||
String s = null;
|
||||
|
||||
|
||||
Object obj0 = new Object();
|
||||
Object obj1 = new Object();
|
||||
Object obj2 = new Object();
|
||||
|
||||
s = cache.getFormattedValue(10f, obj0);
|
||||
|
||||
Assert.assertEquals("10.00", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(10f, obj0);
|
||||
|
||||
Assert.assertEquals("10.00", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(11f, obj1);
|
||||
|
||||
Assert.assertEquals("11.00", s);
|
||||
|
||||
s = cache.getFormattedValue(10f, obj2);
|
||||
|
||||
Assert.assertEquals("10.00", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(11f, obj0);
|
||||
|
||||
Assert.assertEquals("11.00", s);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void genericDoubleFloat() {
|
||||
|
||||
|
||||
|
||||
int digits = 2;
|
||||
|
||||
StringBuffer b = new StringBuffer();
|
||||
for (int i = 0; i < digits; i++) {
|
||||
if (i == 0)
|
||||
b.append(".");
|
||||
b.append("0");
|
||||
}
|
||||
|
||||
FormattedStringCache.Generic<Double, Float> cache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
|
||||
String s = null;
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 0d);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 0d);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f ,1d);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f, 1d);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f, 0d);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 1d);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
for(int i = 0 ; i < 100 ; i++){
|
||||
float f = 0.75f + i;
|
||||
s = cache.getFormattedValue(f, (double)i);
|
||||
Assert.assertEquals(i+".75", s);
|
||||
}
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.5323234f, 200d);
|
||||
Assert.assertEquals("1.53", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 300d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 300d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 400d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 500d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 5000d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 0d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 1d);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
}
|
||||
|
||||
private void genericIntFloat() {
|
||||
|
||||
|
||||
int digits = 2;
|
||||
|
||||
StringBuffer b = new StringBuffer();
|
||||
for (int i = 0; i < digits; i++) {
|
||||
if (i == 0)
|
||||
b.append(".");
|
||||
b.append("0");
|
||||
}
|
||||
|
||||
FormattedStringCache.Generic<Integer, Float> cache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString()));
|
||||
|
||||
String s = null;
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 0);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 0);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f ,1);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f, 1);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3f, 0);
|
||||
|
||||
Assert.assertEquals("1.30", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.0f, 1);
|
||||
|
||||
Assert.assertEquals("1.00", s);
|
||||
|
||||
for(int i = 0 ; i < 100 ; i++){
|
||||
float f = 0.75f + i;
|
||||
s = cache.getFormattedValue(f, i);
|
||||
Assert.assertEquals(i+".75", s);
|
||||
}
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.5323234f, 200);
|
||||
Assert.assertEquals("1.53", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 300);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 300);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 400);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 500);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.3111111f, 5000);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 0);
|
||||
Assert.assertEquals("1.31", s);
|
||||
|
||||
|
||||
s = cache.getFormattedValue(1.31f, 1);
|
||||
Assert.assertEquals("1.31", s);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
package com.github.mikephil.charting.test;
|
||||
|
||||
import com.github.mikephil.charting.utils.ObjectPool;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by otheruser on 6/28/16.
|
||||
*/
|
||||
public class ObjectPoolTest {
|
||||
|
||||
static class TestPoolable extends ObjectPool.Poolable{
|
||||
|
||||
private static ObjectPool<TestPoolable> pool;
|
||||
|
||||
static {
|
||||
pool = ObjectPool.create(4, new TestPoolable(0,0));
|
||||
}
|
||||
|
||||
public int foo = 0;
|
||||
public int bar = 0;
|
||||
|
||||
protected ObjectPool.Poolable instantiate(){
|
||||
return new TestPoolable(0,0);
|
||||
}
|
||||
|
||||
private TestPoolable(int foo, int bar){
|
||||
this.foo = foo;
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
public static TestPoolable getInstance(int foo, int bar){
|
||||
TestPoolable result = pool.get();
|
||||
result.foo = foo;
|
||||
result.bar = bar;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void recycleInstance(TestPoolable instance){
|
||||
pool.recycle(instance);
|
||||
}
|
||||
|
||||
public static void recycleInstances(List<TestPoolable> instances){
|
||||
pool.recycle(instances);
|
||||
}
|
||||
|
||||
public static ObjectPool getPool(){
|
||||
return pool;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectPool(){
|
||||
|
||||
int poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
int poolCount = TestPoolable.getPool().getPoolCount();
|
||||
TestPoolable testPoolable;
|
||||
ArrayList<TestPoolable> testPoolables = new ArrayList<>();
|
||||
|
||||
Assert.assertEquals(4, poolCapacity);
|
||||
Assert.assertEquals(4, poolCount);
|
||||
|
||||
testPoolable = TestPoolable.getInstance(6,7);
|
||||
Assert.assertEquals(6, testPoolable.foo);
|
||||
Assert.assertEquals(7, testPoolable.bar);
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
|
||||
Assert.assertEquals(4, poolCapacity);
|
||||
Assert.assertEquals(3, poolCount);
|
||||
|
||||
TestPoolable.recycleInstance(testPoolable);
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(4, poolCapacity);
|
||||
Assert.assertEquals(4, poolCount);
|
||||
|
||||
|
||||
testPoolable = TestPoolable.getInstance(20,30);
|
||||
Assert.assertEquals(20, testPoolable.foo);
|
||||
Assert.assertEquals(30, testPoolable.bar);
|
||||
|
||||
TestPoolable.recycleInstance(testPoolable);
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(4, poolCapacity);
|
||||
Assert.assertEquals(4, poolCount);
|
||||
|
||||
testPoolables.add(TestPoolable.getInstance(12,24));
|
||||
testPoolables.add(TestPoolable.getInstance(1,2));
|
||||
testPoolables.add(TestPoolable.getInstance(3,5));
|
||||
testPoolables.add(TestPoolable.getInstance(6,8));
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(4, poolCapacity);
|
||||
Assert.assertEquals(0, poolCount);
|
||||
|
||||
|
||||
TestPoolable.recycleInstances(testPoolables);
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(4, poolCapacity);
|
||||
Assert.assertEquals(4, poolCount);
|
||||
|
||||
testPoolables.clear();
|
||||
|
||||
|
||||
testPoolables.add(TestPoolable.getInstance(12,24));
|
||||
testPoolables.add(TestPoolable.getInstance(1,2));
|
||||
testPoolables.add(TestPoolable.getInstance(3,5));
|
||||
testPoolables.add(TestPoolable.getInstance(6,8));
|
||||
testPoolables.add(TestPoolable.getInstance(8,9));
|
||||
Assert.assertEquals(12, testPoolables.get(0).foo);
|
||||
Assert.assertEquals(24, testPoolables.get(0).bar);
|
||||
Assert.assertEquals(1, testPoolables.get(1).foo);
|
||||
Assert.assertEquals(2, testPoolables.get(1).bar);
|
||||
Assert.assertEquals(3, testPoolables.get(2).foo);
|
||||
Assert.assertEquals(5, testPoolables.get(2).bar);
|
||||
Assert.assertEquals(6, testPoolables.get(3).foo);
|
||||
Assert.assertEquals(8, testPoolables.get(3).bar);
|
||||
Assert.assertEquals(8, testPoolables.get(4).foo);
|
||||
Assert.assertEquals(9, testPoolables.get(4).bar);
|
||||
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(4, poolCapacity);
|
||||
Assert.assertEquals(3, poolCount);
|
||||
|
||||
TestPoolable.recycleInstances(testPoolables);
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(8, poolCapacity);
|
||||
Assert.assertEquals(8, poolCount);
|
||||
|
||||
testPoolables.clear();
|
||||
|
||||
|
||||
testPoolables.add(TestPoolable.getInstance(0,0));
|
||||
testPoolables.add(TestPoolable.getInstance(6,8));
|
||||
testPoolables.add(TestPoolable.getInstance(1,2));
|
||||
testPoolables.add(TestPoolable.getInstance(3,5));
|
||||
testPoolables.add(TestPoolable.getInstance(8,9));
|
||||
testPoolables.add(TestPoolable.getInstance(12,24));
|
||||
testPoolables.add(TestPoolable.getInstance(12,24));
|
||||
testPoolables.add(TestPoolable.getInstance(12,24));
|
||||
testPoolables.add(TestPoolable.getInstance(6,8));
|
||||
testPoolables.add(TestPoolable.getInstance(6,8));
|
||||
Assert.assertEquals(0, testPoolables.get(0).foo);
|
||||
Assert.assertEquals(0, testPoolables.get(0).bar);
|
||||
Assert.assertEquals(6, testPoolables.get(1).foo);
|
||||
Assert.assertEquals(8, testPoolables.get(1).bar);
|
||||
Assert.assertEquals(1, testPoolables.get(2).foo);
|
||||
Assert.assertEquals(2, testPoolables.get(2).bar);
|
||||
Assert.assertEquals(3, testPoolables.get(3).foo);
|
||||
Assert.assertEquals(5, testPoolables.get(3).bar);
|
||||
Assert.assertEquals(8, testPoolables.get(4).foo);
|
||||
Assert.assertEquals(9, testPoolables.get(4).bar);
|
||||
Assert.assertEquals(12, testPoolables.get(5).foo);
|
||||
Assert.assertEquals(24, testPoolables.get(5).bar);
|
||||
Assert.assertEquals(12, testPoolables.get(6).foo);
|
||||
Assert.assertEquals(24, testPoolables.get(6).bar);
|
||||
Assert.assertEquals(12, testPoolables.get(7).foo);
|
||||
Assert.assertEquals(24, testPoolables.get(7).bar);
|
||||
Assert.assertEquals(6, testPoolables.get(8).foo);
|
||||
Assert.assertEquals(8, testPoolables.get(8).bar);
|
||||
Assert.assertEquals(6, testPoolables.get(9).foo);
|
||||
Assert.assertEquals(8, testPoolables.get(9).bar);
|
||||
|
||||
for(TestPoolable p : testPoolables){
|
||||
TestPoolable.recycleInstance(p);
|
||||
}
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(16, poolCapacity);
|
||||
Assert.assertEquals(16, poolCount);
|
||||
|
||||
testPoolable = TestPoolable.getInstance(9001,9001);
|
||||
Assert.assertEquals(9001, testPoolable.foo);
|
||||
Assert.assertEquals(9001, testPoolable.bar);
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(16, poolCapacity);
|
||||
Assert.assertEquals(15, poolCount);
|
||||
|
||||
|
||||
TestPoolable.recycleInstance(testPoolable);
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(16, poolCapacity);
|
||||
Assert.assertEquals(16, poolCount);
|
||||
|
||||
Exception e = null;
|
||||
try{
|
||||
// expect an exception.
|
||||
TestPoolable.recycleInstance(testPoolable);
|
||||
}catch (Exception ex){
|
||||
e = ex;
|
||||
}finally{
|
||||
Assert.assertEquals(e.getMessage(), true, e != null);
|
||||
}
|
||||
|
||||
testPoolables.clear();
|
||||
|
||||
TestPoolable.getPool().setReplenishPercentage(0.5f);
|
||||
int i = 16;
|
||||
while(i > 0){
|
||||
testPoolables.add(TestPoolable.getInstance(0,0));
|
||||
i--;
|
||||
}
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(16, poolCapacity);
|
||||
Assert.assertEquals(0, poolCount);
|
||||
|
||||
testPoolables.add(TestPoolable.getInstance(0,0));
|
||||
|
||||
poolCapacity = TestPoolable.getPool().getPoolCapacity();
|
||||
poolCount = TestPoolable.getPool().getPoolCount();
|
||||
Assert.assertEquals(16, poolCapacity);
|
||||
Assert.assertEquals(7, poolCount);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue