1 /* 2 * Copyright 2012 AndroidPlot.com 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.androidplot.xy; 18 19 import android.graphics.*; 20 import com.androidplot.ui.LayoutManager; 21 import com.androidplot.ui.SeriesAndFormatterList; 22 import com.androidplot.ui.SizeMetrics; 23 import com.androidplot.ui.TableModel; 24 import com.androidplot.ui.widget.Widget; 25 import com.androidplot.util.FontUtils; 26 27 import java.util.*; 28 29 public class XYLegendWidget extends Widget { 30 31 /** 32 * This class is of no use outside of XYLegendWidget. It's just used to alphabetically sort 33 * Region legend entries. 34 */ 35 private static class RegionEntryComparator implements Comparator<Map.Entry<XYRegionFormatter, String>> { 36 @Override compare(Map.Entry<XYRegionFormatter, String> o1, Map.Entry<XYRegionFormatter, String> o2)37 public int compare(Map.Entry<XYRegionFormatter, String> o1, Map.Entry<XYRegionFormatter, String> o2) { 38 return o1.getValue().compareTo(o2.getValue()); 39 } 40 } 41 42 private enum CellType { 43 SERIES, 44 REGION 45 } 46 47 private XYPlot plot; 48 //private float iconWidth = 12; 49 private Paint textPaint; 50 private Paint iconBorderPaint; 51 private TableModel tableModel; 52 private boolean drawIconBackgroundEnabled = true; 53 private boolean drawIconBorderEnabled = true; 54 55 private SizeMetrics iconSizeMetrics; 56 private static final RegionEntryComparator regionEntryComparator = new RegionEntryComparator(); 57 //private RectF iconRect = new RectF(0, 0, ICON_WIDTH_DEFAULT, ICON_HEIGHT_DEFAULT); 58 59 { 60 textPaint = new Paint(); 61 textPaint.setColor(Color.LTGRAY); 62 textPaint.setAntiAlias(true); 63 64 iconBorderPaint = new Paint(); 65 iconBorderPaint.setStyle(Paint.Style.STROKE); 66 //regionEntryComparator = new RegionEntryComparator(); 67 } 68 XYLegendWidget(LayoutManager layoutManager, XYPlot plot, SizeMetrics widgetSizeMetrics, TableModel tableModel, SizeMetrics iconSizeMetrics)69 public XYLegendWidget(LayoutManager layoutManager, XYPlot plot, 70 SizeMetrics widgetSizeMetrics, 71 TableModel tableModel, 72 SizeMetrics iconSizeMetrics) { 73 super(layoutManager, widgetSizeMetrics); 74 this.plot = plot; 75 setTableModel(tableModel); 76 this.iconSizeMetrics = iconSizeMetrics; 77 } 78 setTableModel(TableModel tableModel)79 public synchronized void setTableModel(TableModel tableModel) { 80 this.tableModel = tableModel; 81 } 82 getIconRect(RectF cellRect)83 private RectF getIconRect(RectF cellRect) { 84 float cellRectCenterY = cellRect.top + (cellRect.height()/2); 85 RectF iconRect = iconSizeMetrics.getRectF(cellRect); 86 87 // center the icon rect vertically 88 float centeredIconOriginY = cellRectCenterY - (iconRect.height()/2); 89 iconRect.offsetTo(cellRect.left + 1, centeredIconOriginY); 90 return iconRect; 91 } 92 getRectCenterY(RectF cellRect)93 private static float getRectCenterY(RectF cellRect) { 94 return cellRect.top + (cellRect.height()/2); 95 } 96 beginDrawingCell(Canvas canvas, RectF iconRect)97 private void beginDrawingCell(Canvas canvas, RectF iconRect) { 98 99 Paint bgPaint = plot.getGraphWidget().getGridBackgroundPaint(); 100 if(drawIconBackgroundEnabled && bgPaint != null) { 101 canvas.drawRect(iconRect, bgPaint); 102 } 103 } 104 finishDrawingCell(Canvas canvas, RectF cellRect, RectF iconRect, String text)105 private void finishDrawingCell(Canvas canvas, RectF cellRect, RectF iconRect, String text) { 106 107 Paint bgPaint = plot.getGraphWidget().getGridBackgroundPaint(); 108 if(drawIconBorderEnabled && bgPaint != null) { 109 iconBorderPaint.setColor(bgPaint.getColor()); 110 canvas.drawRect(iconRect, iconBorderPaint); 111 } 112 113 float centeredTextOriginY = getRectCenterY(cellRect) + (FontUtils.getFontHeight(textPaint)/2); 114 canvas.drawText(text, iconRect.right + 2, centeredTextOriginY, textPaint); 115 } 116 drawRegionLegendIcon(Canvas canvas, RectF rect, XYRegionFormatter formatter)117 protected void drawRegionLegendIcon(Canvas canvas, RectF rect, XYRegionFormatter formatter) { 118 canvas.drawRect(rect, formatter.getPaint()); 119 } 120 drawRegionLegendCell(Canvas canvas, XYRegionFormatter formatter, RectF cellRect, String text)121 private void drawRegionLegendCell(Canvas canvas, XYRegionFormatter formatter, RectF cellRect, String text) { 122 RectF iconRect = getIconRect(cellRect); 123 beginDrawingCell(canvas, iconRect); 124 125 drawRegionLegendIcon( 126 canvas, 127 iconRect, 128 formatter 129 ); 130 finishDrawingCell(canvas, cellRect, iconRect, text); 131 } 132 drawSeriesLegendCell(Canvas canvas, XYSeriesRenderer renderer, XYSeriesFormatter formatter, RectF cellRect, String seriesTitle)133 private void drawSeriesLegendCell(Canvas canvas, XYSeriesRenderer renderer, XYSeriesFormatter formatter, RectF cellRect, String seriesTitle) { 134 RectF iconRect = getIconRect(cellRect); 135 beginDrawingCell(canvas, iconRect); 136 137 renderer.drawSeriesLegendIcon( 138 canvas, 139 iconRect, 140 formatter); 141 finishDrawingCell(canvas, cellRect, iconRect, seriesTitle); 142 } 143 144 @Override doOnDraw(Canvas canvas, RectF widgetRect)145 protected synchronized void doOnDraw(Canvas canvas, RectF widgetRect) { 146 // TODO: a good amount of iterating could be avoided if 147 // we create a temporary list of all the legend items up here. 148 if(plot.isEmpty()) { 149 return; 150 } 151 152 //Hashtable<XYRegionFormatter, XYSeriesRenderer> regionRendererLookup = new Hashtable<XYRegionFormatter, XYSeriesRenderer>(); 153 154 // Keep an alphabetically sorted list of regions: 155 TreeSet<Map.Entry<XYRegionFormatter, String>> sortedRegions = new TreeSet<Map.Entry<XYRegionFormatter, String>>(new RegionEntryComparator()); 156 157 // Calculate the number of cells needed to draw the Legend: 158 int seriesCount = 0; 159 for(XYSeriesRenderer renderer : plot.getRendererList()) { 160 161 SeriesAndFormatterList sfl = plot.getSeriesAndFormatterListForRenderer(renderer.getClass()); 162 if(sfl != null) { 163 seriesCount += sfl.size(); 164 } 165 166 // Figure out how many regions need to be added to the legend: 167 Hashtable<XYRegionFormatter, String> urf = renderer.getUniqueRegionFormatters(); 168 /*for(XYRegionFormatter xyf : urf.keySet()) { 169 regionRendererLookup.put(xyf, renderer); 170 }*/ 171 sortedRegions.addAll(urf.entrySet()); 172 //sortedRegions.addAll(renderer.getUniqueRegionFormatters().entrySet()); 173 } 174 seriesCount += sortedRegions.size(); 175 176 // Create an iterator specially created to draw the number of cells we calculated: 177 Iterator<RectF> it = tableModel.getIterator(widgetRect, seriesCount); 178 179 RectF cellRect; 180 181 // draw each series legend item: 182 for(XYSeriesRenderer renderer : plot.getRendererList()) { 183 SeriesAndFormatterList<XYSeries,XYSeriesFormatter> sfl = plot.getSeriesAndFormatterListForRenderer(renderer.getClass()); 184 185 if (sfl != null) { 186 // maxIndex is only used if it has been determined. 187 // if it is 0 then it could not be determined. 188 for (int i = 0; i < sfl.size() && it.hasNext(); i++) { 189 cellRect = it.next(); 190 XYSeriesFormatter formatter = sfl.getFormatter(i); 191 drawSeriesLegendCell(canvas, renderer, formatter, cellRect, sfl.getSeries(i).getTitle()); 192 } 193 } 194 } 195 196 // draw each region legend item: 197 for(Map.Entry<XYRegionFormatter, String> entry : sortedRegions) { 198 if(!it.hasNext()) { 199 break; 200 } 201 cellRect = it.next(); 202 XYRegionFormatter formatter = entry.getKey(); 203 drawRegionLegendCell(canvas, formatter, cellRect, entry.getValue()); 204 } 205 } 206 207 getTextPaint()208 public Paint getTextPaint() { 209 return textPaint; 210 } 211 setTextPaint(Paint textPaint)212 public void setTextPaint(Paint textPaint) { 213 this.textPaint = textPaint; 214 } 215 isDrawIconBackgroundEnabled()216 public boolean isDrawIconBackgroundEnabled() { 217 return drawIconBackgroundEnabled; 218 } 219 setDrawIconBackgroundEnabled(boolean drawIconBackgroundEnabled)220 public void setDrawIconBackgroundEnabled(boolean drawIconBackgroundEnabled) { 221 this.drawIconBackgroundEnabled = drawIconBackgroundEnabled; 222 } 223 isDrawIconBorderEnabled()224 public boolean isDrawIconBorderEnabled() { 225 return drawIconBorderEnabled; 226 } 227 setDrawIconBorderEnabled(boolean drawIconBorderEnabled)228 public void setDrawIconBorderEnabled(boolean drawIconBorderEnabled) { 229 this.drawIconBorderEnabled = drawIconBorderEnabled; 230 } 231 getTableModel()232 public TableModel getTableModel() { 233 return tableModel; 234 } 235 getIconSizeMetrics()236 public SizeMetrics getIconSizeMetrics() { 237 return iconSizeMetrics; 238 } 239 240 /** 241 * Set the size of each legend's icon. Note that when using relative sizing, 242 * the size is calculated against the countaining cell's size, not the plot's size. 243 * @param iconSizeMetrics 244 */ setIconSizeMetrics(SizeMetrics iconSizeMetrics)245 public void setIconSizeMetrics(SizeMetrics iconSizeMetrics) { 246 this.iconSizeMetrics = iconSizeMetrics; 247 } 248 } 249