Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
269 views
in Technique[技术] by (71.8m points)

android - MPAndroidChart: add custom image inside bars

I am using MPAndroidChart and I want to show a custom drawable inside this CombinedChart like in below image:

a bar chart with star images inside the bars

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

Can any one help me for customise the BarChart?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To get the star image inside our bars, we will need to create a custom renderer. Because our bar chart uses BarChartRenderer we will 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 inspect the source for BarChartRenderer we can see that it calls the method called drawData and then iterates through each DataSet and calls drawDataSet. drawDataSet is where the action is happening: it's drawing the shadows and the bars. It's an appropriate place to add logic to draw an extra like images, so let's add a call to a method to draw our images there:

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

We now need a method that will iterate through the DataSet and draw the star images. An appropriate method that will serve as a template is drawValues so let's copy that and change it so that is draws an image rather than text. The key to understanding this is seeing how BarBuffer works. BarBuffer holds the on-screen (pixel) co-ordinates for a bar for a given Entry at j, j + 1, j + 2, j + 3.

To clarify, j is the left x co-ordinate, j + 1 is the top y co-ordinate and so on through to the right x co-ordinate at j + 3. We'll extract these to 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 the renderer is to add logic to scale the bitmap and position it correctly. Here is the final proof-of-concept of the custom renderer:

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 screen shot - you can see that values over 50 have the star:

bar chart with custom image inside bars


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...