44import android .annotation .SuppressLint ;
55import android .graphics .Matrix ;
66import android .graphics .PointF ;
7+ import android .os .Build ;
78import android .util .Log ;
89import android .view .GestureDetector ;
910import android .view .GestureDetector .SimpleOnGestureListener ;
1011import android .view .MotionEvent ;
12+ import android .view .VelocityTracker ;
1113import android .view .View ;
1214import android .view .View .OnTouchListener ;
15+ import android .view .animation .AnimationUtils ;
1316
1417import com .github .mikephil .charting .charts .BarLineChartBase ;
1518import com .github .mikephil .charting .charts .HorizontalBarChart ;
1821import com .github .mikephil .charting .data .DataSet ;
1922import com .github .mikephil .charting .data .Entry ;
2023import com .github .mikephil .charting .utils .Highlight ;
24+ import com .github .mikephil .charting .utils .Utils ;
2125import 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