• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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