Add feature to move the viewport to a specified position animated

This commit is contained in:
Philipp Jahoda 2016-02-20 11:25:34 +01:00
parent 014c688633
commit 72cabd15aa
9 changed files with 318 additions and 51 deletions

View file

@ -23,12 +23,10 @@ import com.github.mikephil.charting.components.YAxis.AxisDependency;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.data.filter.Approximator;
import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.highlight.Highlight;
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
import java.util.ArrayList;
@ -228,6 +226,7 @@ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListe
mChart.animateXY(3000, 3000);
break;
}
case R.id.actionSave: {
if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) {
Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!",
@ -327,6 +326,9 @@ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListe
@Override
public void onValueSelected(Entry e, int dataSetIndex, Highlight h) {
Log.i("Entry selected", e.toString());
//mChart.centerViewToAnimated(e.getXIndex(), e.getVal(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000);
//mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getXIndex(), e.getVal(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000);
}
@Override

View file

@ -26,6 +26,8 @@ import com.github.mikephil.charting.highlight.ChartHighlighter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
import com.github.mikephil.charting.jobs.AnimatedMoveViewJob;
import com.github.mikephil.charting.jobs.AnimatedZoomJob;
import com.github.mikephil.charting.jobs.MoveViewJob;
import com.github.mikephil.charting.listener.BarLineChartTouchListener;
import com.github.mikephil.charting.listener.OnDrawListener;
@ -683,6 +685,28 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
postInvalidate();
}
/**
* Zooms by the specified scale factor to the specified values on the specified axis.
* STILL WORK IN PROGRESS
*
* @param scaleX
* @param scaleY
* @param xValue
* @param yValue
* @param axis
* @param duration
*/
private void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, long duration) {
PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis);
float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
float xsInView = getXAxis().getValues().size() / mViewPortHandler.getScaleX();
Runnable job = new AnimatedZoomJob(mViewPortHandler, this, getTransformer(axis), scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), xValue - xsInView / 2f, yValue + valsInView / 2f, (float) bounds.x, (float) bounds.y, duration);
postJob(job);
}
/**
* Resets all zooming and dragging and makes the chart fit exactly it's
* bounds.
@ -774,11 +798,7 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
Runnable job = new MoveViewJob(mViewPortHandler, xIndex, 0f,
getTransformer(AxisDependency.LEFT), this);
if (mViewPortHandler.hasChartDimens()) {
post(job);
} else {
mJobs.add(job);
}
postJob(job);
}
/**
@ -795,16 +815,12 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
Runnable job = new MoveViewJob(mViewPortHandler, 0f, yValue + valsInView / 2f,
getTransformer(axis), this);
if (mViewPortHandler.hasChartDimens()) {
post(job);
} else {
mJobs.add(job);
}
postJob(job);
}
/**
* This will move the left side of the current viewport to the specified
* x-index on the x-axis, and center the viewport to the specified y-value
* x-value on the x-axis, and center the viewport to the specified y-value
* on the y-axis.
* This also refreshes the chart by calling invalidate().
*
@ -819,23 +835,41 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
Runnable job = new MoveViewJob(mViewPortHandler, xIndex, yValue + valsInView / 2f,
getTransformer(axis), this);
if (mViewPortHandler.hasChartDimens()) {
post(job);
} else {
mJobs.add(job);
}
postJob(job);
}
/**
* This will move the left side of the current viewport to the specified x-position
* and center the viewport to the specified y-position animated.
* This also refreshes the chart by calling invalidate().
*
* @param xIndex
* @param yValue
* @param axis
* @param duration the duration of the animation in milliseconds
*/
public void moveViewToAnimated(float xIndex, float yValue, AxisDependency axis, long duration) {
PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis);
float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xIndex, yValue + valsInView / 2f,
getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration);
postJob(job);
}
/**
* This will move the center of the current viewport to the specified
* x-index and y-value.
* x-value and y-value.
* This also refreshes the chart by calling invalidate().
*
* @param xIndex
* @param yValue
* @param axis - which axis should be used as a reference for the y-axis
*/
public void centerViewTo(int xIndex, float yValue, AxisDependency axis) {
public void centerViewTo(float xIndex, float yValue, AxisDependency axis) {
float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
float xsInView = getXAxis().getValues().size() / mViewPortHandler.getScaleX();
@ -844,11 +878,30 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
xIndex - xsInView / 2f, yValue + valsInView / 2f,
getTransformer(axis), this);
if (mViewPortHandler.hasChartDimens()) {
post(job);
} else {
mJobs.add(job);
}
postJob(job);
}
/**
* This will move the center of the current viewport to the specified
* x-value and y-value animated.
*
* @param xIndex
* @param yValue
* @param axis
* @param duration the duration of the animation in milliseconds
*/
public void centerViewToAnimated(float xIndex, float yValue, AxisDependency axis, long duration) {
PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis);
float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY();
float xsInView = getXAxis().getValues().size() / mViewPortHandler.getScaleX();
Runnable job = new AnimatedMoveViewJob(mViewPortHandler,
xIndex - xsInView / 2f, yValue + valsInView / 2f,
getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration);
postJob(job);
}
/**

View file

@ -1630,6 +1630,21 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
mJobs.clear();
}
/**
* Either posts a job immediately if the chart has already setup it's
* dimensions or adds the job to the execution queue.
*
* @param job
*/
protected void postJob(Runnable job) {
if (mViewPortHandler.hasChartDimens()) {
post(job);
} else {
mJobs.add(job);
}
}
/**
* Returns all jobs that are scheduled to be executed after
* onSizeChanged(...).

View file

@ -0,0 +1,46 @@
package com.github.mikephil.charting.jobs;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.view.View;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.ViewPortHandler;
/**
* Created by Philipp Jahoda on 19/02/16.
*/
@SuppressLint("NewApi")
public abstract class AnimatedJob extends Job implements ValueAnimator.AnimatorUpdateListener {
protected ObjectAnimator animator;
protected float phase;
protected float xOrigin;
protected float yOrigin;
public AnimatedJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) {
super(viewPortHandler, xValue, yValue, trans, v);
this.xOrigin = xOrigin;
this.yOrigin = yOrigin;
animator = ObjectAnimator.ofFloat(this, "phase", 0f, 1f);
animator.setDuration(duration);
animator.addUpdateListener(this);
}
@SuppressLint("NewApi")
@Override
public void run() {
animator.start();
}
public float getPhase() {
return phase;
}
public void setPhase(float phase) {
this.phase = phase;
}
}

