Fixes & improvements related to circle cache

This commit is contained in:
Philipp Jahoda 2016-07-02 21:05:00 +02:00
parent 6d21817c8a
commit 94a67ccea0
4 changed files with 186 additions and 150 deletions

View file

@ -56,7 +56,7 @@ repositories {
dependencies {
//compile fileTree(dir: 'libs', include: ['*.jar'])
//compile project(':MPChartLib-Realm') // clone "https://github.com/PhilJay/MPAndroidChart-Realm" to get this or uncomment the gradle dependency below:
compile 'com.github.PhilJay:MPAndroidChart-Realm:v1.0.0@aar'
compile 'com.github.PhilJay:MPAndroidChart-Realm:v1.0.2@aar'
compile project(':MPChartLib')
compile 'com.android.support:appcompat-v7:23.1.1'

View file

@ -193,7 +193,7 @@ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListe
LineDataSet set = (LineDataSet) iSet;
set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER
? LineDataSet.Mode.LINEAR
: LineDataSet.Mode.CUBIC_BEZIER);
: LineDataSet.Mode.CUBIC_BEZIER);
}
mChart.invalidate();
break;
@ -207,7 +207,7 @@ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListe
LineDataSet set = (LineDataSet) iSet;
set.setMode(set.getMode() == LineDataSet.Mode.STEPPED
? LineDataSet.Mode.LINEAR
: LineDataSet.Mode.STEPPED);
: LineDataSet.Mode.STEPPED);
}
mChart.invalidate();
break;
@ -221,7 +221,7 @@ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListe
LineDataSet set = (LineDataSet) iSet;
set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER
? LineDataSet.Mode.LINEAR
: LineDataSet.Mode.HORIZONTAL_BEZIER);
: LineDataSet.Mode.HORIZONTAL_BEZIER);
}
mChart.invalidate();
break;
@ -306,8 +306,8 @@ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListe
if (mChart.getData() != null &&
mChart.getData().getDataSetCount() > 0) {
set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0);
set2 = (LineDataSet)mChart.getData().getDataSetByIndex(1);
set1 = (LineDataSet) mChart.getData().getDataSetByIndex(0);
set2 = (LineDataSet) mChart.getData().getDataSetByIndex(1);
set1.setValues(yVals1);
set2.setValues(yVals2);
mChart.getData().notifyDataChanged();
@ -361,9 +361,12 @@ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListe
public void onValueSelected(Entry e, Highlight h) {
Log.i("Entry selected", e.toString());
mChart.centerViewToAnimated(e.getX(), e.getY(), mChart.getData().getDataSetByIndex(h.getDataSetIndex()).getAxisDependency(), 500);
//mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000);
//mChart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000);
mChart.centerViewToAnimated(e.getX(), e.getY(), mChart.getData().getDataSetByIndex(h.getDataSetIndex())
.getAxisDependency(), 500);
//mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex)
// .getAxisDependency(), 1000);
//mChart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex)
// .getAxisDependency(), 1000);
}
@Override

View file

