Show Image Instead Of Circle On Linechart
Solution 1:
And finally after trying so many things, with the help of @David Rawson's suggestion and this post MPAndroidChart LineChart custom highlight drawable
I have managed to create a custom renderer, which replaces the default circle image in chart with the provided image.
Following is the code snippet of solution.
classImageLineChartRendererextendsLineChartRenderer {
privatefinal LineChart lineChart;
privatefinal Bitmap image;
ImageLineChartRenderer(LineChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap image) {
super(chart, animator, viewPortHandler);
this.lineChart = chart;
this.image = image;
}
privatefloat[] mCirclesBuffer = newfloat[2];
@OverrideprotectedvoiddrawCircles(Canvas c) {
mRenderPaint.setStyle(Paint.Style.FILL);
floatphaseY= mAnimator.getPhaseY();
mCirclesBuffer[0] = 0;
mCirclesBuffer[1] = 0;
List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();
//Draw bitmap image for every data set with size as radius * 10, and store it in scaled bitmaps array
Bitmap[] scaledBitmaps = newBitmap[dataSets.size()];
float[] scaledBitmapOffsets = newfloat[dataSets.size()];
for (inti=0; i < dataSets.size(); i++) {
floatimageSize= dataSets.get(i).getCircleRadius() * 10;
scaledBitmapOffsets[i] = imageSize / 2f;
scaledBitmaps[i] = scaleImage((int) imageSize);
}
for (inti=0; i < dataSets.size(); i++) {
ILineDataSetdataSet= dataSets.get(i);
if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || dataSet.getEntryCount() == 0)
continue;
mCirclePaintInner.setColor(dataSet.getCircleHoleColor());
Transformertrans= mChart.getTransformer(dataSet.getAxisDependency());
mXBounds.set(mChart, dataSet);
intboundsRangeCount= mXBounds.range + mXBounds.min;
for (intj= mXBounds.min; j <= boundsRangeCount; j++) {
Entrye= dataSet.getEntryForIndex(j);
if (e == null) break;
mCirclesBuffer[0] = e.getX();
mCirclesBuffer[1] = e.getY() * phaseY;
trans.pointValuesToPixel(mCirclesBuffer);
if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0]))
break;
if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) || !mViewPortHandler.isInBoundsY(mCirclesBuffer[1]))
continue;
if (scaledBitmaps[i] != null) {
c.drawBitmap(scaledBitmaps[i],
mCirclesBuffer[0] - scaledBitmapOffsets[i],
mCirclesBuffer[1] - scaledBitmapOffsets[i],
mRenderPaint);
}
}
}
}
private Bitmap scaleImage(int radius) {
return Bitmap.createScaledBitmap(image, radius, radius, false);
}
Hope this helps someone.
Solution 2:
You can simply create entry with drawable, and it will be drawn instead of circle on graph.
new Entry(i, value, drawable)
Solution 3:
I also came across this question, but had a bit more specific requirements:
- Draw the circles only at specific points on the chart.
- Have the flexibility to use different drawables in different cases.
Finally, I managed to achieve this:
Where each circle is actually a regular drawable and can be replaced with anything else.
Solved it in a next way:
1.Create an Entry subclass which takes a drawable as a parameter.
/**
* Represents an [Entry] which is able to use drawables (including different drawables for different points) instead of the circle.
* For the points where you don't need points use a regular [Entry].
*/classDrawableCircleEntry @JvmOverloadsconstructor(
@DrawableRes val circleDrawableRes: Int,
x: Float,
y: Float,
icon: Drawable? = null,
data: Any? = null
) : Entry(x, y, icon, data)
2.Create a custom rendered, which
draws the drawable instead of the circle in case entry is a type of DrawableCircleEntry.
Doesn't draw the circle in case try is a regular Entry.
internalclassLineChartCustomCirclesRenderer(privateval context: Context, lineChart: LineChart ) : LineChartRenderer(lineChart, lineChart.animator, lineChart.viewPortHandler) { // Contains (left, top) coordinates of the next circle which has to be drawnprivateval circleCoordinates = FloatArray(2) // Cached drawablesprivateval drawablesCache = SparseArray<Drawable>() overridefundrawCircles(canvas: Canvas) { val phaseY = mAnimator.phaseY circleCoordinates[0] = 0f circleCoordinates[1] = 0fval dataSets = mChart.lineData.dataSets dataSets.forEach { dataSet -> if (!dataSet.isVisible || !dataSet.isDrawCirclesEnabled || dataSet.entryCount == 0) return@forEachval transformer = mChart.getTransformer(dataSet.axisDependency) mXBounds[mChart] = dataSet val boundsRangeCount = mXBounds.range + mXBounds.min for (i in mXBounds.min..boundsRangeCount) { // don't do anything in case entry is not type of DrawableCircleEntryval entry = dataSet.getEntryForIndex(i) as? DrawableCircleEntry ?: continue circleCoordinates[0] = entry.x circleCoordinates[1] = entry.y * phaseY transformer.pointValuesToPixel(circleCoordinates) if (!mViewPortHandler.isInBoundsRight(circleCoordinates[0])) breakif (!mViewPortHandler.isInBoundsLeft(circleCoordinates[0]) || !mViewPortHandler.isInBoundsY(circleCoordinates[1])) continue// Drawable radius is taken as `dataSet.circleRadius`val radius = dataSet.circleRadius // Retrieve the drawable, center it and draw on canvas getDrawable(entry.circleDrawableRes)?.run { setBounds((circleCoordinates[0] - radius).roundToInt(), (circleCoordinates[1] - radius).roundToInt(), (circleCoordinates[0] + radius).roundToInt(), (circleCoordinates[1] + radius).roundToInt()) draw(canvas) } } } } privatefungetDrawable(@DrawableRes drawableRes: Int): Drawable? { drawablesCache[drawableRes]?.let { return it } return ContextCompat.getDrawable(context, drawableRes) .also { drawablesCache.append(drawableRes, it) } }
}
3.Enable circles for the dataset and set the needed radius. The drawable's size will be radius*2
dataSet.setDrawCircles(true)
dataSet.circleRadius = 3f
4.When constructing Entries, create either normal Entry for a point where you don't need to draw a circle and a DrawableCircleEntry when you need one. For instance,
...
val entry = when {
someCondition ->DrawableCircleEntry(R.drawable.your_awesome_drawable, floatIndex, floatValue)
anotherCondition ->DrawableCircleEntry(R.drawable.your_another_drawable, floatIndex, floatValue)
else->Entry(floatIndex, floatValue)
}
...
One of the drawables in my case looks like:
<item><shapeandroid:shape="ring"android:thickness="@dimen/chart_circle_stroke_thickness"android:useLevel="false"><solidandroid:color="#497EFF" /></shape></item>
But it can be any other.
Enjoy.
Post a Comment for "Show Image Instead Of Circle On Linechart"