• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 package com.github.mikephil.charting.renderer;
3 
4 import android.graphics.Canvas;
5 import android.graphics.Color;
6 import android.graphics.Paint;
7 import android.graphics.Paint.Align;
8 import android.graphics.Path;
9 import android.graphics.RectF;
10 
11 import com.github.mikephil.charting.components.LimitLine;
12 import com.github.mikephil.charting.components.XAxis;
13 import com.github.mikephil.charting.components.XAxis.XAxisPosition;
14 import com.github.mikephil.charting.utils.FSize;
15 import com.github.mikephil.charting.utils.MPPointD;
16 import com.github.mikephil.charting.utils.MPPointF;
17 import com.github.mikephil.charting.utils.Transformer;
18 import com.github.mikephil.charting.utils.Utils;
19 import com.github.mikephil.charting.utils.ViewPortHandler;
20 
21 import java.util.List;
22 
23 public class XAxisRenderer extends AxisRenderer {
24 
25     protected XAxis mXAxis;
26 
XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans)27     public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) {
28         super(viewPortHandler, trans, xAxis);
29 
30         this.mXAxis = xAxis;
31 
32         mAxisLabelPaint.setColor(Color.BLACK);
33         mAxisLabelPaint.setTextAlign(Align.CENTER);
34         mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f));
35     }
36 
setupGridPaint()37     protected void setupGridPaint() {
38         mGridPaint.setColor(mXAxis.getGridColor());
39         mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth());
40         mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect());
41     }
42 
43     @Override
computeAxis(float min, float max, boolean inverted)44     public void computeAxis(float min, float max, boolean inverted) {
45 
46         // calculate the starting and entry point of the y-labels (depending on
47         // zoom / contentrect bounds)
48         if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) {
49 
50             MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop());
51             MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop());
52 
53             if (inverted) {
54 
55                 min = (float) p2.x;
56                 max = (float) p1.x;
57             } else {
58 
59                 min = (float) p1.x;
60                 max = (float) p2.x;
61             }
62 
63             MPPointD.recycleInstance(p1);
64             MPPointD.recycleInstance(p2);
65         }
66 
67         computeAxisValues(min, max);
68     }
69 
70     @Override
computeAxisValues(float min, float max)71     protected void computeAxisValues(float min, float max) {
72         super.computeAxisValues(min, max);
73 
74         computeSize();
75     }
76 
computeSize()77     protected void computeSize() {
78 
79         String longest = mXAxis.getLongestLabel();
80 
81         mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
82         mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
83 
84         final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest);
85 
86         final float labelWidth = labelSize.width;
87         final float labelHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q");
88 
89         final FSize labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees(
90                 labelWidth,
91                 labelHeight,
92                 mXAxis.getLabelRotationAngle());
93 
94 
95         mXAxis.mLabelWidth = Math.round(labelWidth);
96         mXAxis.mLabelHeight = Math.round(labelHeight);
97         mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width);
98         mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height);
99 
100         FSize.recycleInstance(labelRotatedSize);
101         FSize.recycleInstance(labelSize);
102     }
103 
104     @Override
renderAxisLabels(Canvas c)105     public void renderAxisLabels(Canvas c) {
106 
107         if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled())
108             return;
109 
110         float yoffset = mXAxis.getYOffset();
111 
112         mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
113         mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
114         mAxisLabelPaint.setColor(mXAxis.getTextColor());
115 
116         MPPointF pointF = MPPointF.getInstance(0,0);
117         if (mXAxis.getPosition() == XAxisPosition.TOP) {
118             pointF.x = 0.5f;
119             pointF.y = 1.0f;
120             drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF);
121 
122         } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) {
123             pointF.x = 0.5f;
124             pointF.y = 1.0f;
125             drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight, pointF);
126 
127         } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) {
128             pointF.x = 0.5f;
129             pointF.y = 0.0f;
130             drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF);
131 
132         } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) {
133             pointF.x = 0.5f;
134             pointF.y = 0.0f;
135             drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight, pointF);
136 
137         } else { // BOTH SIDED
138             pointF.x = 0.5f;
139             pointF.y = 1.0f;
140             drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF);
141             pointF.x = 0.5f;
142             pointF.y = 0.0f;
143             drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF);
144         }
145         MPPointF.recycleInstance(pointF);
146     }
147 
148     @Override
renderAxisLine(Canvas c)149     public void renderAxisLine(Canvas c) {
150 
151         if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled())
152             return;
153 
154         mAxisLinePaint.setColor(mXAxis.getAxisLineColor());
155         mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth());
156         mAxisLinePaint.setPathEffect(mXAxis.getAxisLineDashPathEffect());
157 
158         if (mXAxis.getPosition() == XAxisPosition.TOP
159                 || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE
160                 || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) {
161             c.drawLine(mViewPortHandler.contentLeft(),
162                     mViewPortHandler.contentTop(), mViewPortHandler.contentRight(),
163                     mViewPortHandler.contentTop(), mAxisLinePaint);
164         }
165 
166         if (mXAxis.getPosition() == XAxisPosition.BOTTOM
167                 || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE
168                 || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) {
169             c.drawLine(mViewPortHandler.contentLeft(),
170                     mViewPortHandler.contentBottom(), mViewPortHandler.contentRight(),
171                     mViewPortHandler.contentBottom(), mAxisLinePaint);
172         }
173     }
174 
175     /**
176      * draws the x-labels on the specified y-position
177      *
178      * @param pos
179      */
drawLabels(Canvas c, float pos, MPPointF anchor)180     protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
181 
182         final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
183         boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled();
184 
185         float[] positions = new float[mXAxis.mEntryCount * 2];
186 
187         for (int i = 0; i < positions.length; i += 2) {
188 
189             // only fill x values
190             if (centeringEnabled) {
191                 positions[i] = mXAxis.mCenteredEntries[i / 2];
192             } else {
193                 positions[i] = mXAxis.mEntries[i / 2];
194             }
195         }
196 
197         mTrans.pointValuesToPixel(positions);
198 
199         for (int i = 0; i < positions.length; i += 2) {
200 
201             float x = positions[i];
202 
203             if (mViewPortHandler.isInBoundsX(x)) {
204 
205                 String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis);
206 
207                 if (mXAxis.isAvoidFirstLastClippingEnabled()) {
208 
209                     // avoid clipping of the last
210                     if (i / 2 == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) {
211                         float width = Utils.calcTextWidth(mAxisLabelPaint, label);
212 
213                         if (width > mViewPortHandler.offsetRight() * 2
214                                 && x + width > mViewPortHandler.getChartWidth())
215                             x -= width / 2;
216 
217                         // avoid clipping of the first
218                     } else if (i == 0) {
219 
220                         float width = Utils.calcTextWidth(mAxisLabelPaint, label);
221                         x += width / 2;
222                     }
223                 }
224 
225                 drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees);
226             }
227         }
228     }
229 
drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees)230     protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) {
231         Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees);
232     }
233     protected Path mRenderGridLinesPath = new Path();
234     protected float[] mRenderGridLinesBuffer = new float[2];
235     @Override
renderGridLines(Canvas c)236     public void renderGridLines(Canvas c) {
237 
238         if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled())
239             return;
240 
241         int clipRestoreCount = c.save();
242         c.clipRect(getGridClippingRect());
243 
244         if(mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2){
245             mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2];
246         }
247         float[] positions = mRenderGridLinesBuffer;
248 
249         for (int i = 0; i < positions.length; i += 2) {
250             positions[i] = mXAxis.mEntries[i / 2];
251             positions[i + 1] = mXAxis.mEntries[i / 2];
252         }
253 
254         mTrans.pointValuesToPixel(positions);
255 
256         setupGridPaint();
257 
258         Path gridLinePath = mRenderGridLinesPath;
259         gridLinePath.reset();
260 
261         for (int i = 0; i < positions.length; i += 2) {
262 
263             drawGridLine(c, positions[i], positions[i + 1], gridLinePath);
264         }
265 
266         c.restoreToCount(clipRestoreCount);
267     }
268 
269     protected RectF mGridClippingRect = new RectF();
270 
getGridClippingRect()271     public RectF getGridClippingRect() {
272         mGridClippingRect.set(mViewPortHandler.getContentRect());
273         mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f);
274         return mGridClippingRect;
275     }
276 
277     /**
278      * Draws the grid line at the specified position using the provided path.
279      *
280      * @param c
281      * @param x
282      * @param y
283      * @param gridLinePath
284      */
drawGridLine(Canvas c, float x, float y, Path gridLinePath)285     protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) {
286 
287         gridLinePath.moveTo(x, mViewPortHandler.contentBottom());
288         gridLinePath.lineTo(x, mViewPortHandler.contentTop());
289 
290         // draw a path because lines don't support dashing on lower android versions
291         c.drawPath(gridLinePath, mGridPaint);
292 
293         gridLinePath.reset();
294     }
295 
296     protected float[] mRenderLimitLinesBuffer = new float[2];
297     protected RectF mLimitLineClippingRect = new RectF();
298 
299     /**
300      * Draws the LimitLines associated with this axis to the screen.
301      *
302      * @param c
303      */
304     @Override
renderLimitLines(Canvas c)305     public void renderLimitLines(Canvas c) {
306 
307         List<LimitLine> limitLines = mXAxis.getLimitLines();
308 
309         if (limitLines == null || limitLines.size() <= 0)
310             return;
311 
312         float[] position = mRenderLimitLinesBuffer;
313         position[0] = 0;
314         position[1] = 0;
315 
316         for (int i = 0; i < limitLines.size(); i++) {
317 
318             LimitLine l = limitLines.get(i);
319 
320             if (!l.isEnabled())
321                 continue;
322 
323             int clipRestoreCount = c.save();
324             mLimitLineClippingRect.set(mViewPortHandler.getContentRect());
325             mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f);
326             c.clipRect(mLimitLineClippingRect);
327 
328             position[0] = l.getLimit();
329             position[1] = 0.f;
330 
331             mTrans.pointValuesToPixel(position);
332 
333             renderLimitLineLine(c, l, position);
334             renderLimitLineLabel(c, l, position, 2.f + l.getYOffset());
335 
336             c.restoreToCount(clipRestoreCount);
337         }
338     }
339 
340     float[] mLimitLineSegmentsBuffer = new float[4];
341     private Path mLimitLinePath = new Path();
342 
renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position)343     public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) {
344         mLimitLineSegmentsBuffer[0] = position[0];
345         mLimitLineSegmentsBuffer[1] = mViewPortHandler.contentTop();
346         mLimitLineSegmentsBuffer[2] = position[0];
347         mLimitLineSegmentsBuffer[3] = mViewPortHandler.contentBottom();
348 
349         mLimitLinePath.reset();
350         mLimitLinePath.moveTo(mLimitLineSegmentsBuffer[0], mLimitLineSegmentsBuffer[1]);
351         mLimitLinePath.lineTo(mLimitLineSegmentsBuffer[2], mLimitLineSegmentsBuffer[3]);
352 
353         mLimitLinePaint.setStyle(Paint.Style.STROKE);
354         mLimitLinePaint.setColor(limitLine.getLineColor());
355         mLimitLinePaint.setStrokeWidth(limitLine.getLineWidth());
356         mLimitLinePaint.setPathEffect(limitLine.getDashPathEffect());
357 
358         c.drawPath(mLimitLinePath, mLimitLinePaint);
359     }
360 
renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset)361     public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) {
362         String label = limitLine.getLabel();
363 
364         // if drawing the limit-value label is enabled
365         if (label != null && !label.equals("")) {
366 
367             mLimitLinePaint.setStyle(limitLine.getTextStyle());
368             mLimitLinePaint.setPathEffect(null);
369             mLimitLinePaint.setColor(limitLine.getTextColor());
370             mLimitLinePaint.setStrokeWidth(0.5f);
371             mLimitLinePaint.setTextSize(limitLine.getTextSize());
372 
373 
374             float xOffset = limitLine.getLineWidth() + limitLine.getXOffset();
375 
376             final LimitLine.LimitLabelPosition labelPosition = limitLine.getLabelPosition();
377 
378             if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_TOP) {
379 
380                 final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label);
381                 mLimitLinePaint.setTextAlign(Align.LEFT);
382                 c.drawText(label, position[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight,
383                         mLimitLinePaint);
384             } else if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) {
385 
386                 mLimitLinePaint.setTextAlign(Align.LEFT);
387                 c.drawText(label, position[0] + xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint);
388             } else if (labelPosition == LimitLine.LimitLabelPosition.LEFT_TOP) {
389 
390                 mLimitLinePaint.setTextAlign(Align.RIGHT);
391                 final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label);
392                 c.drawText(label, position[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight,
393                         mLimitLinePaint);
394             } else {
395 
396                 mLimitLinePaint.setTextAlign(Align.RIGHT);
397                 c.drawText(label, position[0] - xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint);
398             }
399         }
400     }
401 }
402