View file

@ -0,0 +1,29 @@
package com.github.mikephil.charting.jobs;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.view.View;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.ViewPortHandler;
/**
* Created by Philipp Jahoda on 19/02/16.
*/
public class AnimatedMoveViewJob extends AnimatedJob {
public AnimatedMoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) {
super(viewPortHandler, xValue, yValue, trans, v, xOrigin, yOrigin, duration);
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
pts[0] = xOrigin + (xValue - xOrigin) * phase;
pts[1] = yOrigin + (yValue - yOrigin) * phase;
mTrans.pointValuesToPixel(pts);
mViewPortHandler.centerViewPort(pts, view);
}
}

View file

@ -0,0 +1,71 @@
package com.github.mikephil.charting.jobs;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.view.View;
import com.github.mikephil.charting.charts.BarLineChartBase;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.ViewPortHandler;
/**
* Created by Philipp Jahoda on 19/02/16.
*/
@SuppressLint("NewApi")
public class AnimatedZoomJob extends AnimatedJob implements Animator.AnimatorListener {
protected float zoomOriginX;
protected float zoomOriginY;
protected float zoomCenterX;
protected float zoomCenterY;
@SuppressLint("NewApi")
public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer trans, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) {
super(viewPortHandler, scaleX, scaleY, trans, v, xOrigin, yOrigin, duration);
this.zoomCenterX = zoomCenterX;
this.zoomCenterY = zoomCenterY;
this.zoomOriginX = zoomOriginX;
this.zoomOriginY = zoomOriginY;
this.animator.addListener(this);
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float scaleX = xOrigin + (xValue - xOrigin) * phase;
float scaleY = yOrigin + (yValue - yOrigin) * phase;
pts[0] = zoomOriginX + (zoomCenterX - zoomOriginX) * phase;
pts[1] = zoomOriginY + (zoomCenterY - zoomOriginY) * phase;
mTrans.pointValuesToPixel(pts);
//Matrix save = mViewPortHandler.zoomAndCenter(xValue * phase, yValue * phase, zoomCenterX * phase, zoomCenterY * phase);
mViewPortHandler.zoomAndCenter(scaleX, scaleY, pts, view);//zoomOriginX + (zoomCenterX - zoomOriginX) * phase, zoomOriginY + (zoomCenterY - zoomOriginY) * phase);
}
@Override
public void onAnimationEnd(Animator animation) {
((BarLineChartBase) view).calculateOffsets();
view.postInvalidate();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationStart(Animator animation) {
}
}

