user2553586
user2553586

Reputation: 11

How to put an image at last point in LineChart using MPAndroidChart?

I am using following code with MPAndroidChart library but it's putting image at all the points.I want the image only at the last point.

enter image description here

public class ImageLineChartRenderer extends LineChartRenderer {
    private final LineChart lineChart;
    private final Bitmap image;

    ImageLineChartRenderer(LineChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap image) {
        super(chart, animator, viewPortHandler);
        this.lineChart = chart;
        this.image = image;
    }

    private float[] mCirclesBuffer = new float[2];

    @Override
    protected void drawCircles(Canvas c) {
        mRenderPaint.setStyle(Paint.Style.FILL);
        float phaseY = 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 = new Bitmap[dataSets.size()];
        float[] scaledBitmapOffsets = new float[dataSets.size()];
        for (int i = 0; i < dataSets.size(); i++) {
            float imageSize = dataSets.get(i).getCircleRadius() * 10;
            scaledBitmapOffsets[i] = imageSize / 2f;
            scaledBitmaps[i] = scaleImage((int) imageSize);
        }

        for (int i = 0; i < dataSets.size(); i++) {
            ILineDataSet dataSet = dataSets.get(i);

            if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || dataSet.getEntryCount() == 0)
                continue;

            mCirclePaintInner.setColor(dataSet.getCircleHoleColor());
            Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
            mXBounds.set(mChart, dataSet);


            int boundsRangeCount = mXBounds.range + mXBounds.min;
            for (int j = mXBounds.min; j <= boundsRangeCount; j++) {
                Entry e = 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);
    }
}

Then following code

Bitmap starBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.personal_plan_check_icon);
        mChart.setRenderer(new ImageLineChartRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler(), starBitmap));
        mChart.invalidate();

Upvotes: 0

Views: 355

Answers (2)

user2553586
user2553586

Reputation: 11

Thanks to Shahin Mursalov's answer, I have rewritten my code as follows, and it works fine:

public class ImageLineChartRenderer extends LineChartRenderer {
    private final LineChart lineChart;
    private final Bitmap image;


    ImageLineChartRenderer(LineChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap image) {
        super(chart, animator, viewPortHandler);
        this.lineChart = chart;
        this.image = image;
    }

    private float[] mCirclesBuffer = new float[2];
    private float[] imageBuffer = new float[2];

    @Override
    protected void drawCircles(Canvas c) {
        mRenderPaint.setStyle(Paint.Style.FILL);
        float phaseY = mAnimator.getPhaseY();
        mCirclesBuffer[0] = 0;
        mCirclesBuffer[1] = 0;
        imageBuffer[0] = 0;
        imageBuffer[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 = new Bitmap[dataSets.size()];
        float[] scaledBitmapOffsets = new float[dataSets.size()];
        for (int i = dataSets.size() - 1; i < dataSets.size(); i++) {
            float imageSize = dataSets.get(i).getCircleRadius() * 2;
            scaledBitmapOffsets[i] = imageSize / 2f;
            scaledBitmaps[i] = scaleImage((int) imageSize);
        }

        for (int i = 0; i < dataSets.size(); i++) {
            ILineDataSet dataSet = dataSets.get(i);

            if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || dataSet.getEntryCount() == 0)
                continue;

            mCirclePaintInner.setColor(dataSet.getCircleHoleColor());
            Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
            mXBounds.set(mChart, dataSet);

            int boundsRangeCount = mXBounds.range + mXBounds.min;

            float circleRadius = dataSet.getCircleRadius();
            float circleHoleRadius = dataSet.getCircleHoleRadius();
            boolean drawCircleHole = dataSet.isDrawCircleHoleEnabled() &&
                    circleHoleRadius < circleRadius &&
                    circleHoleRadius > 0.f;
            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);
            }

            for (int j = mXBounds.min; j <= boundsRangeCount; j++) {

                Entry e = 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;

                Bitmap circleBitmap = imageCache.getBitmap(j);

                if (circleBitmap != null) {
                    c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, null);
                }
            }
            if (boundsRangeCount == dataSet.getEntryCount() - 1) {
                for (int j = boundsRangeCount; j <= boundsRangeCount; j++) {
                    Entry e = dataSet.getEntryForIndex(j);
                    if (e == null) break;
                    imageBuffer[0] = e.getX();
                    imageBuffer[1] = e.getY() * phaseY;
                    trans.pointValuesToPixel(imageBuffer);
                    if (!mViewPortHandler.isInBoundsRight(imageBuffer[0]))
                        break;
                    if (!mViewPortHandler.isInBoundsLeft(imageBuffer[0]) || !mViewPortHandler.isInBoundsY(imageBuffer[1]))
                        continue;

                    if (scaledBitmaps[i] != null) {
                        c.drawBitmap(scaledBitmaps[i],
                                imageBuffer[0] - scaledBitmapOffsets[i],
                                imageBuffer[1] - scaledBitmapOffsets[i],
                                mRenderPaint);
                    }
                }
            }



        }

    }


    private HashMap<IDataSet, ImageLineChartRenderer.DataSetImageCache> mImageCaches = new HashMap<>();

    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];
        }
    }


    private Bitmap scaleImage(int radius) {
        return Bitmap.createScaledBitmap(image, radius, radius, false);
    }
}

Upvotes: 0

user7010102
user7010102

Reputation:

Try changing this piece of code:

for (int j = mXBounds.min; j <= boundsRangeCount; j++)

to this:

for (int j = boundsRangeCount; j <= boundsRangeCount; j++)

PS: I do not think this is the best possible solution.

Upvotes: 1

Related Questions