Skip to content

Commit 1e59b09

Browse files
committed
Deceleration after dragging in Line and Pie/Radar charts
1 parent fe9f904 commit 1e59b09

7 files changed

Lines changed: 395 additions & 11 deletions

File tree

‎MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ protected void onCreate(Bundle savedInstanceState) {
5959
setContentView(R.layout.activity_main);
6060

6161
// initialize the utilities
62-
Utils.init(getResources());
62+
Utils.init(this);
6363

6464
ArrayList<ContentItem> objects = new ArrayList<ContentItem>();
6565

‎MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,16 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleData<? exte
9595

9696
protected boolean mDrawBorders = false;
9797

98+
/** If set to true, chart continues to scroll after touch up */
99+
private boolean mDragDecelarationEnabled = true;
100+
101+
/**
102+
* Decelaration friction coefficient in [0 ; 1] interval, higher values indicate that
103+
* speed will decrease slowly, for example if it set to 0, it will stop immediately.
104+
* 1 is an invalid value, and will be converted to 0 automatically.
105+
*/
106+
private float mDragDecelarationFrictionCoef = 0.9f;
107+
98108
/** the listener for user drawing on the chart */
99109
protected OnDrawListener mDrawListener;
100110

@@ -539,6 +549,13 @@ public boolean onTouchEvent(MotionEvent event) {
539549
return mListener.onTouch(this, event);
540550
}
541551

552+
@Override
553+
public void computeScroll() {
554+
555+
if (mListener instanceof BarLineChartTouchListener)
556+
((BarLineChartTouchListener)mListener).computeScroll();
557+
}
558+
542559
/**
543560
* ################ ################ ################ ################
544561
*/
@@ -968,6 +985,46 @@ public void setBorderColor(int color) {
968985
mBorderPaint.setColor(color);
969986
}
970987

988+
/**
989+
* If set to true, chart continues to scroll after touch up
990+
*
991+
* default: true
992+
*/
993+
public boolean isDragDecelarationEnabled() {
994+
return mDragDecelarationEnabled;
995+
}
996+
997+
/**
998+
* If set to true, chart continues to scroll after touch up
999+
*
1000+
* @param enabled
1001+
*/
1002+
public void setDragDecelarationEnabled(boolean enabled) {
1003+
mDragDecelarationEnabled = enabled;
1004+
}
1005+
1006+
/**
1007+
* Returns drag deceleration friction coefficient
1008+
* @return
1009+
*/
1010+
public float getDragDecelarationFrictionCoef() {
1011+
return mDragDecelarationFrictionCoef;
1012+
}
1013+
1014+
/**+
1015+
* Decelaration friction coefficient in [0 ; 1] interval, higher values indicate that
1016+
* speed will decrease slowly, for example if it set to 0, it will stop immediately.
1017+
* 1 is an invalid value, and will be converted to 0 automatically.
1018+
*
1019+
* @param newValue
1020+
*/
1021+
public void setDragDecelarationFrictionCoef(float newValue) {
1022+
if (newValue < 0.f || newValue >= 1.f)
1023+
newValue = 0.f;
1024+
1025+
mDragDecelarationFrictionCoef = newValue;
1026+
}
1027+
9711028
/**
9721029
* Returns the Highlight object (contains x-index and DataSet index) of the
9731030
* selected value at the given touch point inside the Line-, Scatter-, or

‎MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java‎

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends DataSet<?
4141
/** flag that indicates if rotation is enabled or not */
4242
protected boolean mRotateEnabled = true;
4343

44+
/** If set to true, chart continues to scroll after touch up */
45+
private boolean mDragDecelarationEnabled = true;
46+
47+
/**
48+
* Decelaration friction coefficient in [0 ; 1] interval, higher values indicate that
49+
* speed will decrease slowly, for example if it set to 0, it will stop immediately,
50+
* if set to 1, it will scroll with constant speed, until the last point
51+
*/
52+
private float mDragDecelarationFrictionCoef = 0.9f;
53+
4454
/** the pie- and radarchart touchlistener */
4555
protected OnTouchListener mListener;
4656

@@ -77,6 +87,13 @@ public boolean onTouchEvent(MotionEvent event) {
7787
return super.onTouchEvent(event);
7888
}
7989

90+
@Override
91+
public void computeScroll() {
92+
93+
if (mListener instanceof PieRadarChartTouchListener)
94+
((PieRadarChartTouchListener)mListener).computeScroll();
95+
}
96+
8097
@Override
8198
public void notifyDataSetChanged() {
8299
if (mDataNotSet)
@@ -203,7 +220,7 @@ protected void calculateOffsets() {
203220
* @param x
204221
* @param y
205222
*/
206-
public void setStartAngle(float x, float y) {
223+
public void setGestureStartAngle(float x, float y) {
207224

208225
mStartAngle = getAngleForPoint(x, y);
209226

@@ -330,8 +347,10 @@ public float distanceToCenter(float x, float y) {
330347
*/
331348
public void setRotationAngle(float angle) {
332349

333-
angle = (int) Math.abs(angle % 360);
334-
mRotationAngle = angle;
350+
while (angle < 0.f)
351+
angle += 360.f;
352+
353+
mRotationAngle = angle % 360;
335354
}
336355

337356
/**
@@ -362,6 +381,46 @@ public boolean isRotationEnabled() {
362381
return mRotateEnabled;
363382
}
364383

384+
/**
385+
* If set to true, chart continues to scroll after touch up
386+
*
387+
* default: true
388+
*/
389+
public boolean isDragDecelarationEnabled() {
390+
return mDragDecelarationEnabled;
391+
}
392+
393+
/**
394+
* If set to true, chart continues to scroll after touch up
395+
*
396+
* @param enabled
397+
*/
398+
public void setDragDecelarationEnabled(boolean enabled) {
399+
mDragDecelarationEnabled = enabled;
400+
}
401+
402+
/**
403+
* Returns drag deceleration friction coefficient
404+
* @return
405+
*/
406+
public float getDragDecelarationFrictionCoef() {
407+
return mDragDecelarationFrictionCoef;
408+
}
409+
410+
/**+
411+
* Decelaration friction coefficient in [0 ; 1] interval, higher values indicate that
412+
* speed will decrease slowly, for example if it set to 0, it will stop immediately,
413+
* if set to 1, it will scroll with constant speed, until the last point
414+
*
415+
* @param newValue
416+
*/
417+
public void setDragDecelarationFrictionCoef(float newValue) {
418+
if (newValue < 0.f || newValue >= 1.f)
419+
newValue = 0.f;
420+
421+
mDragDecelarationFrictionCoef = newValue;
422+
}
423+
365424
/**
366425
* returns the diameter of the pie- or radar-chart
367426
*

‎MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
import android.annotation.SuppressLint;
55
import android.graphics.Matrix;
66
import android.graphics.PointF;
7+
import android.os.Build;
78
import android.util.Log;
89
import android.view.GestureDetector;
910
import android.view.GestureDetector.SimpleOnGestureListener;
1011
import android.view.MotionEvent;
12+
import android.view.VelocityTracker;
1113
import android.view.View;
1214
import android.view.View.OnTouchListener;
15+
import android.view.animation.AnimationUtils;
1316

1417
import com.github.mikephil.charting.charts.BarLineChartBase;
1518
import com.github.mikephil.charting.charts.HorizontalBarChart;
@@ -18,6 +21,7 @@
1821
import com.github.mikephil.charting.data.DataSet;
1922
import com.github.mikephil.charting.data.Entry;
2023
import com.github.mikephil.charting.utils.Highlight;
24+
import com.github.mikephil.charting.utils.Utils;
2125
import com.github.mikephil.charting.utils.ViewPortHandler;
2226

2327
/**
@@ -70,6 +74,13 @@ public class BarLineChartTouchListener<T extends BarLineChartBase<? extends BarL
7074
/** the gesturedetector used for detecting taps and longpresses, ... */
7175
private GestureDetector mGestureDetector;
7276

77+
/** used for tracking velocity of dragging */
78+
private VelocityTracker mVelocityTracker;
79+
80+
private long mDecelarationLastTime = 0;
81+
private PointF mDecelarationCurrentPoint = new PointF();
82+
private PointF mDecelarationVelocity = new PointF();
83+
7384
public BarLineChartTouchListener(T chart, Matrix touchMatrix) {
7485
this.mChart = chart;
7586
this.mMatrix = touchMatrix;
@@ -81,6 +92,18 @@ public BarLineChartTouchListener(T chart, Matrix touchMatrix) {
8192
@Override
8293
public boolean onTouch(View v, MotionEvent event) {
8394

95+
if (mVelocityTracker == null) {
96+
mVelocityTracker = VelocityTracker.obtain();
97+
}
98+
mVelocityTracker.addMovement(event);
99+
100+
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
101+
if (mVelocityTracker != null) {
102+
mVelocityTracker.recycle();
103+
mVelocityTracker = null;
104+
}
105+
}
106+
84107
if (mTouchMode == NONE) {
85108
mGestureDetector.onTouchEvent(event);
86109
}
@@ -93,7 +116,10 @@ public boolean onTouch(View v, MotionEvent event) {
93116

94117
case MotionEvent.ACTION_DOWN:
95118

119+
stopDeceleration();
120+
96121
saveTouchStart(event);
122+
97123
break;
98124
case MotionEvent.ACTION_POINTER_DOWN:
99125

@@ -162,12 +188,48 @@ public boolean onTouch(View v, MotionEvent event) {
162188
break;
163189

164190
case MotionEvent.ACTION_UP:
191+
192+
final VelocityTracker velocityTracker = mVelocityTracker;
193+
final int pointerId = event.getPointerId(0);
194+
velocityTracker.computeCurrentVelocity(1000, Utils.getMaximumFlingVelocity());
195+
final float velocityY = velocityTracker.getYVelocity(pointerId);
196+
final float velocityX = velocityTracker.getXVelocity(pointerId);
197+
198+
if (Math.abs(velocityX) > Utils.getMinimumFlingVelocity() ||
199+
Math.abs(velocityY) > Utils.getMinimumFlingVelocity()) {
200+
201+
if (mTouchMode == DRAG && mChart.isDragDecelarationEnabled()) {
202+
203+
stopDeceleration();
204+
205+
mDecelarationLastTime = AnimationUtils.currentAnimationTimeMillis();
206+
mDecelarationCurrentPoint = new PointF(event.getX(), event.getY());
207+
mDecelarationVelocity = new PointF(velocityX, velocityY);
208+
209+
Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google
210+
}
211+
}
212+
165213
mTouchMode = NONE;
166214
mChart.enableScroll();
215+
216+
if (mVelocityTracker != null) {
217+
mVelocityTracker.recycle();
218+
mVelocityTracker = null;
219+
}
220+
167221
break;
168222
case MotionEvent.ACTION_POINTER_UP:
223+
Utils.velocityTrackerPointerUpCleanUpIfNecessary(event, mVelocityTracker);
224+
169225
mTouchMode = POST_ZOOM;
170226
break;
227+
228+
case MotionEvent.ACTION_CANCEL:
229+
230+
mTouchMode = NONE;
231+
232+
break;
171233
}
172234

173235
// Perform the transformation, update the chart
@@ -513,4 +575,38 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve
513575

514576
return super.onFling(e1, e2, velocityX, velocityY);
515577
}
578+
579+
public void stopDeceleration() {
580+
mDecelarationVelocity = new PointF(0.f, 0.f);
581+
}
582+
583+
public void computeScroll() {
584+
585+
if (mDecelarationVelocity.x == 0.f && mDecelarationVelocity.y == 0.f)
586+
return; // There's no deceleration in progress
587+
588+
final long currentTime = AnimationUtils.currentAnimationTimeMillis();
589+
590+
mDecelarationVelocity.x *= mChart.getDragDecelarationFrictionCoef();
591+
mDecelarationVelocity.y *= mChart.getDragDecelarationFrictionCoef();
592+
593+
final float timeInterval = (float)(currentTime - mDecelarationLastTime) / 1000.f;
594+
595+
float distanceX = mDecelarationVelocity.x * timeInterval;
596+
float distanceY = mDecelarationVelocity.y * timeInterval;
597+
598+
mDecelarationCurrentPoint.x += distanceX;
599+
mDecelarationCurrentPoint.y += distanceY;
600+
601+
MotionEvent event = MotionEvent.obtain(currentTime, currentTime, MotionEvent.ACTION_MOVE, mDecelarationCurrentPoint.x, mDecelarationCurrentPoint.y, 0);
602+
performDrag(event);
603+
mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, false);
604+
605+
mDecelarationLastTime = currentTime;
606+
607+
if (Math.abs(mDecelarationVelocity.x) >= 0.001 || Math.abs(mDecelarationVelocity.y) >= 0.001)
608+
Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google
609+
else
610+
stopDeceleration();
611+
}
516612
}

0 commit comments

Comments
 (0)