View file

@ -0,0 +1,35 @@
package com.github.mikephil.charting.jobs;
import android.view.View;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.ViewPortHandler;
/**
* Runnable that is used for viewport modifications since they cannot be
* executed at any time. This can be used to delay the execution of viewport
* modifications until the onSizeChanged(...) method of the chartview is called.
*
* @author Philipp Jahoda
*/
public abstract class Job implements Runnable {
protected float[] pts = new float[2];
protected ViewPortHandler mViewPortHandler;
protected float xValue = 0f;
protected float yValue = 0f;
protected Transformer mTrans;
protected View view;
public Job(ViewPortHandler viewPortHandler, float xValue, float yValue,
Transformer trans, View v) {
this.mViewPortHandler = viewPortHandler;
this.xValue = xValue;
this.yValue = yValue;
this.mTrans = trans;
this.view = v;
}
}

View file

@ -7,36 +7,19 @@ import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.ViewPortHandler;
/**
* Runnable that is used for viewport modifications since they cannot be
* executed at any time. This can be used to delay the execution of viewport
* modifications until the onSizeChanged(...) method of the chartview is called.
*
* @author Philipp Jahoda
* Created by Philipp Jahoda on 19/02/16.
*/
public class MoveViewJob implements Runnable {
public class MoveViewJob extends Job {
protected ViewPortHandler mViewPortHandler;
protected float xIndex = 0f;
protected float yValue = 0f;
protected Transformer mTrans;
protected View view;
public MoveViewJob(ViewPortHandler viewPortHandler, float xIndex, float yValue,
Transformer trans, View v) {
this.mViewPortHandler = viewPortHandler;
this.xIndex = xIndex;
this.yValue = yValue;
this.mTrans = trans;
this.view = v;
public MoveViewJob(ViewPortHandler viewPortHandler, float xIndex, float yValue, Transformer trans, View v) {
super(viewPortHandler, xIndex, yValue, trans, v);
}
@Override
public void run() {
float[] pts = new float[] {
xIndex, yValue
};
pts[0] = xValue;
pts[1] = yValue;
mTrans.pointValuesToPixel(pts);
mViewPortHandler.centerViewPort(pts, view);

View file

@ -223,13 +223,46 @@ public class ViewPortHandler {
Matrix save = new Matrix();
save.set(mMatrixTouch);
// Log.i(LOG_TAG, "Zooming, x: " + x + ", y: " + y);
save.postScale(scaleX, scaleY, x, y);
return save;
}
public Matrix zoom(float scaleX, float scaleY) {
Matrix save = new Matrix();
save.set(mMatrixTouch);
save.setScale(scaleX, scaleY);
return save;
}
/**
* Zooms by the specified scale factors and moves the viewport to the specified values.
*
* @param scaleX
* @param scaleY
* @param pts
* @param view
* @return
*/
public synchronized Matrix zoomAndCenter(float scaleX, float scaleY, float[] pts, final View view) {
Matrix save = new Matrix();
save.set(mMatrixTouch);
final float x = pts[0] - offsetLeft();
final float y = pts[1] - offsetTop();
PointF center = getContentCenter();
save.setScale(scaleX, scaleY, center.x, center.y);
save.postTranslate(-x, -y);
return refresh(save, view, true);
}
/**
* Resets all zooming and dragging and makes the chart fit exactly it's
* bounds.