Home | History | Annotate | Download | only in xy
      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
     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 
     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 
     79     public synchronized void setTableModel(TableModel tableModel) {
     80         this.tableModel = tableModel;
     81     }
     82 
     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 
     93     private static float getRectCenterY(RectF cellRect) {
     94         return cellRect.top + (cellRect.height()/2);
     95     }
     96 
     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 
    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 
    117     protected void drawRegionLegendIcon(Canvas canvas, RectF rect, XYRegionFormatter formatter) {
    118             canvas.drawRect(rect, formatter.getPaint());
    119         }
    120 
    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 
    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
    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 
    208     public Paint getTextPaint() {
    209         return textPaint;
    210     }
    211 
    212     public void setTextPaint(Paint textPaint) {
    213         this.textPaint = textPaint;
    214     }
    215 
    216     public boolean isDrawIconBackgroundEnabled() {
    217         return drawIconBackgroundEnabled;
    218     }
    219 
    220     public void setDrawIconBackgroundEnabled(boolean drawIconBackgroundEnabled) {
    221         this.drawIconBackgroundEnabled = drawIconBackgroundEnabled;
    222     }
    223 
    224     public boolean isDrawIconBorderEnabled() {
    225         return drawIconBorderEnabled;
    226     }
    227 
    228     public void setDrawIconBorderEnabled(boolean drawIconBorderEnabled) {
    229         this.drawIconBorderEnabled = drawIconBorderEnabled;
    230     }
    231 
    232     public TableModel getTableModel() {
    233         return tableModel;
    234     }
    235 
    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      */
    245     public void setIconSizeMetrics(SizeMetrics iconSizeMetrics) {
    246         this.iconSizeMetrics = iconSizeMetrics;
    247     }
    248 }
    249