@ -5,10 +5,10 @@ import android.content.Context;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.formatter.DefaultFillFormatter;
import com.github.mikephil.charting.formatter.FillFormatter;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.utils.Utils;
import java.util.ArrayList;
@ -16,31 +16,49 @@ import java.util.List;
public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet {
/** Drawing mode for this line dataset **/
/**
* Drawing mode for this line dataset
**/
private LineDataSet.Mode mMode = Mode.LINEAR;
/** List representing all colors that are used for the circles */
/**
* List representing all colors that are used for the circles
*/
private List<Integer> mCircleColors = null;
/** the color of the inner circles */
/**
* the color of the inner circles
*/
private int mCircleColorHole = Color.WHITE;
/** the radius of the circle-shaped value indicators */
/**
* the radius of the circle-shaped value indicators
*/
private float mCircleRadius = 8f;
/** the hole radius of the circle-shaped value indicators */
/**
* the hole radius of the circle-shaped value indicators
*/
private float mCircleHoleRadius = 4f;
/** sets the intensity of the cubic lines */
/**
* sets the intensity of the cubic lines
*/
private float mCubicIntensity = 0.2f;
/** the path effect of this DataSet that makes dashed lines possible */
/**
* the path effect of this DataSet that makes dashed lines possible
*/
private DashPathEffect mDashPathEffect = null;
/** formatter for customizing the position of the fill-line */
/**
* formatter for customizing the position of the fill-line
*/
private FillFormatter mFillFormatter = new DefaultFillFormatter();
/** if true, drawing circles is enabled */
/**
* if true, drawing circles is enabled
*/
private boolean mDrawCircles = true;
private boolean mDrawCircleHole = true;
@ -52,11 +70,11 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
// mCircleRadius = Utils.convertDpToPixel(4f);
// mLineWidth = Utils.convertDpToPixel(1f);
if(mCircleColors == null) {
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));
@ -108,7 +126,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
/**
* Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic,
* Min = 0.05f = low cubic effect, Default: 0.2f
*
*
* @param intensity
*/
public void setCubicIntensity(float intensity) {
@ -160,7 +178,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
/**
* sets the size (radius) of the circle shpaed value indicators,
* default size = 4f
*
* <p/>
* This method is deprecated because of unclarity. Use setCircleRadius instead.
*
* @param size
@ -171,9 +189,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
}
/**
*
* This function is deprecated because of unclarity. Use getCircleRadius instead.
*
*/
@Deprecated
public float getCircleSize() {
@ -184,13 +200,13 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
* Enables the line to be drawn in dashed mode, e.g. like this
* "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
* Keep in mind that hardware acceleration boosts performance.
*
* @param lineLength the length of the line pieces
*
* @param lineLength the length of the line pieces
* @param spaceLength the length of space in between the pieces
* @param phase offset, in degrees (normally, use 0)
* @param phase offset, in degrees (normally, use 0)
*/
public void enableDashedLine(float lineLength, float spaceLength, float phase) {
mDashPathEffect = new DashPathEffect(new float[] {
mDashPathEffect = new DashPathEffect(new float[]{
lineLength, spaceLength
}, phase);
}
@ -215,7 +231,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
/**
* set this to true to enable the drawing of circle indicators for this
* DataSet, default true
*
*
* @param enabled
*/
public void setDrawCircles(boolean enabled) {
@ -243,7 +259,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
/**
* returns all colors specified for the circles
*
*
* @return
*/
public List<Integer> getCircleColors() {
@ -252,11 +268,11 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
@Override
public int getCircleColor(int index) {
return mCircleColors.get(index % mCircleColors.size());
return mCircleColors.get(index);
}
@Override
public int getCircleColorCount(){
public int getCircleColorCount() {
return mCircleColors.size();
}
@ -266,7 +282,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
* is higher than the size of the colors array. Make sure that the colors
* are already prepared (by calling getResources().getColor(...)) before
* adding them to the DataSet.
*
*
* @param colors
*/
public void setCircleColors(List<Integer> colors) {
@ -279,7 +295,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
* is higher than the size of the colors array. Make sure that the colors
* are already prepared (by calling getResources().getColor(...)) before
* adding them to the DataSet.
*
*
* @param colors
*/
public void setCircleColors(int[] colors) {
@ -293,13 +309,13 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
* "new String[] { R.color.red, R.color.green, ... }" to provide colors for
* this method. Internally, the colors are resolved using
* getResources().getColor(...)
*
*
* @param colors
*/
public void setCircleColors(int[] colors, Context c) {
List<Integer> clrs = mCircleColors;
if(clrs == null){
if (clrs == null) {
clrs = new ArrayList<>();
}
clrs.clear();
@ -314,7 +330,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
/**
* Sets the one and ONLY color that should be used for this DataSet.
* Internally, this recreates the colors array and adds the specified color.
*
*
* @param color
*/
public void setCircleColor(int color) {
@ -326,7 +342,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
* resets the circle-colors array and creates a new one
*/
public void resetCircleColors() {
if(mCircleColors == null) {
if (mCircleColors == null) {
mCircleColors = new ArrayList<Integer>();
}
mCircleColors.clear();
@ -334,7 +350,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
/**
* Sets the color of the inner circle of the line-circles.
*
*
* @param color
*/
public void setCircleColorHole(int color) {
@ -348,7 +364,7 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
/**
* Set this to true to allow drawing a hole in each data circle.
*
*
* @param enabled
*/
public void setDrawCircleHole(boolean enabled) {

View file

@ -27,36 +27,6 @@ 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;
/**
@ -95,9 +65,7 @@ public class LineChartRenderer extends LineRadarRenderer {
}
@Override
public void initBuffers() {
}
public void initBuffers() { }
@Override
public void drawData(Canvas c) {
@ -595,10 +563,16 @@ public class LineChartRenderer extends LineRadarRenderer {
drawCircles(c);
}
private Path mCirclePathBuffer = new Path();
private float[] mCirclesBuffer = new float[2];
/**
* cache for the circle bitmaps of all datasets
*/
private HashMap<IDataSet, DataSetImageCache> mImageCaches = new HashMap<>();
/**
* buffer for drawing the circles
*/
private float[] mCirclesBuffer = new float[2];
protected void drawCircles(Canvas c) {
mRenderPaint.setStyle(Paint.Style.FILL);
@ -614,16 +588,6 @@ public class LineChartRenderer extends LineRadarRenderer {
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;
@ -642,6 +606,22 @@ public class LineChartRenderer extends LineRadarRenderer {
boolean drawTransparentCircleHole = drawCircleHole &&
dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE;
DataSetImageCache imageCache;
if (mImageCaches.containsKey(dataSet)) {
imageCache = mImageCaches.get(dataSet);
} else {
imageCache = new DataSetImageCache();
mImageCaches.put(dataSet, imageCache);
}
boolean changeRequired = imageCache.init(dataSet);
// only fill the cache with new bitmaps if a change is required
if (changeRequired) {
imageCache.fill(dataSet, drawCircleHole, drawTransparentCircleHole);
}
int boundsRangeCount = mXBounds.range + mXBounds.min;
for (int j = mXBounds.min; j <= boundsRangeCount; j++) {
@ -658,73 +638,11 @@ public class LineChartRenderer extends LineRadarRenderer {
if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0]))
break;
// make sure the circles don't do shitty things outside
// bounds
if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) ||
!mViewPortHandler.isInBoundsY(mCirclesBuffer[1]))
continue;
final int circleColor = dataSet.getCircleColor(j);
mRenderPaint.setColor(circleColor);
Bitmap circleBitmap = null;
int colorIndex;
for (colorIndex = 0; colorIndex < imageCache.circleColors.length; 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);
}
}
}
Bitmap circleBitmap = imageCache.getBitmap(j);
if (circleBitmap != null) {
c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, mRenderPaint);
@ -795,4 +713,103 @@ public class LineChartRenderer extends LineRadarRenderer {
mDrawBitmap = null;
}
}
private class DataSetImageCache {
private Path mCirclePathBuffer = new Path();
private Bitmap[] circleBitmaps;
/**
* Sets up the cache, returns true if a change of cache was required.
*
* @param set
* @return
*/
protected boolean init(ILineDataSet set) {
int size = set.getCircleColorCount();
boolean changeRequired = false;
if (circleBitmaps == null) {
circleBitmaps = new Bitmap[size];
changeRequired = true;
} else if (circleBitmaps.length != size) {
circleBitmaps = new Bitmap[size];
changeRequired = true;
}
return changeRequired;
}
/**
* Fills the cache with bitmaps for the given dataset.
*
* @param set
* @param drawCircleHole
* @param drawTransparentCircleHole
*/
protected void fill(ILineDataSet set, boolean drawCircleHole, boolean drawTransparentCircleHole) {
int colorCount = set.getCircleColorCount();
float circleRadius = set.getCircleRadius();
float circleHoleRadius = set.getCircleHoleRadius();
for (int i = 0; i < colorCount; i++) {
Bitmap.Config conf = Bitmap.Config.ARGB_4444;
Bitmap circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf);
Canvas canvas = new Canvas(circleBitmap);
circleBitmaps[i] = circleBitmap;
mRenderPaint.setColor(set.getCircleColor(i));
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);
}
}
}
}
/**
* Returns the cached Bitmap at the given index.
*
* @param index
* @return
*/
protected Bitmap getBitmap(int index) {
return circleBitmaps[index % circleBitmaps.length];
}
}
}