/**
   * Zoom in the graph
   *
   * <p>拡大表示する。
   *
   * <p>放大表示
   */
  public void zoomIn() {
    if (displayNumber > minDisplayNumber) {
      // 区分缩放方向
      if (zoomBaseLine == ZOOM_BASE_LINE_CENTER) {
        displayNumber = displayNumber - ZOOM_STEP;
        displayFrom = displayFrom + ZOOM_STEP / 2;
      } else if (zoomBaseLine == ZOOM_BASE_LINE_LEFT) {
        displayNumber = displayNumber - ZOOM_STEP;
      } else if (zoomBaseLine == ZOOM_BASE_LINE_RIGHT) {
        displayNumber = displayNumber - ZOOM_STEP;
        displayFrom = displayFrom + ZOOM_STEP;
      }

      // 处理displayNumber越界
      if (displayNumber < minDisplayNumber) {
        displayNumber = minDisplayNumber;
      }

      // 处理displayFrom越界
      if (displayFrom + displayNumber >= stickData.size()) {
        displayFrom = stickData.size() - displayNumber;
      }

      // Listener
      if (onZoomGestureListener != null) {
        onZoomGestureListener.onZoom(ZOOM_IN, displayFrom, displayNumber);
      }
    }
  }
 /**
  * initialize degrees on Y axis
  *
  * <p>Y軸の目盛を初期化
  *
  * <p>初始化Y轴的坐标值
  */
 protected void initAxisX() {
   List<String> titleX = new ArrayList<String>();
   if (null != stickData && stickData.size() > 0) {
     float average = displayNumber / this.getLongitudeNum();
     for (int i = 0; i < this.getLongitudeNum(); i++) {
       int index = (int) Math.floor(i * average);
       if (index > displayNumber - 1) {
         index = displayNumber - 1;
       }
       index = index + displayFrom;
       titleX.add(formatAxisXDegree(stickData.get(index).getDate()));
     }
     titleX.add(formatAxisXDegree(stickData.get(displayFrom + displayNumber - 1).getDate()));
   }
   super.setLongitudeTitles(titleX);
 }
 /**
  * add a new stick data to sticks
  *
  * <p>新しいスティックデータを追加する
  *
  * <p>追加一条新数据
  *
  * @param entity
  *     <p>data
  *     <p>データ
  *     <p>新数据
  */
 public void addData(IStickEntity entity) {
   if (null != entity) {
     // add
     stickData.add(entity);
     if (this.maxValue < entity.getHigh()) {
       this.maxValue = 100 + ((int) entity.getHigh()) / 100 * 100;
     }
   }
 }
  protected void drawSticks(Canvas canvas) {
    if (null == stickData) {
      return;
    }
    if (stickData.size() == 0) {
      return;
    }

    Paint mPaintStick = new Paint();
    mPaintStick.setColor(stickFillColor);

    float stickWidth = getDataQuadrantPaddingWidth() / displayNumber - stickSpacing;
    float stickX = getDataQuadrantPaddingStartX();

    for (int i = displayFrom; i < displayFrom + displayNumber; i++) {
      IMeasurable stick = stickData.get(i);
      float highY =
          (float)
              ((1f - (stick.getHigh() - minValue) / (maxValue - minValue))
                      * (getDataQuadrantPaddingHeight())
                  + getDataQuadrantPaddingStartY());
      float lowY =
          (float)
              ((1f - (stick.getLow() - minValue) / (maxValue - minValue))
                      * (getDataQuadrantPaddingHeight())
                  + getDataQuadrantPaddingStartY());

      // stick or line?
      if (stickWidth >= 2f) {
        canvas.drawRect(stickX, highY, stickX + stickWidth, lowY, mPaintStick);
      } else {
        canvas.drawLine(stickX, highY, stickX, lowY, mPaintStick);
      }

      // next x
      stickX = stickX + stickSpacing + stickWidth;
    }
  }
  /**
   * Zoom out the grid
   *
   * <p>縮小表示する。
   *
   * <p>缩小
   */
  public void zoomOut() {
    if (displayNumber < stickData.size() - 1) {
      if (displayNumber + ZOOM_STEP > stickData.size() - 1) {
        displayNumber = stickData.size() - 1;
        displayFrom = 0;
      } else {
        // 区分缩放方向
        if (zoomBaseLine == ZOOM_BASE_LINE_CENTER) {
          displayNumber = displayNumber + ZOOM_STEP;
          if (displayFrom > 1) {
            displayFrom = displayFrom - ZOOM_STEP / 2;
          } else {
            displayFrom = 0;
          }
        } else if (zoomBaseLine == ZOOM_BASE_LINE_LEFT) {
          displayNumber = displayNumber + ZOOM_STEP;
        } else if (zoomBaseLine == ZOOM_BASE_LINE_RIGHT) {
          displayNumber = displayNumber + ZOOM_STEP;
          if (displayFrom > ZOOM_STEP) {
            displayFrom = displayFrom - ZOOM_STEP;
          } else {
            displayFrom = 0;
          }
        }
      }

      if (displayFrom + displayNumber >= stickData.size()) {
        displayNumber = stickData.size() - displayFrom;
      }

      // Listener
      if (onZoomGestureListener != null) {
        onZoomGestureListener.onZoom(ZOOM_OUT, displayFrom, displayNumber);
      }
    }
  }
  /*
   * (non-Javadoc)
   *
   * @param value
   *
   * @return
   *
   * @see
   * cn.limc.androidcharts.view.GridChart#getAxisXGraduate(java.lang.Object)
   */
  @Override
  public String getAxisXGraduate(Object value) {
    float graduate = Float.valueOf(super.getAxisXGraduate(value));
    int index = (int) Math.floor(graduate * displayNumber);

    if (index >= displayNumber) {
      index = displayNumber - 1;
    } else if (index < 0) {
      index = 0;
    }

    index = index + displayFrom;

    return formatAxisXDegree(stickData.get(index).getDate());
  }
  public void moveRight() {
    int dataSize = stickData.size();
    if (displayFrom + displayNumber < dataSize - SLIP_STEP) {
      displayFrom = displayFrom + SLIP_STEP;
    } else {
      displayFrom = dataSize - displayNumber;
    }

    // 处理displayFrom越界
    if (displayFrom + displayNumber >= dataSize) {
      displayFrom = dataSize - displayNumber;
    }

    // Listener
    if (onSlipGestureListener != null) {
      onSlipGestureListener.onSlip(SLIP_DIRECTION_RIGHT, displayFrom, displayNumber);
    }
  }
  public void moveLeft() {
    int dataSize = stickData.size();

    if (displayFrom <= SLIP_STEP) {
      displayFrom = 0;
    } else if (displayFrom > SLIP_STEP) {
      displayFrom = displayFrom - SLIP_STEP;
    } else {

    }

    // 处理displayFrom越界
    if (displayFrom + displayNumber >= dataSize) {
      displayFrom = dataSize - displayNumber;
    }

    // Listener
    if (onSlipGestureListener != null) {
      onSlipGestureListener.onSlip(SLIP_DIRECTION_LEFT, displayFrom, displayNumber);
    }
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // valid
    if (!isValidTouchPoint(event.getX(), event.getY())) {
      return false;
    }

    if (null == stickData || stickData.size() == 0) {
      return false;
    }

    final float MIN_LENGTH = (super.getWidth() / 40) < 5 ? 5 : (super.getWidth() / 50);

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        // 设置拖拉模式
      case MotionEvent.ACTION_DOWN:
        touchMode = TOUCH_MODE_SINGLE;
        if (event.getPointerCount() == 1) {
          touchPoint = new PointF(event.getX(), event.getY());
          fixTouchPoint();
          if (onTouchGestureListener != null) {
            onTouchGestureListener.onTouchDown(touchPoint, getSelectedIndex());
          }
          super.postInvalidate();
          // Notifier
          super.notifyEventAll(this);
        }
        break;
      case MotionEvent.ACTION_UP:
        touchMode = TOUCH_MODE_NONE;
        startPointA = null;
        startPointB = null;
        if (event.getPointerCount() == 1) {
          touchPoint = new PointF(event.getX(), event.getY());
          fixTouchPoint();
          if (onTouchGestureListener != null) {
            onTouchGestureListener.onTouchUp(touchPoint, getSelectedIndex());
          }
          super.postInvalidate();
          // Notifier
          super.notifyEventAll(this);
        }
        break;
      case MotionEvent.ACTION_POINTER_UP:
        touchMode = TOUCH_MODE_NONE;
        startPointA = null;
        startPointB = null;
        return super.onTouchEvent(event);
        // 设置多点触摸模式
      case MotionEvent.ACTION_POINTER_DOWN:
        olddistance = calcDistance(event);
        if (olddistance > MIN_LENGTH) {
          touchMode = TOUCH_MODE_MULTI;
          startPointA = new PointF(event.getX(0), event.getY(0));
          startPointB = new PointF(event.getX(1), event.getY(1));
        }
        break;
      case MotionEvent.ACTION_MOVE:
        if (touchMode == TOUCH_MODE_MULTI) {
          newdistance = calcDistance(event);
          if (newdistance > MIN_LENGTH) {
            //					if (startPointA.x >= event.getX(0)
            //							&& startPointB.x >= event.getX(1)) {
            //						moveRight();
            //					} else if (startPointA.x <= event.getX(0)
            //							&& startPointB.x <= event.getX(1)) {
            //						moveLeft();
            //					} else {
            if (Math.abs(newdistance - olddistance) > MIN_LENGTH) {
              if (newdistance > olddistance) {
                zoomIn();
              } else {
                zoomOut();
              }
              // 重置距离
              olddistance = newdistance;
            }
            //					}
            startPointA = new PointF(event.getX(0), event.getY(0));
            startPointB = new PointF(event.getX(1), event.getY(1));

            super.postInvalidate();
            super.notifyEventAll(this);
          }
        } else {
          // 单点拖动效果
          if (event.getPointerCount() == 1) {
            float moveXdistance = Math.abs(event.getX() - touchPoint.x);
            if (moveXdistance > MIN_LENGTH) {
              if (touchPoint.x - event.getX() > 0) {
                int startindex = getSelectedIndex();
                touchPoint = new PointF(event.getX(), event.getY());
                fixTouchPoint();
                if (onTouchGestureListener != null) {
                  onTouchGestureListener.onTouchMoved(touchPoint, TOUCH_MODE_SINGLE);
                }
                int gapindex = Math.abs(getSelectedIndex() - startindex);
                for (int i = 0; i < gapindex; i++) {
                  moveRight();
                }
              } else if (touchPoint.x - event.getX() < 0) {
                int startindex = getSelectedIndex();
                touchPoint = new PointF(event.getX(), event.getY());
                fixTouchPoint();
                if (onTouchGestureListener != null) {
                  onTouchGestureListener.onTouchMoved(touchPoint, TOUCH_MODE_SINGLE);
                }
                int gapindex = Math.abs(getSelectedIndex() - startindex);
                for (int i = 0; i < gapindex; i++) {
                  moveLeft();
                }
              }
            }
            super.postInvalidate();
            super.notifyEventAll(this);
          }
        }
        break;
    }
    return true;
  }