Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mpandroidchart - animate y-value change in barchart

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)?

like image 767
Thomas Kremmel Avatar asked Jan 31 '15 13:01

Thomas Kremmel


1 Answers

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();
like image 59
Chris Stillwell Avatar answered Nov 15 '22 21:11

Chris Stillwell