MPAndroidChart: add custom images inside bars

I am using MPAndroidChart and I want to show a custom drawing inside this CombinedChart , as shown below:

a bar chart with star-shaped images inside the bars

If the value bar => the target value is, say, 50, then I would like to add a star image inside the panel.

Can someone help me set up BarChart?

+5
source share
1 answer

To get a star image inside our bars, we need to create our own renderer. Since our histogram uses BarChartRenderer , we subclass this first and add a parameter for our image:

 public class ImageBarChartRenderer extends BarChartRenderer { private final Bitmap barImage; public ImageBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap barImage) { super(chart, animator, viewPortHandler); this.barImage = barImage; } 

If we check the source for BarChartRenderer , we see that it calls a method called drawData , and then drawData through each DataSet and calls drawDataSet . drawDataSet is where the action takes place: it draws shadows and stripes. This is a good place to add logic to draw additional images, so add a method call to draw our images:

  @Override protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { super.drawDataSet(c, dataSet, index); drawBarImages(c, dataSet, index); } 

Now we need a method that will go through the DataSet and draw star-shaped images. The appropriate method that will serve as the template is drawValues , so copy it and change it so that it draws an image, not text. The key to understanding this is how BarBuffer works. BarBuffer holds the coordinates of the screen (pixel) for the strip for a given input in j , j + 1 , j + 2 , j + 3 .

To clarify, j is the x coordinate on the left, j + 1 is the upper y coordinate, etc. to the right coordinate x at j + 3 . We will extract them for the variables to make it easier to understand:

  protected void drawBarImages(Canvas c, IBarDataSet dataSet, int index) { BarBuffer buffer = mBarBuffers[index]; float left; //avoid allocation inside loop float right; float top; float bottom; for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { left = buffer.buffer[j]; right = buffer.buffer[j + 2]; top = buffer.buffer[j + 1]; bottom = buffer.buffer[j + 3]; float x = (left + right) / 2f; if (!mViewPortHandler.isInBoundsRight(x)) break; if (!mViewPortHandler.isInBoundsY(top) || !mViewPortHandler.isInBoundsLeft(x)) continue; BarEntry entry = dataSet.getEntryForIndex(j / 4); float val = entry.getY(); if (val > 50) { drawStar(c, barImage, x, top); } } } 

Here's how to consume the renderer:

  Bitmap starBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.star); mChart.setRenderer(new ImageBarChartRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler(), starBitmap)); 

The final step to rendering is to add logic to scale the bitmap and the correct position. Here is the final proof of the concept of custom rendering:

 package com.xxmassdeveloper.mpchartexample; import android.graphics.Bitmap; import android.graphics.Canvas; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.buffer.BarBuffer; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.renderer.BarChartRenderer; import com.github.mikephil.charting.utils.ViewPortHandler; /** * Created by David on 29/12/2016. */ public class ImageBarChartRenderer extends BarChartRenderer { private final Bitmap barImage; public ImageBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap barImage) { super(chart, animator, viewPortHandler); this.barImage = barImage; } @Override public void drawData(Canvas c) { super.drawData(c); } @Override protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { super.drawDataSet(c, dataSet, index); drawBarImages(c, dataSet, index); } protected void drawBarImages(Canvas c, IBarDataSet dataSet, int index) { BarBuffer buffer = mBarBuffers[index]; float left; //avoid allocation inside loop float right; float top; float bottom; final Bitmap scaledBarImage = scaleBarImage(buffer); int starWidth = scaledBarImage.getWidth(); int starOffset = starWidth / 2; for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { left = buffer.buffer[j]; right = buffer.buffer[j + 2]; top = buffer.buffer[j + 1]; bottom = buffer.buffer[j + 3]; float x = (left + right) / 2f; if (!mViewPortHandler.isInBoundsRight(x)) break; if (!mViewPortHandler.isInBoundsY(top) || !mViewPortHandler.isInBoundsLeft(x)) continue; BarEntry entry = dataSet.getEntryForIndex(j / 4); float val = entry.getY(); if (val > 50) { drawImage(c, scaledBarImage, x - starOffset, top); } } } private Bitmap scaleBarImage(BarBuffer buffer) { float firstLeft = buffer.buffer[0]; float firstRight = buffer.buffer[2]; int firstWidth = (int) Math.ceil(firstRight - firstLeft); return Bitmap.createScaledBitmap(barImage, firstWidth, firstWidth, false); } protected void drawImage(Canvas c, Bitmap image, float x, float y) { if (image != null) { c.drawBitmap(image, x, y, null); } } } 

Here's a screenshot - you can see that values ​​over 50 have a star:

bar chart with customizable inner bar

+4
source

Source: https://habr.com/ru/post/1261931/


All Articles