10

When calling animateY on a barChart the entire chart is redrawn, animating the bars from y-zero to y-new.

barChart.invalidate();
barChart.animateY(1000);

Is it possible to restrict the animation to the value change. Thus allowing the user to see how the chart grows from y-old (e.g. 100) to y-new (e.g. 120)?

Ammar Tahir
  • 312
  • 4
  • 19
Thomas Kremmel
  • 14,575
  • 26
  • 108
  • 177
  • I don't think so. The current version of the MPChart does not support this. – abhi Aug 05 '15 at 10:16
  • It is possible but you would have to implement it yourself. A simple way would be to draw y values from 100 to 120 using a timer. So you draw 100 then 101 then 102 ... untill 120. – Marcin D Dec 10 '15 at 19:46
  • Is there any solution for this in the latest version of MPAndroidChart ie v3.1.0? – Maximus Jul 21 '23 at 10:10

1 Answers1

7

I was facing the same problem and couldn't find a solution. So, I've created a class that can be called to handle this kind of task.

import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import com.github.mikephil.charting.charts.BarLineChartBase;
import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.data.Entry;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import android.os.Handler;

public class AnimateDataSetChanged {
    private int duration;
    private long startTime;
    private int fps = 30;
    private Handler timerHandler;
    private Chart chart;
    private List<Entry> oldData;
    private List<Entry> newData;
    private Interpolator interpolator;

    public AnimateDataSetChanged(int duration, Chart chart, List<Entry> oldData, List<Entry> newData){
        this.duration = duration;
        this.chart = chart;
        this.oldData = new ArrayList<>(oldData);
        this.newData = new ArrayList<>(newData);
        interpolator = new LinearInterpolator();
    }

    public void setInterpolator(Interpolator interpolator){
        this.interpolator = interpolator;
    }

    public void run(int fps){
        this.fps = fps;
        run();
    }

    public void run(){
        startTime = Calendar.getInstance().getTimeInMillis();
        timerHandler = new Handler();
        Runner runner = new Runner();
        runner.run();
    }

    private class Runner implements Runnable{
        @Override
        public void run() {
            float increment = (Calendar.getInstance().getTimeInMillis() - startTime) / (float)duration;
            increment = interpolator.getInterpolation(increment < 0f ? 0f : increment > 1f ? 1f :increment);
            chart.getData().getDataSetByIndex(0).clear();
            for(int i = 0; i < newData.size(); i++){
                float oldY = oldData.size() > i ? oldData.get(i).getY() : newData.get(i).getY();
                float oldX = oldData.size() > i ? oldData.get(i).getX() : newData.get(i).getX();
                float newX = newData.get(i).getX();
                float newY = newData.get(i).getY();
                Entry e = new Entry(oldX + (newX - oldX) * increment, oldY + (newY - oldY) * increment);
                chart.getData().getDataSetByIndex(0).addEntry(e);
            }
            chart.getXAxis().resetAxisMaximum();
            chart.getXAxis().resetAxisMinimum();
            chart.notifyDataSetChanged();
            chart.refreshDrawableState();
            chart.invalidate();
            if(chart instanceof BarLineChartBase){
                ((BarLineChartBase)chart).setAutoScaleMinMaxEnabled(true);
            }
            if(increment < 1f){
                timerHandler.postDelayed(this, 1000/fps);
            }
        }
    }
}

You can call the class like so:

List<Entry> oldEntries = ...
List<Entry> newEntries = ...

AnimateDataSetChanged changer = new AnimateDataSetChanged(600, mChart, oldEntries, currentDataEntries);
changer.setInterpolator(new AccelerateInterpolator()); // optionally set the Interpolator
changer.run();
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77