public void computeScroll() {

    if (mDecelerationVelocity.x == 0.f && mDecelerationVelocity.y == 0.f)
      return; // There's no deceleration in progress

    final long currentTime = AnimationUtils.currentAnimationTimeMillis();

    mDecelerationVelocity.x *= mChart.getDragDecelerationFrictionCoef();
    mDecelerationVelocity.y *= mChart.getDragDecelerationFrictionCoef();

    final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f;

    float distanceX = mDecelerationVelocity.x * timeInterval;
    float distanceY = mDecelerationVelocity.y * timeInterval;

    mDecelerationCurrentPoint.x += distanceX;
    mDecelerationCurrentPoint.y += distanceY;

    MotionEvent event =
        MotionEvent.obtain(
            currentTime,
            currentTime,
            MotionEvent.ACTION_MOVE,
            mDecelerationCurrentPoint.x,
            mDecelerationCurrentPoint.y,
            0);
    performDrag(event);
    event.recycle();
    mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, false);

    mDecelerationLastTime = currentTime;

    if (Math.abs(mDecelerationVelocity.x) >= 0.01 || Math.abs(mDecelerationVelocity.y) >= 0.01)
      Utils.postInvalidateOnAnimation(
          mChart); // This causes computeScroll to fire, recommended for this by Google
    else {
      // Range might have changed, which means that Y-axis labels
      // could have changed in size, affecting Y-axis size.
      // So we need to recalculate offsets.
      mChart.calculateOffsets();
      mChart.postInvalidate();

      stopDeceleration();
    }
  }
  @SuppressLint("ClickableViewAccessibility")
  @Override
  public boolean onTouch(View v, MotionEvent event) {

    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(event);

    if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
      if (mVelocityTracker != null) {
        mVelocityTracker.recycle();
        mVelocityTracker = null;
      }
    }

    if (mTouchMode == NONE) {
      mGestureDetector.onTouchEvent(event);
    }

    if (!mChart.isDragEnabled() && (!mChart.isScaleXEnabled() && !mChart.isScaleYEnabled()))
      return true;

    // Handle touch events here...
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
        stopDeceleration();

        saveTouchStart(event);

        break;
      case MotionEvent.ACTION_POINTER_DOWN:
        if (event.getPointerCount() >= 2) {

          mChart.disableScroll();

          saveTouchStart(event);

          // get the distance between the pointers on the x-axis
          mSavedXDist = getXDist(event);

          // get the distance between the pointers on the y-axis
          mSavedYDist = getYDist(event);

          // get the total distance between the pointers
          mSavedDist = spacing(event);

          if (mSavedDist > 10f) {

            if (mChart.isPinchZoomEnabled()) {
              mTouchMode = PINCH_ZOOM;
            } else {
              if (mSavedXDist > mSavedYDist) mTouchMode = X_ZOOM;
              else mTouchMode = Y_ZOOM;
            }
          }

          // determine the touch-pointer center
          midPoint(mTouchPointCenter, event);
        }
        break;
      case MotionEvent.ACTION_MOVE:
        if (mTouchMode == DRAG) {

          mChart.disableScroll();
          performDrag(event);

        } else if (mTouchMode == X_ZOOM || mTouchMode == Y_ZOOM || mTouchMode == PINCH_ZOOM) {

          mChart.disableScroll();

          if (mChart.isScaleXEnabled() || mChart.isScaleYEnabled()) performZoom(event);

        } else if (mTouchMode == NONE
            && Math.abs(
                    distance(event.getX(), mTouchStartPoint.x, event.getY(), mTouchStartPoint.y))
                > 5f) {

          if (mChart.hasNoDragOffset()) {

            if (!mChart.isFullyZoomedOut() && mChart.isDragEnabled()) mTouchMode = DRAG;
            else {
              if (mChart.isHighlightPerDragEnabled()) performHighlightDrag(event);
            }

          } else if (mChart.isDragEnabled()) {
            mTouchMode = DRAG;
          }
        }
        break;

      case MotionEvent.ACTION_UP:
        final VelocityTracker velocityTracker = mVelocityTracker;
        final int pointerId = event.getPointerId(0);
        velocityTracker.computeCurrentVelocity(1000, Utils.getMaximumFlingVelocity());
        final float velocityY = velocityTracker.getYVelocity(pointerId);
        final float velocityX = velocityTracker.getXVelocity(pointerId);

        if (Math.abs(velocityX) > Utils.getMinimumFlingVelocity()
            || Math.abs(velocityY) > Utils.getMinimumFlingVelocity()) {

          if (mTouchMode == DRAG && mChart.isDragDecelerationEnabled()) {

            stopDeceleration();

            mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis();
            mDecelerationCurrentPoint = new PointF(event.getX(), event.getY());
            mDecelerationVelocity = new PointF(velocityX, velocityY);

            Utils.postInvalidateOnAnimation(
                mChart); // This causes computeScroll to fire, recommended for this by Google
          }
        }

        if (mTouchMode == X_ZOOM
            || mTouchMode == Y_ZOOM
            || mTouchMode == PINCH_ZOOM
            || mTouchMode == POST_ZOOM) {

          // Range might have changed, which means that Y-axis labels
          // could have changed in size, affecting Y-axis size.
          // So we need to recalculate offsets.
          mChart.calculateOffsets();
          mChart.postInvalidate();
        }

        mTouchMode = NONE;
        mChart.enableScroll();

        if (mVelocityTracker != null) {
          mVelocityTracker.recycle();
          mVelocityTracker = null;
        }

        break;
      case MotionEvent.ACTION_POINTER_UP:
        Utils.velocityTrackerPointerUpCleanUpIfNecessary(event, mVelocityTracker);

        mTouchMode = POST_ZOOM;
        break;

      case MotionEvent.ACTION_CANCEL:
        mTouchMode = NONE;

        break;
    }

    // Perform the transformation, update the chart
    // if (needsRefresh())
    mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true);

    return true; // indicate event was handled
  }