• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 package com.github.mikephil.charting.charts;
3 
4 import android.content.Context;
5 import android.graphics.Canvas;
6 import android.graphics.Paint;
7 import android.graphics.RectF;
8 import android.graphics.Typeface;
9 import android.util.AttributeSet;
10 
11 import com.github.mikephil.charting.components.XAxis;
12 import com.github.mikephil.charting.data.PieData;
13 import com.github.mikephil.charting.highlight.Highlight;
14 import com.github.mikephil.charting.highlight.PieHighlighter;
15 import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
16 import com.github.mikephil.charting.renderer.PieChartRenderer;
17 import com.github.mikephil.charting.utils.MPPointF;
18 import com.github.mikephil.charting.utils.Utils;
19 
20 import java.util.List;
21 
22 /**
23  * View that represents a pie chart. Draws cake like slices.
24  *
25  * @author Philipp Jahoda
26  */
27 public class PieChart extends PieRadarChartBase<PieData> {
28 
29     /**
30      * rect object that represents the bounds of the piechart, needed for
31      * drawing the circle
32      */
33     private RectF mCircleBox = new RectF();
34 
35     /**
36      * flag indicating if entry labels should be drawn or not
37      */
38     private boolean mDrawEntryLabels = true;
39 
40     /**
41      * array that holds the width of each pie-slice in degrees
42      */
43     private float[] mDrawAngles = new float[1];
44 
45     /**
46      * array that holds the absolute angle in degrees of each slice
47      */
48     private float[] mAbsoluteAngles = new float[1];
49 
50     /**
51      * if true, the white hole inside the chart will be drawn
52      */
53     private boolean mDrawHole = true;
54 
55     /**
56      * if true, the hole will see-through to the inner tips of the slices
57      */
58     private boolean mDrawSlicesUnderHole = false;
59 
60     /**
61      * if true, the values inside the piechart are drawn as percent values
62      */
63     private boolean mUsePercentValues = false;
64 
65     /**
66      * if true, the slices of the piechart are rounded
67      */
68     private boolean mDrawRoundedSlices = false;
69 
70     /**
71      * variable for the text that is drawn in the center of the pie-chart
72      */
73     private CharSequence mCenterText = "";
74 
75     private MPPointF mCenterTextOffset = MPPointF.getInstance(0, 0);
76 
77     /**
78      * indicates the size of the hole in the center of the piechart, default:
79      * radius / 2
80      */
81     private float mHoleRadiusPercent = 50f;
82 
83     /**
84      * the radius of the transparent circle next to the chart-hole in the center
85      */
86     protected float mTransparentCircleRadiusPercent = 55f;
87 
88     /**
89      * if enabled, centertext is drawn
90      */
91     private boolean mDrawCenterText = true;
92 
93     private float mCenterTextRadiusPercent = 100.f;
94 
95     protected float mMaxAngle = 360f;
96 
97     /**
98      * Minimum angle to draw slices, this only works if there is enough room for all slices to have
99      * the minimum angle, default 0f.
100      */
101     private float mMinAngleForSlices = 0f;
102 
PieChart(Context context)103     public PieChart(Context context) {
104         super(context);
105     }
106 
PieChart(Context context, AttributeSet attrs)107     public PieChart(Context context, AttributeSet attrs) {
108         super(context, attrs);
109     }
110 
PieChart(Context context, AttributeSet attrs, int defStyle)111     public PieChart(Context context, AttributeSet attrs, int defStyle) {
112         super(context, attrs, defStyle);
113     }
114 
115     @Override
init()116     protected void init() {
117         super.init();
118 
119         mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler);
120         mXAxis = null;
121 
122         mHighlighter = new PieHighlighter(this);
123     }
124 
125     @Override
onDraw(Canvas canvas)126     protected void onDraw(Canvas canvas) {
127         super.onDraw(canvas);
128 
129         if (mData == null)
130             return;
131 
132         mRenderer.drawData(canvas);
133 
134         if (valuesToHighlight())
135             mRenderer.drawHighlighted(canvas, mIndicesToHighlight);
136 
137         mRenderer.drawExtras(canvas);
138 
139         mRenderer.drawValues(canvas);
140 
141         mLegendRenderer.renderLegend(canvas);
142 
143         drawDescription(canvas);
144 
145         drawMarkers(canvas);
146     }
147 
148     @Override
calculateOffsets()149     public void calculateOffsets() {
150         super.calculateOffsets();
151 
152         // prevent nullpointer when no data set
153         if (mData == null)
154             return;
155 
156         float diameter = getDiameter();
157         float radius = diameter / 2f;
158 
159         MPPointF c = getCenterOffsets();
160 
161         float shift = mData.getDataSet().getSelectionShift();
162 
163         // create the circle box that will contain the pie-chart (the bounds of
164         // the pie-chart)
165         mCircleBox.set(c.x - radius + shift,
166                 c.y - radius + shift,
167                 c.x + radius - shift,
168                 c.y + radius - shift);
169 
170         MPPointF.recycleInstance(c);
171     }
172 
173     @Override
calcMinMax()174     protected void calcMinMax() {
175         calcAngles();
176     }
177 
178     @Override
getMarkerPosition(Highlight highlight)179     protected float[] getMarkerPosition(Highlight highlight) {
180 
181         MPPointF center = getCenterCircleBox();
182         float r = getRadius();
183 
184         float off = r / 10f * 3.6f;
185 
186         if (isDrawHoleEnabled()) {
187             off = (r - (r / 100f * getHoleRadius())) / 2f;
188         }
189 
190         r -= off; // offset to keep things inside the chart
191 
192         float rotationAngle = getRotationAngle();
193 
194         int entryIndex = (int) highlight.getX();
195 
196         // offset needed to center the drawn text in the slice
197         float offset = mDrawAngles[entryIndex] / 2;
198 
199         // calculate the text position
200         float x = (float) (r
201                 * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
202                 * mAnimator.getPhaseY())) + center.x);
203         float y = (float) (r
204                 * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
205                 * mAnimator.getPhaseY())) + center.y);
206 
207         MPPointF.recycleInstance(center);
208         return new float[]{x, y};
209     }
210 
211     /**
212      * calculates the needed angles for the chart slices
213      */
calcAngles()214     private void calcAngles() {
215 
216         int entryCount = mData.getEntryCount();
217 
218         if (mDrawAngles.length != entryCount) {
219             mDrawAngles = new float[entryCount];
220         } else {
221             for (int i = 0; i < entryCount; i++) {
222                 mDrawAngles[i] = 0;
223             }
224         }
225         if (mAbsoluteAngles.length != entryCount) {
226             mAbsoluteAngles = new float[entryCount];
227         } else {
228             for (int i = 0; i < entryCount; i++) {
229                 mAbsoluteAngles[i] = 0;
230             }
231         }
232 
233         float yValueSum = mData.getYValueSum();
234 
235         List<IPieDataSet> dataSets = mData.getDataSets();
236 
237         boolean hasMinAngle = mMinAngleForSlices != 0f && entryCount * mMinAngleForSlices <= mMaxAngle;
238         float[] minAngles = new float[entryCount];
239 
240         int cnt = 0;
241         float offset = 0f;
242         float diff = 0f;
243 
244         for (int i = 0; i < mData.getDataSetCount(); i++) {
245 
246             IPieDataSet set = dataSets.get(i);
247 
248             for (int j = 0; j < set.getEntryCount(); j++) {
249 
250                 float drawAngle = calcAngle(Math.abs(set.getEntryForIndex(j).getY()), yValueSum);
251 
252                 if (hasMinAngle) {
253                     float temp = drawAngle - mMinAngleForSlices;
254                     if (temp <= 0) {
255                         minAngles[cnt] = mMinAngleForSlices;
256                         offset += -temp;
257                     } else {
258                         minAngles[cnt] = drawAngle;
259                         diff += temp;
260                     }
261                 }
262 
263                 mDrawAngles[cnt] = drawAngle;
264 
265                 if (cnt == 0) {
266                     mAbsoluteAngles[cnt] = mDrawAngles[cnt];
267                 } else {
268                     mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt];
269                 }
270 
271                 cnt++;
272             }
273         }
274 
275         if (hasMinAngle) {
276             // Correct bigger slices by relatively reducing their angles based on the total angle needed to subtract
277             // This requires that `entryCount * mMinAngleForSlices <= mMaxAngle` be true to properly work!
278             for (int i = 0; i < entryCount; i++) {
279                 minAngles[i] -= (minAngles[i] - mMinAngleForSlices) / diff * offset;
280                 if (i == 0) {
281                     mAbsoluteAngles[0] = minAngles[0];
282                 } else {
283                     mAbsoluteAngles[i] = mAbsoluteAngles[i - 1] + minAngles[i];
284                 }
285             }
286 
287             mDrawAngles = minAngles;
288         }
289     }
290 
291     /**
292      * Checks if the given index is set to be highlighted.
293      *
294      * @param index
295      * @return
296      */
needsHighlight(int index)297     public boolean needsHighlight(int index) {
298 
299         // no highlight
300         if (!valuesToHighlight())
301             return false;
302 
303         for (int i = 0; i < mIndicesToHighlight.length; i++)
304 
305             // check if the xvalue for the given dataset needs highlight
306             if ((int) mIndicesToHighlight[i].getX() == index)
307                 return true;
308 
309         return false;
310     }
311 
312     /**
313      * calculates the needed angle for a given value
314      *
315      * @param value
316      * @return
317      */
calcAngle(float value)318     private float calcAngle(float value) {
319         return calcAngle(value, mData.getYValueSum());
320     }
321 
322     /**
323      * calculates the needed angle for a given value
324      *
325      * @param value
326      * @param yValueSum
327      * @return
328      */
calcAngle(float value, float yValueSum)329     private float calcAngle(float value, float yValueSum) {
330         return value / yValueSum * mMaxAngle;
331     }
332 
333     /**
334      * This will throw an exception, PieChart has no XAxis object.
335      *
336      * @return
337      */
338     @Deprecated
339     @Override
getXAxis()340     public XAxis getXAxis() {
341         throw new RuntimeException("PieChart has no XAxis");
342     }
343 
344     @Override
getIndexForAngle(float angle)345     public int getIndexForAngle(float angle) {
346 
347         // take the current angle of the chart into consideration
348         float a = Utils.getNormalizedAngle(angle - getRotationAngle());
349 
350         for (int i = 0; i < mAbsoluteAngles.length; i++) {
351             if (mAbsoluteAngles[i] > a)
352                 return i;
353         }
354 
355         return -1; // return -1 if no index found
356     }
357 
358     /**
359      * Returns the index of the DataSet this x-index belongs to.
360      *
361      * @param xIndex
362      * @return
363      */
getDataSetIndexForIndex(int xIndex)364     public int getDataSetIndexForIndex(int xIndex) {
365 
366         List<IPieDataSet> dataSets = mData.getDataSets();
367 
368         for (int i = 0; i < dataSets.size(); i++) {
369             if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null)
370                 return i;
371         }
372 
373         return -1;
374     }
375 
376     /**
377      * returns an integer array of all the different angles the chart slices
378      * have the angles in the returned array determine how much space (of 360°)
379      * each slice takes
380      *
381      * @return
382      */
getDrawAngles()383     public float[] getDrawAngles() {
384         return mDrawAngles;
385     }
386 
387     /**
388      * returns the absolute angles of the different chart slices (where the
389      * slices end)
390      *
391      * @return
392      */
getAbsoluteAngles()393     public float[] getAbsoluteAngles() {
394         return mAbsoluteAngles;
395     }
396 
397     /**
398      * Sets the color for the hole that is drawn in the center of the PieChart
399      * (if enabled).
400      *
401      * @param color
402      */
setHoleColor(int color)403     public void setHoleColor(int color) {
404         ((PieChartRenderer) mRenderer).getPaintHole().setColor(color);
405     }
406 
407     /**
408      * Enable or disable the visibility of the inner tips of the slices behind the hole
409      */
setDrawSlicesUnderHole(boolean enable)410     public void setDrawSlicesUnderHole(boolean enable) {
411         mDrawSlicesUnderHole = enable;
412     }
413 
414     /**
415      * Returns true if the inner tips of the slices are visible behind the hole,
416      * false if not.
417      *
418      * @return true if slices are visible behind the hole.
419      */
isDrawSlicesUnderHoleEnabled()420     public boolean isDrawSlicesUnderHoleEnabled() {
421         return mDrawSlicesUnderHole;
422     }
423 
424     /**
425      * set this to true to draw the pie center empty
426      *
427      * @param enabled
428      */
setDrawHoleEnabled(boolean enabled)429     public void setDrawHoleEnabled(boolean enabled) {
430         this.mDrawHole = enabled;
431     }
432 
433     /**
434      * returns true if the hole in the center of the pie-chart is set to be
435      * visible, false if not
436      *
437      * @return
438      */
isDrawHoleEnabled()439     public boolean isDrawHoleEnabled() {
440         return mDrawHole;
441     }
442 
443     /**
444      * Sets the text String that is displayed in the center of the PieChart.
445      *
446      * @param text
447      */
setCenterText(CharSequence text)448     public void setCenterText(CharSequence text) {
449         if (text == null)
450             mCenterText = "";
451         else
452             mCenterText = text;
453     }
454 
455     /**
456      * returns the text that is drawn in the center of the pie-chart
457      *
458      * @return
459      */
getCenterText()460     public CharSequence getCenterText() {
461         return mCenterText;
462     }
463 
464     /**
465      * set this to true to draw the text that is displayed in the center of the
466      * pie chart
467      *
468      * @param enabled
469      */
setDrawCenterText(boolean enabled)470     public void setDrawCenterText(boolean enabled) {
471         this.mDrawCenterText = enabled;
472     }
473 
474     /**
475      * returns true if drawing the center text is enabled
476      *
477      * @return
478      */
isDrawCenterTextEnabled()479     public boolean isDrawCenterTextEnabled() {
480         return mDrawCenterText;
481     }
482 
483     @Override
getRequiredLegendOffset()484     protected float getRequiredLegendOffset() {
485         return mLegendRenderer.getLabelPaint().getTextSize() * 2.f;
486     }
487 
488     @Override
getRequiredBaseOffset()489     protected float getRequiredBaseOffset() {
490         return 0;
491     }
492 
493     @Override
getRadius()494     public float getRadius() {
495         if (mCircleBox == null)
496             return 0;
497         else
498             return Math.min(mCircleBox.width() / 2f, mCircleBox.height() / 2f);
499     }
500 
501     /**
502      * returns the circlebox, the boundingbox of the pie-chart slices
503      *
504      * @return
505      */
getCircleBox()506     public RectF getCircleBox() {
507         return mCircleBox;
508     }
509 
510     /**
511      * returns the center of the circlebox
512      *
513      * @return
514      */
getCenterCircleBox()515     public MPPointF getCenterCircleBox() {
516         return MPPointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY());
517     }
518 
519     /**
520      * sets the typeface for the center-text paint
521      *
522      * @param t
523      */
setCenterTextTypeface(Typeface t)524     public void setCenterTextTypeface(Typeface t) {
525         ((PieChartRenderer) mRenderer).getPaintCenterText().setTypeface(t);
526     }
527 
528     /**
529      * Sets the size of the center text of the PieChart in dp.
530      *
531      * @param sizeDp
532      */
setCenterTextSize(float sizeDp)533     public void setCenterTextSize(float sizeDp) {
534         ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(
535                 Utils.convertDpToPixel(sizeDp));
536     }
537 
538     /**
539      * Sets the size of the center text of the PieChart in pixels.
540      *
541      * @param sizePixels
542      */
setCenterTextSizePixels(float sizePixels)543     public void setCenterTextSizePixels(float sizePixels) {
544         ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels);
545     }
546 
547     /**
548      * Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0
549      *
550      * @param x
551      * @param y
552      */
setCenterTextOffset(float x, float y)553     public void setCenterTextOffset(float x, float y) {
554         mCenterTextOffset.x = Utils.convertDpToPixel(x);
555         mCenterTextOffset.y = Utils.convertDpToPixel(y);
556     }
557 
558     /**
559      * Returns the offset on the x- and y-axis the center text has in dp.
560      *
561      * @return
562      */
getCenterTextOffset()563     public MPPointF getCenterTextOffset() {
564         return MPPointF.getInstance(mCenterTextOffset.x, mCenterTextOffset.y);
565     }
566 
567     /**
568      * Sets the color of the center text of the PieChart.
569      *
570      * @param color
571      */
setCenterTextColor(int color)572     public void setCenterTextColor(int color) {
573         ((PieChartRenderer) mRenderer).getPaintCenterText().setColor(color);
574     }
575 
576     /**
577      * sets the radius of the hole in the center of the piechart in percent of
578      * the maximum radius (max = the radius of the whole chart), default 50%
579      *
580      * @param percent
581      */
setHoleRadius(final float percent)582     public void setHoleRadius(final float percent) {
583         mHoleRadiusPercent = percent;
584     }
585 
586     /**
587      * Returns the size of the hole radius in percent of the total radius.
588      *
589      * @return
590      */
getHoleRadius()591     public float getHoleRadius() {
592         return mHoleRadiusPercent;
593     }
594 
595     /**
596      * Sets the color the transparent-circle should have.
597      *
598      * @param color
599      */
setTransparentCircleColor(int color)600     public void setTransparentCircleColor(int color) {
601 
602         Paint p = ((PieChartRenderer) mRenderer).getPaintTransparentCircle();
603         int alpha = p.getAlpha();
604         p.setColor(color);
605         p.setAlpha(alpha);
606     }
607 
608     /**
609      * sets the radius of the transparent circle that is drawn next to the hole
610      * in the piechart in percent of the maximum radius (max = the radius of the
611      * whole chart), default 55% -> means 5% larger than the center-hole by
612      * default
613      *
614      * @param percent
615      */
setTransparentCircleRadius(final float percent)616     public void setTransparentCircleRadius(final float percent) {
617         mTransparentCircleRadiusPercent = percent;
618     }
619 
getTransparentCircleRadius()620     public float getTransparentCircleRadius() {
621         return mTransparentCircleRadiusPercent;
622     }
623 
624     /**
625      * Sets the amount of transparency the transparent circle should have 0 = fully transparent,
626      * 255 = fully opaque.
627      * Default value is 100.
628      *
629      * @param alpha 0-255
630      */
setTransparentCircleAlpha(int alpha)631     public void setTransparentCircleAlpha(int alpha) {
632         ((PieChartRenderer) mRenderer).getPaintTransparentCircle().setAlpha(alpha);
633     }
634 
635     /**
636      * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class).
637      * Deprecated -> use setDrawEntryLabels(...) instead.
638      *
639      * @param enabled
640      */
641     @Deprecated
setDrawSliceText(boolean enabled)642     public void setDrawSliceText(boolean enabled) {
643         mDrawEntryLabels = enabled;
644     }
645 
646     /**
647      * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class).
648      *
649      * @param enabled
650      */
setDrawEntryLabels(boolean enabled)651     public void setDrawEntryLabels(boolean enabled) {
652         mDrawEntryLabels = enabled;
653     }
654 
655     /**
656      * Returns true if drawing the entry labels is enabled, false if not.
657      *
658      * @return
659      */
isDrawEntryLabelsEnabled()660     public boolean isDrawEntryLabelsEnabled() {
661         return mDrawEntryLabels;
662     }
663 
664     /**
665      * Sets the color the entry labels are drawn with.
666      *
667      * @param color
668      */
setEntryLabelColor(int color)669     public void setEntryLabelColor(int color) {
670         ((PieChartRenderer) mRenderer).getPaintEntryLabels().setColor(color);
671     }
672 
673     /**
674      * Sets a custom Typeface for the drawing of the entry labels.
675      *
676      * @param tf
677      */
setEntryLabelTypeface(Typeface tf)678     public void setEntryLabelTypeface(Typeface tf) {
679         ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTypeface(tf);
680     }
681 
682     /**
683      * Sets the size of the entry labels in dp. Default: 13dp
684      *
685      * @param size
686      */
setEntryLabelTextSize(float size)687     public void setEntryLabelTextSize(float size) {
688         ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTextSize(Utils.convertDpToPixel(size));
689     }
690 
691     /**
692      * Sets whether to draw slices in a curved fashion, only works if drawing the hole is enabled
693      * and if the slices are not drawn under the hole.
694      *
695      * @param enabled draw curved ends of slices
696      */
setDrawRoundedSlices(boolean enabled)697     public void setDrawRoundedSlices(boolean enabled) {
698         mDrawRoundedSlices = enabled;
699     }
700 
701     /**
702      * Returns true if the chart is set to draw each end of a pie-slice
703      * "rounded".
704      *
705      * @return
706      */
isDrawRoundedSlicesEnabled()707     public boolean isDrawRoundedSlicesEnabled() {
708         return mDrawRoundedSlices;
709     }
710 
711     /**
712      * If this is enabled, values inside the PieChart are drawn in percent and
713      * not with their original value. Values provided for the IValueFormatter to
714      * format are then provided in percent.
715      *
716      * @param enabled
717      */
setUsePercentValues(boolean enabled)718     public void setUsePercentValues(boolean enabled) {
719         mUsePercentValues = enabled;
720     }
721 
722     /**
723      * Returns true if using percentage values is enabled for the chart.
724      *
725      * @return
726      */
isUsePercentValuesEnabled()727     public boolean isUsePercentValuesEnabled() {
728         return mUsePercentValues;
729     }
730 
731     /**
732      * the rectangular radius of the bounding box for the center text, as a percentage of the pie
733      * hole
734      * default 1.f (100%)
735      */
setCenterTextRadiusPercent(float percent)736     public void setCenterTextRadiusPercent(float percent) {
737         mCenterTextRadiusPercent = percent;
738     }
739 
740     /**
741      * the rectangular radius of the bounding box for the center text, as a percentage of the pie
742      * hole
743      * default 1.f (100%)
744      */
getCenterTextRadiusPercent()745     public float getCenterTextRadiusPercent() {
746         return mCenterTextRadiusPercent;
747     }
748 
getMaxAngle()749     public float getMaxAngle() {
750         return mMaxAngle;
751     }
752 
753     /**
754      * Sets the max angle that is used for calculating the pie-circle. 360f means
755      * it's a full PieChart, 180f results in a half-pie-chart. Default: 360f
756      *
757      * @param maxangle min 90, max 360
758      */
setMaxAngle(float maxangle)759     public void setMaxAngle(float maxangle) {
760 
761         if (maxangle > 360)
762             maxangle = 360f;
763 
764         if (maxangle < 90)
765             maxangle = 90f;
766 
767         this.mMaxAngle = maxangle;
768     }
769 
770     /**
771      * The minimum angle slices on the chart are rendered with, default is 0f.
772      *
773      * @return minimum angle for slices
774      */
getMinAngleForSlices()775     public float getMinAngleForSlices() {
776         return mMinAngleForSlices;
777     }
778 
779     /**
780      * Set the angle to set minimum size for slices, you must call {@link #notifyDataSetChanged()}
781      * and {@link #invalidate()} when changing this, only works if there is enough room for all
782      * slices to have the minimum angle.
783      *
784      * @param minAngle minimum 0, maximum is half of {@link #setMaxAngle(float)}
785      */
setMinAngleForSlices(float minAngle)786     public void setMinAngleForSlices(float minAngle) {
787 
788         if (minAngle > (mMaxAngle / 2f))
789             minAngle = mMaxAngle / 2f;
790         else if (minAngle < 0)
791             minAngle = 0f;
792 
793         this.mMinAngleForSlices = minAngle;
794     }
795 
796     @Override
onDetachedFromWindow()797     protected void onDetachedFromWindow() {
798         // releases the bitmap in the renderer to avoid oom error
799         if (mRenderer != null && mRenderer instanceof PieChartRenderer) {
800             ((PieChartRenderer) mRenderer).releaseBitmap();
801         }
802         super.onDetachedFromWindow();
803     }
804 }
805