1 2 package com.github.mikephil.charting.charts; 3 4 import android.annotation.SuppressLint; 5 import android.annotation.TargetApi; 6 import android.content.Context; 7 import android.graphics.Canvas; 8 import android.graphics.Color; 9 import android.graphics.Matrix; 10 import android.graphics.Paint; 11 import android.graphics.Paint.Style; 12 import android.graphics.RectF; 13 import android.util.AttributeSet; 14 import android.util.Log; 15 import android.view.MotionEvent; 16 17 import com.github.mikephil.charting.components.XAxis.XAxisPosition; 18 import com.github.mikephil.charting.components.YAxis; 19 import com.github.mikephil.charting.components.YAxis.AxisDependency; 20 import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; 21 import com.github.mikephil.charting.data.Entry; 22 import com.github.mikephil.charting.highlight.ChartHighlighter; 23 import com.github.mikephil.charting.highlight.Highlight; 24 import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; 25 import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; 26 import com.github.mikephil.charting.jobs.AnimatedMoveViewJob; 27 import com.github.mikephil.charting.jobs.AnimatedZoomJob; 28 import com.github.mikephil.charting.jobs.MoveViewJob; 29 import com.github.mikephil.charting.jobs.ZoomJob; 30 import com.github.mikephil.charting.listener.BarLineChartTouchListener; 31 import com.github.mikephil.charting.listener.OnDrawListener; 32 import com.github.mikephil.charting.renderer.XAxisRenderer; 33 import com.github.mikephil.charting.renderer.YAxisRenderer; 34 import com.github.mikephil.charting.utils.MPPointD; 35 import com.github.mikephil.charting.utils.MPPointF; 36 import com.github.mikephil.charting.utils.Transformer; 37 import com.github.mikephil.charting.utils.Utils; 38 39 /** 40 * Base-class of LineChart, BarChart, ScatterChart and CandleStickChart. 41 * 42 * @author Philipp Jahoda 43 */ 44 @SuppressLint("RtlHardcoded") 45 public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<? extends 46 IBarLineScatterCandleBubbleDataSet<? extends Entry>>> 47 extends Chart<T> implements BarLineScatterCandleBubbleDataProvider { 48 49 /** 50 * the maximum number of entries to which values will be drawn 51 * (entry numbers greater than this value will cause value-labels to disappear) 52 */ 53 protected int mMaxVisibleCount = 100; 54 55 /** 56 * flag that indicates if auto scaling on the y axis is enabled 57 */ 58 protected boolean mAutoScaleMinMaxEnabled = false; 59 60 /** 61 * flag that indicates if pinch-zoom is enabled. if true, both x and y axis 62 * can be scaled with 2 fingers, if false, x and y axis can be scaled 63 * separately 64 */ 65 protected boolean mPinchZoomEnabled = false; 66 67 /** 68 * flag that indicates if double tap zoom is enabled or not 69 */ 70 protected boolean mDoubleTapToZoomEnabled = true; 71 72 /** 73 * flag that indicates if highlighting per dragging over a fully zoomed out 74 * chart is enabled 75 */ 76 protected boolean mHighlightPerDragEnabled = true; 77 78 /** 79 * if true, dragging is enabled for the chart 80 */ 81 private boolean mDragXEnabled = true; 82 private boolean mDragYEnabled = true; 83 84 private boolean mScaleXEnabled = true; 85 private boolean mScaleYEnabled = true; 86 87 /** 88 * paint object for the (by default) lightgrey background of the grid 89 */ 90 protected Paint mGridBackgroundPaint; 91 92 protected Paint mBorderPaint; 93 94 /** 95 * flag indicating if the grid background should be drawn or not 96 */ 97 protected boolean mDrawGridBackground = false; 98 99 protected boolean mDrawBorders = false; 100 101 protected boolean mClipValuesToContent = false; 102 103 protected boolean mClipDataToContent = true; 104 105 /** 106 * Sets the minimum offset (padding) around the chart, defaults to 15 107 */ 108 protected float mMinOffset = 15.f; 109 110 /** 111 * flag indicating if the chart should stay at the same position after a rotation. Default is false. 112 */ 113 protected boolean mKeepPositionOnRotation = false; 114 115 /** 116 * the listener for user drawing on the chart 117 */ 118 protected OnDrawListener mDrawListener; 119 120 /** 121 * the object representing the labels on the left y-axis 122 */ 123 protected YAxis mAxisLeft; 124 125 /** 126 * the object representing the labels on the right y-axis 127 */ 128 protected YAxis mAxisRight; 129 130 protected YAxisRenderer mAxisRendererLeft; 131 protected YAxisRenderer mAxisRendererRight; 132 133 protected Transformer mLeftAxisTransformer; 134 protected Transformer mRightAxisTransformer; 135 136 protected XAxisRenderer mXAxisRenderer; 137 138 // /** the approximator object used for data filtering */ 139 // private Approximator mApproximator; 140 BarLineChartBase(Context context, AttributeSet attrs, int defStyle)141 public BarLineChartBase(Context context, AttributeSet attrs, int defStyle) { 142 super(context, attrs, defStyle); 143 } 144 BarLineChartBase(Context context, AttributeSet attrs)145 public BarLineChartBase(Context context, AttributeSet attrs) { 146 super(context, attrs); 147 } 148 BarLineChartBase(Context context)149 public BarLineChartBase(Context context) { 150 super(context); 151 } 152 153 @Override init()154 protected void init() { 155 super.init(); 156 157 mAxisLeft = new YAxis(AxisDependency.LEFT); 158 mAxisRight = new YAxis(AxisDependency.RIGHT); 159 160 mLeftAxisTransformer = new Transformer(mViewPortHandler); 161 mRightAxisTransformer = new Transformer(mViewPortHandler); 162 163 mAxisRendererLeft = new YAxisRenderer(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); 164 mAxisRendererRight = new YAxisRenderer(mViewPortHandler, mAxisRight, mRightAxisTransformer); 165 166 mXAxisRenderer = new XAxisRenderer(mViewPortHandler, mXAxis, mLeftAxisTransformer); 167 168 setHighlighter(new ChartHighlighter(this)); 169 170 mChartTouchListener = new BarLineChartTouchListener(this, mViewPortHandler.getMatrixTouch(), 3f); 171 172 mGridBackgroundPaint = new Paint(); 173 mGridBackgroundPaint.setStyle(Style.FILL); 174 // mGridBackgroundPaint.setColor(Color.WHITE); 175 mGridBackgroundPaint.setColor(Color.rgb(240, 240, 240)); // light 176 // grey 177 178 mBorderPaint = new Paint(); 179 mBorderPaint.setStyle(Style.STROKE); 180 mBorderPaint.setColor(Color.BLACK); 181 mBorderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); 182 } 183 184 // for performance tracking 185 private long totalTime = 0; 186 private long drawCycles = 0; 187 188 @Override onDraw(Canvas canvas)189 protected void onDraw(Canvas canvas) { 190 super.onDraw(canvas); 191 192 if (mData == null) 193 return; 194 195 long starttime = System.currentTimeMillis(); 196 197 // execute all drawing commands 198 drawGridBackground(canvas); 199 200 if (mAutoScaleMinMaxEnabled) { 201 autoScale(); 202 } 203 204 if (mAxisLeft.isEnabled()) 205 mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted()); 206 207 if (mAxisRight.isEnabled()) 208 mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted()); 209 210 if (mXAxis.isEnabled()) 211 mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); 212 213 mXAxisRenderer.renderAxisLine(canvas); 214 mAxisRendererLeft.renderAxisLine(canvas); 215 mAxisRendererRight.renderAxisLine(canvas); 216 217 if (mXAxis.isDrawGridLinesBehindDataEnabled()) 218 mXAxisRenderer.renderGridLines(canvas); 219 220 if (mAxisLeft.isDrawGridLinesBehindDataEnabled()) 221 mAxisRendererLeft.renderGridLines(canvas); 222 223 if (mAxisRight.isDrawGridLinesBehindDataEnabled()) 224 mAxisRendererRight.renderGridLines(canvas); 225 226 if (mXAxis.isEnabled() && mXAxis.isDrawLimitLinesBehindDataEnabled()) 227 mXAxisRenderer.renderLimitLines(canvas); 228 229 if (mAxisLeft.isEnabled() && mAxisLeft.isDrawLimitLinesBehindDataEnabled()) 230 mAxisRendererLeft.renderLimitLines(canvas); 231 232 if (mAxisRight.isEnabled() && mAxisRight.isDrawLimitLinesBehindDataEnabled()) 233 mAxisRendererRight.renderLimitLines(canvas); 234 235 int clipRestoreCount = canvas.save(); 236 237 if (isClipDataToContentEnabled()) { 238 // make sure the data cannot be drawn outside the content-rect 239 canvas.clipRect(mViewPortHandler.getContentRect()); 240 } 241 242 mRenderer.drawData(canvas); 243 244 if (!mXAxis.isDrawGridLinesBehindDataEnabled()) 245 mXAxisRenderer.renderGridLines(canvas); 246 247 if (!mAxisLeft.isDrawGridLinesBehindDataEnabled()) 248 mAxisRendererLeft.renderGridLines(canvas); 249 250 if (!mAxisRight.isDrawGridLinesBehindDataEnabled()) 251 mAxisRendererRight.renderGridLines(canvas); 252 253 // if highlighting is enabled 254 if (valuesToHighlight()) 255 mRenderer.drawHighlighted(canvas, mIndicesToHighlight); 256 257 // Removes clipping rectangle 258 canvas.restoreToCount(clipRestoreCount); 259 260 mRenderer.drawExtras(canvas); 261 262 if (mXAxis.isEnabled() && !mXAxis.isDrawLimitLinesBehindDataEnabled()) 263 mXAxisRenderer.renderLimitLines(canvas); 264 265 if (mAxisLeft.isEnabled() && !mAxisLeft.isDrawLimitLinesBehindDataEnabled()) 266 mAxisRendererLeft.renderLimitLines(canvas); 267 268 if (mAxisRight.isEnabled() && !mAxisRight.isDrawLimitLinesBehindDataEnabled()) 269 mAxisRendererRight.renderLimitLines(canvas); 270 271 mXAxisRenderer.renderAxisLabels(canvas); 272 mAxisRendererLeft.renderAxisLabels(canvas); 273 mAxisRendererRight.renderAxisLabels(canvas); 274 275 if (isClipValuesToContentEnabled()) { 276 clipRestoreCount = canvas.save(); 277 canvas.clipRect(mViewPortHandler.getContentRect()); 278 279 mRenderer.drawValues(canvas); 280 281 canvas.restoreToCount(clipRestoreCount); 282 } else { 283 mRenderer.drawValues(canvas); 284 } 285 286 mLegendRenderer.renderLegend(canvas); 287 288 drawDescription(canvas); 289 290 drawMarkers(canvas); 291 292 if (mLogEnabled) { 293 long drawtime = (System.currentTimeMillis() - starttime); 294 totalTime += drawtime; 295 drawCycles += 1; 296 long average = totalTime / drawCycles; 297 Log.i(LOG_TAG, "Drawtime: " + drawtime + " ms, average: " + average + " ms, cycles: " 298 + drawCycles); 299 } 300 } 301 302 /** 303 * RESET PERFORMANCE TRACKING FIELDS 304 */ resetTracking()305 public void resetTracking() { 306 totalTime = 0; 307 drawCycles = 0; 308 } 309 prepareValuePxMatrix()310 protected void prepareValuePxMatrix() { 311 312 if (mLogEnabled) 313 Log.i(LOG_TAG, "Preparing Value-Px Matrix, xmin: " + mXAxis.mAxisMinimum + ", xmax: " 314 + mXAxis.mAxisMaximum + ", xdelta: " + mXAxis.mAxisRange); 315 316 mRightAxisTransformer.prepareMatrixValuePx(mXAxis.mAxisMinimum, 317 mXAxis.mAxisRange, 318 mAxisRight.mAxisRange, 319 mAxisRight.mAxisMinimum); 320 mLeftAxisTransformer.prepareMatrixValuePx(mXAxis.mAxisMinimum, 321 mXAxis.mAxisRange, 322 mAxisLeft.mAxisRange, 323 mAxisLeft.mAxisMinimum); 324 } 325 prepareOffsetMatrix()326 protected void prepareOffsetMatrix() { 327 328 mRightAxisTransformer.prepareMatrixOffset(mAxisRight.isInverted()); 329 mLeftAxisTransformer.prepareMatrixOffset(mAxisLeft.isInverted()); 330 } 331 332 @Override notifyDataSetChanged()333 public void notifyDataSetChanged() { 334 335 if (mData == null) { 336 if (mLogEnabled) 337 Log.i(LOG_TAG, "Preparing... DATA NOT SET."); 338 return; 339 } else { 340 if (mLogEnabled) 341 Log.i(LOG_TAG, "Preparing..."); 342 } 343 344 if (mRenderer != null) 345 mRenderer.initBuffers(); 346 347 calcMinMax(); 348 349 mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted()); 350 mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted()); 351 mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); 352 353 if (mLegend != null) 354 mLegendRenderer.computeLegend(mData); 355 356 calculateOffsets(); 357 } 358 359 /** 360 * Performs auto scaling of the axis by recalculating the minimum and maximum y-values based on the entries currently in view. 361 */ autoScale()362 protected void autoScale() { 363 364 final float fromX = getLowestVisibleX(); 365 final float toX = getHighestVisibleX(); 366 367 mData.calcMinMaxY(fromX, toX); 368 369 mXAxis.calculate(mData.getXMin(), mData.getXMax()); 370 371 // calculate axis range (min / max) according to provided data 372 373 if (mAxisLeft.isEnabled()) 374 mAxisLeft.calculate(mData.getYMin(AxisDependency.LEFT), 375 mData.getYMax(AxisDependency.LEFT)); 376 377 if (mAxisRight.isEnabled()) 378 mAxisRight.calculate(mData.getYMin(AxisDependency.RIGHT), 379 mData.getYMax(AxisDependency.RIGHT)); 380 381 calculateOffsets(); 382 } 383 384 @Override calcMinMax()385 protected void calcMinMax() { 386 387 mXAxis.calculate(mData.getXMin(), mData.getXMax()); 388 389 // calculate axis range (min / max) according to provided data 390 mAxisLeft.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); 391 mAxisRight.calculate(mData.getYMin(AxisDependency.RIGHT), mData.getYMax(AxisDependency 392 .RIGHT)); 393 } 394 calculateLegendOffsets(RectF offsets)395 protected void calculateLegendOffsets(RectF offsets) { 396 397 offsets.left = 0.f; 398 offsets.right = 0.f; 399 offsets.top = 0.f; 400 offsets.bottom = 0.f; 401 402 if (mLegend == null || !mLegend.isEnabled() || mLegend.isDrawInsideEnabled()) 403 return; 404 405 switch (mLegend.getOrientation()) { 406 case VERTICAL: 407 408 switch (mLegend.getHorizontalAlignment()) { 409 case LEFT: 410 offsets.left += Math.min(mLegend.mNeededWidth, 411 mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) 412 + mLegend.getXOffset(); 413 break; 414 415 case RIGHT: 416 offsets.right += Math.min(mLegend.mNeededWidth, 417 mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) 418 + mLegend.getXOffset(); 419 break; 420 421 case CENTER: 422 423 switch (mLegend.getVerticalAlignment()) { 424 case TOP: 425 offsets.top += Math.min(mLegend.mNeededHeight, 426 mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) 427 + mLegend.getYOffset(); 428 break; 429 430 case BOTTOM: 431 offsets.bottom += Math.min(mLegend.mNeededHeight, 432 mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) 433 + mLegend.getYOffset(); 434 break; 435 436 default: 437 break; 438 } 439 } 440 441 break; 442 443 case HORIZONTAL: 444 445 switch (mLegend.getVerticalAlignment()) { 446 case TOP: 447 offsets.top += Math.min(mLegend.mNeededHeight, 448 mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) 449 + mLegend.getYOffset(); 450 451 452 break; 453 454 case BOTTOM: 455 offsets.bottom += Math.min(mLegend.mNeededHeight, 456 mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) 457 + mLegend.getYOffset(); 458 459 460 break; 461 462 default: 463 break; 464 } 465 break; 466 } 467 } 468 469 private RectF mOffsetsBuffer = new RectF(); 470 471 @Override calculateOffsets()472 public void calculateOffsets() { 473 474 if (!mCustomViewPortEnabled) { 475 476 float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; 477 478 calculateLegendOffsets(mOffsetsBuffer); 479 480 offsetLeft += mOffsetsBuffer.left; 481 offsetTop += mOffsetsBuffer.top; 482 offsetRight += mOffsetsBuffer.right; 483 offsetBottom += mOffsetsBuffer.bottom; 484 485 // offsets for y-labels 486 if (mAxisLeft.needsOffset()) { 487 offsetLeft += mAxisLeft.getRequiredWidthSpace(mAxisRendererLeft 488 .getPaintAxisLabels()); 489 } 490 491 if (mAxisRight.needsOffset()) { 492 offsetRight += mAxisRight.getRequiredWidthSpace(mAxisRendererRight 493 .getPaintAxisLabels()); 494 } 495 496 if (mXAxis.isEnabled() && mXAxis.isDrawLabelsEnabled()) { 497 498 float xLabelHeight = mXAxis.mLabelRotatedHeight + mXAxis.getYOffset(); 499 500 // offsets for x-labels 501 if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { 502 503 offsetBottom += xLabelHeight; 504 505 } else if (mXAxis.getPosition() == XAxisPosition.TOP) { 506 507 offsetTop += xLabelHeight; 508 509 } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { 510 511 offsetBottom += xLabelHeight; 512 offsetTop += xLabelHeight; 513 } 514 } 515 516 offsetTop += getExtraTopOffset(); 517 offsetRight += getExtraRightOffset(); 518 offsetBottom += getExtraBottomOffset(); 519 offsetLeft += getExtraLeftOffset(); 520 521 float minOffset = Utils.convertDpToPixel(mMinOffset); 522 523 mViewPortHandler.restrainViewPort( 524 Math.max(minOffset, offsetLeft), 525 Math.max(minOffset, offsetTop), 526 Math.max(minOffset, offsetRight), 527 Math.max(minOffset, offsetBottom)); 528 529 if (mLogEnabled) { 530 Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop 531 + ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom); 532 Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString()); 533 } 534 } 535 536 prepareOffsetMatrix(); 537 prepareValuePxMatrix(); 538 } 539 540 /** 541 * draws the grid background 542 */ drawGridBackground(Canvas c)543 protected void drawGridBackground(Canvas c) { 544 545 if (mDrawGridBackground) { 546 547 // draw the grid background 548 c.drawRect(mViewPortHandler.getContentRect(), mGridBackgroundPaint); 549 } 550 551 if (mDrawBorders) { 552 c.drawRect(mViewPortHandler.getContentRect(), mBorderPaint); 553 } 554 } 555 556 /** 557 * Returns the Transformer class that contains all matrices and is 558 * responsible for transforming values into pixels on the screen and 559 * backwards. 560 * 561 * @return 562 */ getTransformer(AxisDependency which)563 public Transformer getTransformer(AxisDependency which) { 564 if (which == AxisDependency.LEFT) 565 return mLeftAxisTransformer; 566 else 567 return mRightAxisTransformer; 568 } 569 570 @Override onTouchEvent(MotionEvent event)571 public boolean onTouchEvent(MotionEvent event) { 572 super.onTouchEvent(event); 573 574 if (mChartTouchListener == null || mData == null) 575 return false; 576 577 // check if touch gestures are enabled 578 if (!mTouchEnabled) 579 return false; 580 else 581 return mChartTouchListener.onTouch(this, event); 582 } 583 584 @Override computeScroll()585 public void computeScroll() { 586 587 if (mChartTouchListener instanceof BarLineChartTouchListener) 588 ((BarLineChartTouchListener) mChartTouchListener).computeScroll(); 589 } 590 591 /** 592 * ################ ################ ################ ################ 593 */ 594 /** 595 * CODE BELOW THIS RELATED TO SCALING AND GESTURES AND MODIFICATION OF THE 596 * VIEWPORT 597 */ 598 599 protected Matrix mZoomMatrixBuffer = new Matrix(); 600 601 /** 602 * Zooms in by 1.4f, into the charts center. 603 */ zoomIn()604 public void zoomIn() { 605 606 MPPointF center = mViewPortHandler.getContentCenter(); 607 608 mViewPortHandler.zoomIn(center.x, -center.y, mZoomMatrixBuffer); 609 mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); 610 611 MPPointF.recycleInstance(center); 612 613 // Range might have changed, which means that Y-axis labels 614 // could have changed in size, affecting Y-axis size. 615 // So we need to recalculate offsets. 616 calculateOffsets(); 617 postInvalidate(); 618 } 619 620 /** 621 * Zooms out by 0.7f, from the charts center. 622 */ zoomOut()623 public void zoomOut() { 624 625 MPPointF center = mViewPortHandler.getContentCenter(); 626 627 mViewPortHandler.zoomOut(center.x, -center.y, mZoomMatrixBuffer); 628 mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); 629 630 MPPointF.recycleInstance(center); 631 632 // Range might have changed, which means that Y-axis labels 633 // could have changed in size, affecting Y-axis size. 634 // So we need to recalculate offsets. 635 calculateOffsets(); 636 postInvalidate(); 637 } 638 639 /** 640 * Zooms out to original size. 641 */ resetZoom()642 public void resetZoom() { 643 644 mViewPortHandler.resetZoom(mZoomMatrixBuffer); 645 mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); 646 647 // Range might have changed, which means that Y-axis labels 648 // could have changed in size, affecting Y-axis size. 649 // So we need to recalculate offsets. 650 calculateOffsets(); 651 postInvalidate(); 652 } 653 654 /** 655 * Zooms in or out by the given scale factor. x and y are the coordinates 656 * (in pixels) of the zoom center. 657 * 658 * @param scaleX if < 1f --> zoom out, if > 1f --> zoom in 659 * @param scaleY if < 1f --> zoom out, if > 1f --> zoom in 660 * @param x 661 * @param y 662 */ zoom(float scaleX, float scaleY, float x, float y)663 public void zoom(float scaleX, float scaleY, float x, float y) { 664 665 mViewPortHandler.zoom(scaleX, scaleY, x, -y, mZoomMatrixBuffer); 666 mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); 667 668 // Range might have changed, which means that Y-axis labels 669 // could have changed in size, affecting Y-axis size. 670 // So we need to recalculate offsets. 671 calculateOffsets(); 672 postInvalidate(); 673 } 674 675 /** 676 * Zooms in or out by the given scale factor. 677 * x and y are the values (NOT PIXELS) of the zoom center.. 678 * 679 * @param scaleX 680 * @param scaleY 681 * @param xValue 682 * @param yValue 683 * @param axis the axis relative to which the zoom should take place 684 */ zoom(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis)685 public void zoom(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) { 686 687 Runnable job = ZoomJob.getInstance(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this); 688 addViewportJob(job); 689 } 690 691 /** 692 * Zooms to the center of the chart with the given scale factor. 693 * 694 * @param scaleX 695 * @param scaleY 696 */ zoomToCenter(float scaleX, float scaleY)697 public void zoomToCenter(float scaleX, float scaleY) { 698 699 MPPointF center = getCenterOffsets(); 700 701 Matrix save = mZoomMatrixBuffer; 702 mViewPortHandler.zoom(scaleX, scaleY, center.x, -center.y, save); 703 mViewPortHandler.refresh(save, this, false); 704 } 705 706 /** 707 * Zooms by the specified scale factor to the specified values on the specified axis. 708 * 709 * @param scaleX 710 * @param scaleY 711 * @param xValue 712 * @param yValue 713 * @param axis 714 * @param duration 715 */ 716 @TargetApi(11) zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, long duration)717 public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, 718 long duration) { 719 720 MPPointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); 721 722 Runnable job = AnimatedZoomJob.getInstance(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis 723 .mAxisRange, scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), 724 xValue, yValue, (float) origin.x, (float) origin.y, duration); 725 addViewportJob(job); 726 727 MPPointD.recycleInstance(origin); 728 } 729 730 protected Matrix mFitScreenMatrixBuffer = new Matrix(); 731 732 /** 733 * Resets all zooming and dragging and makes the chart fit exactly it's 734 * bounds. 735 */ fitScreen()736 public void fitScreen() { 737 Matrix save = mFitScreenMatrixBuffer; 738 mViewPortHandler.fitScreen(save); 739 mViewPortHandler.refresh(save, this, false); 740 741 calculateOffsets(); 742 postInvalidate(); 743 } 744 745 /** 746 * Sets the minimum scale factor value to which can be zoomed out. 1f = 747 * fitScreen 748 * 749 * @param scaleX 750 * @param scaleY 751 */ setScaleMinima(float scaleX, float scaleY)752 public void setScaleMinima(float scaleX, float scaleY) { 753 mViewPortHandler.setMinimumScaleX(scaleX); 754 mViewPortHandler.setMinimumScaleY(scaleY); 755 } 756 757 /** 758 * Sets the size of the area (range on the x-axis) that should be maximum 759 * visible at once (no further zooming out allowed). If this is e.g. set to 760 * 10, no more than a range of 10 on the x-axis can be viewed at once without 761 * scrolling. 762 * 763 * @param maxXRange The maximum visible range of x-values. 764 */ setVisibleXRangeMaximum(float maxXRange)765 public void setVisibleXRangeMaximum(float maxXRange) { 766 float xScale = mXAxis.mAxisRange / (maxXRange); 767 mViewPortHandler.setMinimumScaleX(xScale); 768 } 769 770 /** 771 * Sets the size of the area (range on the x-axis) that should be minimum 772 * visible at once (no further zooming in allowed). If this is e.g. set to 773 * 10, no less than a range of 10 on the x-axis can be viewed at once without 774 * scrolling. 775 * 776 * @param minXRange The minimum visible range of x-values. 777 */ setVisibleXRangeMinimum(float minXRange)778 public void setVisibleXRangeMinimum(float minXRange) { 779 float xScale = mXAxis.mAxisRange / (minXRange); 780 mViewPortHandler.setMaximumScaleX(xScale); 781 } 782 783 /** 784 * Limits the maximum and minimum x range that can be visible by pinching and zooming. e.g. minRange=10, maxRange=100 the 785 * smallest range to be displayed at once is 10, and no more than a range of 100 values can be viewed at once without 786 * scrolling 787 * 788 * @param minXRange 789 * @param maxXRange 790 */ setVisibleXRange(float minXRange, float maxXRange)791 public void setVisibleXRange(float minXRange, float maxXRange) { 792 float minScale = mXAxis.mAxisRange / minXRange; 793 float maxScale = mXAxis.mAxisRange / maxXRange; 794 mViewPortHandler.setMinMaxScaleX(minScale, maxScale); 795 } 796 797 /** 798 * Sets the size of the area (range on the y-axis) that should be maximum 799 * visible at once. 800 * 801 * @param maxYRange the maximum visible range on the y-axis 802 * @param axis the axis for which this limit should apply 803 */ setVisibleYRangeMaximum(float maxYRange, AxisDependency axis)804 public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { 805 float yScale = getAxisRange(axis) / maxYRange; 806 mViewPortHandler.setMinimumScaleY(yScale); 807 } 808 809 /** 810 * Sets the size of the area (range on the y-axis) that should be minimum visible at once, no further zooming in possible. 811 * 812 * @param minYRange 813 * @param axis the axis for which this limit should apply 814 */ setVisibleYRangeMinimum(float minYRange, AxisDependency axis)815 public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { 816 float yScale = getAxisRange(axis) / minYRange; 817 mViewPortHandler.setMaximumScaleY(yScale); 818 } 819 820 /** 821 * Limits the maximum and minimum y range that can be visible by pinching and zooming. 822 * 823 * @param minYRange 824 * @param maxYRange 825 * @param axis 826 */ setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis)827 public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { 828 float minScale = getAxisRange(axis) / minYRange; 829 float maxScale = getAxisRange(axis) / maxYRange; 830 mViewPortHandler.setMinMaxScaleY(minScale, maxScale); 831 } 832 833 834 /** 835 * Moves the left side of the current viewport to the specified x-position. 836 * This also refreshes the chart by calling invalidate(). 837 * 838 * @param xValue 839 */ moveViewToX(float xValue)840 public void moveViewToX(float xValue) { 841 842 Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, 0f, 843 getTransformer(AxisDependency.LEFT), this); 844 845 addViewportJob(job); 846 } 847 848 /** 849 * This will move the left side of the current viewport to the specified 850 * x-value on the x-axis, and center the viewport to the specified y value on the y-axis. 851 * This also refreshes the chart by calling invalidate(). 852 * 853 * @param xValue 854 * @param yValue 855 * @param axis - which axis should be used as a reference for the y-axis 856 */ moveViewTo(float xValue, float yValue, AxisDependency axis)857 public void moveViewTo(float xValue, float yValue, AxisDependency axis) { 858 859 float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); 860 861 Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, 862 getTransformer(axis), this); 863 864 addViewportJob(job); 865 } 866 867 /** 868 * This will move the left side of the current viewport to the specified x-value 869 * and center the viewport to the y value animated. 870 * This also refreshes the chart by calling invalidate(). 871 * 872 * @param xValue 873 * @param yValue 874 * @param axis 875 * @param duration the duration of the animation in milliseconds 876 */ 877 @TargetApi(11) moveViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration)878 public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { 879 880 MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); 881 882 float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); 883 884 Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, 885 getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); 886 887 addViewportJob(job); 888 889 MPPointD.recycleInstance(bounds); 890 } 891 892 /** 893 * Centers the viewport to the specified y value on the y-axis. 894 * This also refreshes the chart by calling invalidate(). 895 * 896 * @param yValue 897 * @param axis - which axis should be used as a reference for the y-axis 898 */ centerViewToY(float yValue, AxisDependency axis)899 public void centerViewToY(float yValue, AxisDependency axis) { 900 901 float valsInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); 902 903 Runnable job = MoveViewJob.getInstance(mViewPortHandler, 0f, yValue + valsInView / 2f, 904 getTransformer(axis), this); 905 906 addViewportJob(job); 907 } 908 909 /** 910 * This will move the center of the current viewport to the specified 911 * x and y value. 912 * This also refreshes the chart by calling invalidate(). 913 * 914 * @param xValue 915 * @param yValue 916 * @param axis - which axis should be used as a reference for the y axis 917 */ centerViewTo(float xValue, float yValue, AxisDependency axis)918 public void centerViewTo(float xValue, float yValue, AxisDependency axis) { 919 920 float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); 921 float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); 922 923 Runnable job = MoveViewJob.getInstance(mViewPortHandler, 924 xValue - xInView / 2f, yValue + yInView / 2f, 925 getTransformer(axis), this); 926 927 addViewportJob(job); 928 } 929 930 /** 931 * This will move the center of the current viewport to the specified 932 * x and y value animated. 933 * 934 * @param xValue 935 * @param yValue 936 * @param axis 937 * @param duration the duration of the animation in milliseconds 938 */ 939 @TargetApi(11) centerViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration)940 public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { 941 942 MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); 943 944 float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); 945 float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); 946 947 Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, 948 xValue - xInView / 2f, yValue + yInView / 2f, 949 getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); 950 951 addViewportJob(job); 952 953 MPPointD.recycleInstance(bounds); 954 } 955 956 /** 957 * flag that indicates if a custom viewport offset has been set 958 */ 959 private boolean mCustomViewPortEnabled = false; 960 961 /** 962 * Sets custom offsets for the current ViewPort (the offsets on the sides of 963 * the actual chart window). Setting this will prevent the chart from 964 * automatically calculating it's offsets. Use resetViewPortOffsets() to 965 * undo this. ONLY USE THIS WHEN YOU KNOW WHAT YOU ARE DOING, else use 966 * setExtraOffsets(...). 967 * 968 * @param left 969 * @param top 970 * @param right 971 * @param bottom 972 */ setViewPortOffsets(final float left, final float top, final float right, final float bottom)973 public void setViewPortOffsets(final float left, final float top, 974 final float right, final float bottom) { 975 976 mCustomViewPortEnabled = true; 977 post(new Runnable() { 978 979 @Override 980 public void run() { 981 982 mViewPortHandler.restrainViewPort(left, top, right, bottom); 983 prepareOffsetMatrix(); 984 prepareValuePxMatrix(); 985 } 986 }); 987 } 988 989 /** 990 * Resets all custom offsets set via setViewPortOffsets(...) method. Allows 991 * the chart to again calculate all offsets automatically. 992 */ resetViewPortOffsets()993 public void resetViewPortOffsets() { 994 mCustomViewPortEnabled = false; 995 calculateOffsets(); 996 } 997 998 /** 999 * ################ ################ ################ ################ 1000 */ 1001 /** CODE BELOW IS GETTERS AND SETTERS */ 1002 1003 /** 1004 * Returns the range of the specified axis. 1005 * 1006 * @param axis 1007 * @return 1008 */ getAxisRange(AxisDependency axis)1009 protected float getAxisRange(AxisDependency axis) { 1010 if (axis == AxisDependency.LEFT) 1011 return mAxisLeft.mAxisRange; 1012 else 1013 return mAxisRight.mAxisRange; 1014 } 1015 1016 /** 1017 * Sets the OnDrawListener 1018 * 1019 * @param drawListener 1020 */ setOnDrawListener(OnDrawListener drawListener)1021 public void setOnDrawListener(OnDrawListener drawListener) { 1022 this.mDrawListener = drawListener; 1023 } 1024 1025 /** 1026 * Gets the OnDrawListener. May be null. 1027 * 1028 * @return 1029 */ getDrawListener()1030 public OnDrawListener getDrawListener() { 1031 return mDrawListener; 1032 } 1033 1034 protected float[] mGetPositionBuffer = new float[2]; 1035 1036 /** 1037 * Returns a recyclable MPPointF instance. 1038 * Returns the position (in pixels) the provided Entry has inside the chart 1039 * view or null, if the provided Entry is null. 1040 * 1041 * @param e 1042 * @return 1043 */ getPosition(Entry e, AxisDependency axis)1044 public MPPointF getPosition(Entry e, AxisDependency axis) { 1045 1046 if (e == null) 1047 return null; 1048 1049 mGetPositionBuffer[0] = e.getX(); 1050 mGetPositionBuffer[1] = e.getY(); 1051 1052 getTransformer(axis).pointValuesToPixel(mGetPositionBuffer); 1053 1054 return MPPointF.getInstance(mGetPositionBuffer[0], mGetPositionBuffer[1]); 1055 } 1056 1057 /** 1058 * sets the number of maximum visible drawn values on the chart only active 1059 * when setDrawValues() is enabled 1060 * 1061 * @param count 1062 */ setMaxVisibleValueCount(int count)1063 public void setMaxVisibleValueCount(int count) { 1064 this.mMaxVisibleCount = count; 1065 } 1066 getMaxVisibleCount()1067 public int getMaxVisibleCount() { 1068 return mMaxVisibleCount; 1069 } 1070 1071 /** 1072 * Set this to true to allow highlighting per dragging over the chart 1073 * surface when it is fully zoomed out. Default: true 1074 * 1075 * @param enabled 1076 */ setHighlightPerDragEnabled(boolean enabled)1077 public void setHighlightPerDragEnabled(boolean enabled) { 1078 mHighlightPerDragEnabled = enabled; 1079 } 1080 isHighlightPerDragEnabled()1081 public boolean isHighlightPerDragEnabled() { 1082 return mHighlightPerDragEnabled; 1083 } 1084 1085 /** 1086 * Sets the color for the background of the chart-drawing area (everything 1087 * behind the grid lines). 1088 * 1089 * @param color 1090 */ setGridBackgroundColor(int color)1091 public void setGridBackgroundColor(int color) { 1092 mGridBackgroundPaint.setColor(color); 1093 } 1094 1095 /** 1096 * Set this to true to enable dragging (moving the chart with the finger) 1097 * for the chart (this does not effect scaling). 1098 * 1099 * @param enabled 1100 */ setDragEnabled(boolean enabled)1101 public void setDragEnabled(boolean enabled) { 1102 this.mDragXEnabled = enabled; 1103 this.mDragYEnabled = enabled; 1104 } 1105 1106 /** 1107 * Returns true if dragging is enabled for the chart, false if not. 1108 * 1109 * @return 1110 */ isDragEnabled()1111 public boolean isDragEnabled() { 1112 return mDragXEnabled || mDragYEnabled; 1113 } 1114 1115 /** 1116 * Set this to true to enable dragging on the X axis 1117 * 1118 * @param enabled 1119 */ setDragXEnabled(boolean enabled)1120 public void setDragXEnabled(boolean enabled) { 1121 this.mDragXEnabled = enabled; 1122 } 1123 1124 /** 1125 * Returns true if dragging on the X axis is enabled for the chart, false if not. 1126 * 1127 * @return 1128 */ isDragXEnabled()1129 public boolean isDragXEnabled() { 1130 return mDragXEnabled; 1131 } 1132 1133 /** 1134 * Set this to true to enable dragging on the Y axis 1135 * 1136 * @param enabled 1137 */ setDragYEnabled(boolean enabled)1138 public void setDragYEnabled(boolean enabled) { 1139 this.mDragYEnabled = enabled; 1140 } 1141 1142 /** 1143 * Returns true if dragging on the Y axis is enabled for the chart, false if not. 1144 * 1145 * @return 1146 */ isDragYEnabled()1147 public boolean isDragYEnabled() { 1148 return mDragYEnabled; 1149 } 1150 1151 /** 1152 * Set this to true to enable scaling (zooming in and out by gesture) for 1153 * the chart (this does not effect dragging) on both X- and Y-Axis. 1154 * 1155 * @param enabled 1156 */ setScaleEnabled(boolean enabled)1157 public void setScaleEnabled(boolean enabled) { 1158 this.mScaleXEnabled = enabled; 1159 this.mScaleYEnabled = enabled; 1160 } 1161 setScaleXEnabled(boolean enabled)1162 public void setScaleXEnabled(boolean enabled) { 1163 mScaleXEnabled = enabled; 1164 } 1165 setScaleYEnabled(boolean enabled)1166 public void setScaleYEnabled(boolean enabled) { 1167 mScaleYEnabled = enabled; 1168 } 1169 isScaleXEnabled()1170 public boolean isScaleXEnabled() { 1171 return mScaleXEnabled; 1172 } 1173 isScaleYEnabled()1174 public boolean isScaleYEnabled() { 1175 return mScaleYEnabled; 1176 } 1177 1178 /** 1179 * Set this to true to enable zooming in by double-tap on the chart. 1180 * Default: enabled 1181 * 1182 * @param enabled 1183 */ setDoubleTapToZoomEnabled(boolean enabled)1184 public void setDoubleTapToZoomEnabled(boolean enabled) { 1185 mDoubleTapToZoomEnabled = enabled; 1186 } 1187 1188 /** 1189 * Returns true if zooming via double-tap is enabled false if not. 1190 * 1191 * @return 1192 */ isDoubleTapToZoomEnabled()1193 public boolean isDoubleTapToZoomEnabled() { 1194 return mDoubleTapToZoomEnabled; 1195 } 1196 1197 /** 1198 * set this to true to draw the grid background, false if not 1199 * 1200 * @param enabled 1201 */ setDrawGridBackground(boolean enabled)1202 public void setDrawGridBackground(boolean enabled) { 1203 mDrawGridBackground = enabled; 1204 } 1205 1206 /** 1207 * When enabled, the borders rectangle will be rendered. 1208 * If this is enabled, there is no point drawing the axis-lines of x- and y-axis. 1209 * 1210 * @param enabled 1211 */ setDrawBorders(boolean enabled)1212 public void setDrawBorders(boolean enabled) { 1213 mDrawBorders = enabled; 1214 } 1215 1216 /** 1217 * When enabled, the borders rectangle will be rendered. 1218 * If this is enabled, there is no point drawing the axis-lines of x- and y-axis. 1219 * 1220 * @return 1221 */ isDrawBordersEnabled()1222 public boolean isDrawBordersEnabled() { 1223 return mDrawBorders; 1224 } 1225 1226 /** 1227 * When enabled, the values will be clipped to contentRect, 1228 * otherwise they can bleed outside the content rect. 1229 * 1230 * @param enabled 1231 */ setClipValuesToContent(boolean enabled)1232 public void setClipValuesToContent(boolean enabled) { 1233 mClipValuesToContent = enabled; 1234 } 1235 1236 /** 1237 * When disabled, the data and/or highlights will not be clipped to contentRect. Disabling this option can 1238 * be useful, when the data lies fully within the content rect, but is drawn in such a way (such as thick lines) 1239 * that there is unwanted clipping. 1240 * 1241 * @param enabled 1242 */ setClipDataToContent(boolean enabled)1243 public void setClipDataToContent(boolean enabled) { 1244 mClipDataToContent = enabled; 1245 } 1246 1247 /** 1248 * When enabled, the values will be clipped to contentRect, 1249 * otherwise they can bleed outside the content rect. 1250 * 1251 * @return 1252 */ isClipValuesToContentEnabled()1253 public boolean isClipValuesToContentEnabled() { 1254 return mClipValuesToContent; 1255 } 1256 1257 /** 1258 * When disabled, the data and/or highlights will not be clipped to contentRect. Disabling this option can 1259 * be useful, when the data lies fully within the content rect, but is drawn in such a way (such as thick lines) 1260 * that there is unwanted clipping. 1261 * 1262 * @return 1263 */ isClipDataToContentEnabled()1264 public boolean isClipDataToContentEnabled() { 1265 return mClipDataToContent; 1266 } 1267 1268 /** 1269 * Sets the width of the border lines in dp. 1270 * 1271 * @param width 1272 */ setBorderWidth(float width)1273 public void setBorderWidth(float width) { 1274 mBorderPaint.setStrokeWidth(Utils.convertDpToPixel(width)); 1275 } 1276 1277 /** 1278 * Sets the color of the chart border lines. 1279 * 1280 * @param color 1281 */ setBorderColor(int color)1282 public void setBorderColor(int color) { 1283 mBorderPaint.setColor(color); 1284 } 1285 1286 /** 1287 * Gets the minimum offset (padding) around the chart, defaults to 15.f 1288 */ getMinOffset()1289 public float getMinOffset() { 1290 return mMinOffset; 1291 } 1292 1293 /** 1294 * Sets the minimum offset (padding) around the chart, defaults to 15.f 1295 */ setMinOffset(float minOffset)1296 public void setMinOffset(float minOffset) { 1297 mMinOffset = minOffset; 1298 } 1299 1300 /** 1301 * Returns true if keeping the position on rotation is enabled and false if not. 1302 */ isKeepPositionOnRotation()1303 public boolean isKeepPositionOnRotation() { 1304 return mKeepPositionOnRotation; 1305 } 1306 1307 /** 1308 * Sets whether the chart should keep its position (zoom / scroll) after a rotation (orientation change) 1309 */ setKeepPositionOnRotation(boolean keepPositionOnRotation)1310 public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { 1311 mKeepPositionOnRotation = keepPositionOnRotation; 1312 } 1313 1314 /** 1315 * Returns a recyclable MPPointD instance 1316 * Returns the x and y values in the chart at the given touch point 1317 * (encapsulated in a MPPointD). This method transforms pixel coordinates to 1318 * coordinates / values in the chart. This is the opposite method to 1319 * getPixelForValues(...). 1320 * 1321 * @param x 1322 * @param y 1323 * @return 1324 */ getValuesByTouchPoint(float x, float y, AxisDependency axis)1325 public MPPointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { 1326 MPPointD result = MPPointD.getInstance(0, 0); 1327 getValuesByTouchPoint(x, y, axis, result); 1328 return result; 1329 } 1330 getValuesByTouchPoint(float x, float y, AxisDependency axis, MPPointD outputPoint)1331 public void getValuesByTouchPoint(float x, float y, AxisDependency axis, MPPointD outputPoint) { 1332 getTransformer(axis).getValuesByTouchPoint(x, y, outputPoint); 1333 } 1334 1335 /** 1336 * Returns a recyclable MPPointD instance 1337 * Transforms the given chart values into pixels. This is the opposite 1338 * method to getValuesByTouchPoint(...). 1339 * 1340 * @param x 1341 * @param y 1342 * @return 1343 */ getPixelForValues(float x, float y, AxisDependency axis)1344 public MPPointD getPixelForValues(float x, float y, AxisDependency axis) { 1345 return getTransformer(axis).getPixelForValues(x, y); 1346 } 1347 1348 /** 1349 * returns the Entry object displayed at the touched position of the chart 1350 * 1351 * @param x 1352 * @param y 1353 * @return 1354 */ getEntryByTouchPoint(float x, float y)1355 public Entry getEntryByTouchPoint(float x, float y) { 1356 Highlight h = getHighlightByTouchPoint(x, y); 1357 if (h != null) { 1358 return mData.getEntryForHighlight(h); 1359 } 1360 return null; 1361 } 1362 1363 /** 1364 * returns the DataSet object displayed at the touched position of the chart 1365 * 1366 * @param x 1367 * @param y 1368 * @return 1369 */ getDataSetByTouchPoint(float x, float y)1370 public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float y) { 1371 Highlight h = getHighlightByTouchPoint(x, y); 1372 if (h != null) { 1373 return mData.getDataSetByIndex(h.getDataSetIndex()); 1374 } 1375 return null; 1376 } 1377 1378 /** 1379 * buffer for storing lowest visible x point 1380 */ 1381 protected MPPointD posForGetLowestVisibleX = MPPointD.getInstance(0, 0); 1382 1383 /** 1384 * Returns the lowest x-index (value on the x-axis) that is still visible on 1385 * the chart. 1386 * 1387 * @return 1388 */ 1389 @Override getLowestVisibleX()1390 public float getLowestVisibleX() { 1391 getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), 1392 mViewPortHandler.contentBottom(), posForGetLowestVisibleX); 1393 float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.x); 1394 return result; 1395 } 1396 1397 /** 1398 * buffer for storing highest visible x point 1399 */ 1400 protected MPPointD posForGetHighestVisibleX = MPPointD.getInstance(0, 0); 1401 1402 /** 1403 * Returns the highest x-index (value on the x-axis) that is still visible 1404 * on the chart. 1405 * 1406 * @return 1407 */ 1408 @Override getHighestVisibleX()1409 public float getHighestVisibleX() { 1410 getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentRight(), 1411 mViewPortHandler.contentBottom(), posForGetHighestVisibleX); 1412 float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.x); 1413 return result; 1414 } 1415 1416 /** 1417 * Returns the range visible on the x-axis. 1418 * 1419 * @return 1420 */ getVisibleXRange()1421 public float getVisibleXRange() { 1422 return Math.abs(getHighestVisibleX() - getLowestVisibleX()); 1423 } 1424 1425 /** 1426 * returns the current x-scale factor 1427 */ getScaleX()1428 public float getScaleX() { 1429 if (mViewPortHandler == null) 1430 return 1f; 1431 else 1432 return mViewPortHandler.getScaleX(); 1433 } 1434 1435 /** 1436 * returns the current y-scale factor 1437 */ getScaleY()1438 public float getScaleY() { 1439 if (mViewPortHandler == null) 1440 return 1f; 1441 else 1442 return mViewPortHandler.getScaleY(); 1443 } 1444 1445 /** 1446 * if the chart is fully zoomed out, return true 1447 * 1448 * @return 1449 */ isFullyZoomedOut()1450 public boolean isFullyZoomedOut() { 1451 return mViewPortHandler.isFullyZoomedOut(); 1452 } 1453 1454 /** 1455 * Returns the left y-axis object. In the horizontal bar-chart, this is the 1456 * top axis. 1457 * 1458 * @return 1459 */ getAxisLeft()1460 public YAxis getAxisLeft() { 1461 return mAxisLeft; 1462 } 1463 1464 /** 1465 * Returns the right y-axis object. In the horizontal bar-chart, this is the 1466 * bottom axis. 1467 * 1468 * @return 1469 */ getAxisRight()1470 public YAxis getAxisRight() { 1471 return mAxisRight; 1472 } 1473 1474 /** 1475 * Returns the y-axis object to the corresponding AxisDependency. In the 1476 * horizontal bar-chart, LEFT == top, RIGHT == BOTTOM 1477 * 1478 * @param axis 1479 * @return 1480 */ getAxis(AxisDependency axis)1481 public YAxis getAxis(AxisDependency axis) { 1482 if (axis == AxisDependency.LEFT) 1483 return mAxisLeft; 1484 else 1485 return mAxisRight; 1486 } 1487 1488 @Override isInverted(AxisDependency axis)1489 public boolean isInverted(AxisDependency axis) { 1490 return getAxis(axis).isInverted(); 1491 } 1492 1493 /** 1494 * If set to true, both x and y axis can be scaled simultaneously with 2 fingers, if false, 1495 * x and y axis can be scaled separately. default: false 1496 * 1497 * @param enabled 1498 */ setPinchZoom(boolean enabled)1499 public void setPinchZoom(boolean enabled) { 1500 mPinchZoomEnabled = enabled; 1501 } 1502 1503 /** 1504 * returns true if pinch-zoom is enabled, false if not 1505 * 1506 * @return 1507 */ isPinchZoomEnabled()1508 public boolean isPinchZoomEnabled() { 1509 return mPinchZoomEnabled; 1510 } 1511 1512 /** 1513 * Set an offset in dp that allows the user to drag the chart over it's 1514 * bounds on the x-axis. 1515 * 1516 * @param offset 1517 */ setDragOffsetX(float offset)1518 public void setDragOffsetX(float offset) { 1519 mViewPortHandler.setDragOffsetX(offset); 1520 } 1521 1522 /** 1523 * Set an offset in dp that allows the user to drag the chart over it's 1524 * bounds on the y-axis. 1525 * 1526 * @param offset 1527 */ setDragOffsetY(float offset)1528 public void setDragOffsetY(float offset) { 1529 mViewPortHandler.setDragOffsetY(offset); 1530 } 1531 1532 /** 1533 * Returns true if both drag offsets (x and y) are zero or smaller. 1534 * 1535 * @return 1536 */ hasNoDragOffset()1537 public boolean hasNoDragOffset() { 1538 return mViewPortHandler.hasNoDragOffset(); 1539 } 1540 getRendererXAxis()1541 public XAxisRenderer getRendererXAxis() { 1542 return mXAxisRenderer; 1543 } 1544 1545 /** 1546 * Sets a custom XAxisRenderer and overrides the existing (default) one. 1547 * 1548 * @param xAxisRenderer 1549 */ setXAxisRenderer(XAxisRenderer xAxisRenderer)1550 public void setXAxisRenderer(XAxisRenderer xAxisRenderer) { 1551 mXAxisRenderer = xAxisRenderer; 1552 } 1553 getRendererLeftYAxis()1554 public YAxisRenderer getRendererLeftYAxis() { 1555 return mAxisRendererLeft; 1556 } 1557 1558 /** 1559 * Sets a custom axis renderer for the left axis and overwrites the existing one. 1560 * 1561 * @param rendererLeftYAxis 1562 */ setRendererLeftYAxis(YAxisRenderer rendererLeftYAxis)1563 public void setRendererLeftYAxis(YAxisRenderer rendererLeftYAxis) { 1564 mAxisRendererLeft = rendererLeftYAxis; 1565 } 1566 getRendererRightYAxis()1567 public YAxisRenderer getRendererRightYAxis() { 1568 return mAxisRendererRight; 1569 } 1570 1571 /** 1572 * Sets a custom axis renderer for the right acis and overwrites the existing one. 1573 * 1574 * @param rendererRightYAxis 1575 */ setRendererRightYAxis(YAxisRenderer rendererRightYAxis)1576 public void setRendererRightYAxis(YAxisRenderer rendererRightYAxis) { 1577 mAxisRendererRight = rendererRightYAxis; 1578 } 1579 1580 @Override getYChartMax()1581 public float getYChartMax() { 1582 return Math.max(mAxisLeft.mAxisMaximum, mAxisRight.mAxisMaximum); 1583 } 1584 1585 @Override getYChartMin()1586 public float getYChartMin() { 1587 return Math.min(mAxisLeft.mAxisMinimum, mAxisRight.mAxisMinimum); 1588 } 1589 1590 /** 1591 * Returns true if either the left or the right or both axes are inverted. 1592 * 1593 * @return 1594 */ isAnyAxisInverted()1595 public boolean isAnyAxisInverted() { 1596 if (mAxisLeft.isInverted()) 1597 return true; 1598 if (mAxisRight.isInverted()) 1599 return true; 1600 return false; 1601 } 1602 1603 /** 1604 * Flag that indicates if auto scaling on the y axis is enabled. This is 1605 * especially interesting for charts displaying financial data. 1606 * 1607 * @param enabled the y axis automatically adjusts to the min and max y 1608 * values of the current x axis range whenever the viewport 1609 * changes 1610 */ setAutoScaleMinMaxEnabled(boolean enabled)1611 public void setAutoScaleMinMaxEnabled(boolean enabled) { 1612 mAutoScaleMinMaxEnabled = enabled; 1613 } 1614 1615 /** 1616 * @return true if auto scaling on the y axis is enabled. 1617 * @default false 1618 */ isAutoScaleMinMaxEnabled()1619 public boolean isAutoScaleMinMaxEnabled() { 1620 return mAutoScaleMinMaxEnabled; 1621 } 1622 1623 @Override setPaint(Paint p, int which)1624 public void setPaint(Paint p, int which) { 1625 super.setPaint(p, which); 1626 1627 switch (which) { 1628 case PAINT_GRID_BACKGROUND: 1629 mGridBackgroundPaint = p; 1630 break; 1631 } 1632 } 1633 1634 @Override getPaint(int which)1635 public Paint getPaint(int which) { 1636 Paint p = super.getPaint(which); 1637 if (p != null) 1638 return p; 1639 1640 switch (which) { 1641 case PAINT_GRID_BACKGROUND: 1642 return mGridBackgroundPaint; 1643 } 1644 1645 return null; 1646 } 1647 1648 protected float[] mOnSizeChangedBuffer = new float[2]; 1649 1650 @Override onSizeChanged(int w, int h, int oldw, int oldh)1651 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 1652 1653 // Saving current position of chart. 1654 mOnSizeChangedBuffer[0] = mOnSizeChangedBuffer[1] = 0; 1655 1656 if (mKeepPositionOnRotation) { 1657 mOnSizeChangedBuffer[0] = mViewPortHandler.contentLeft(); 1658 mOnSizeChangedBuffer[1] = mViewPortHandler.contentTop(); 1659 getTransformer(AxisDependency.LEFT).pixelsToValue(mOnSizeChangedBuffer); 1660 } 1661 1662 //Superclass transforms chart. 1663 super.onSizeChanged(w, h, oldw, oldh); 1664 1665 if (mKeepPositionOnRotation) { 1666 1667 //Restoring old position of chart. 1668 getTransformer(AxisDependency.LEFT).pointValuesToPixel(mOnSizeChangedBuffer); 1669 mViewPortHandler.centerViewPort(mOnSizeChangedBuffer, this); 1670 } else { 1671 mViewPortHandler.refresh(mViewPortHandler.getMatrixTouch(), this, true); 1672 } 1673 } 1674 } 1675