/*
* Copyright 2012 AndroidPlot.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.androidplot.xy;
import android.graphics.*;
import com.androidplot.ui.LayoutManager;
import com.androidplot.ui.SeriesAndFormatterList;
import com.androidplot.ui.SizeMetrics;
import com.androidplot.ui.TableModel;
import com.androidplot.ui.widget.Widget;
import com.androidplot.util.FontUtils;
import java.util.*;
public class XYLegendWidget extends Widget {
/**
* This class is of no use outside of XYLegendWidget. It's just used to alphabetically sort
* Region legend entries.
*/
private static class RegionEntryComparator implements Comparator<Map.Entry<XYRegionFormatter, String>> {
@Override
public int compare(Map.Entry<XYRegionFormatter, String> o1, Map.Entry<XYRegionFormatter, String> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
private enum CellType {
SERIES,
REGION
}
private XYPlot plot;
//private float iconWidth = 12;
private Paint textPaint;
private Paint iconBorderPaint;
private TableModel tableModel;
private boolean drawIconBackgroundEnabled = true;
private boolean drawIconBorderEnabled = true;
private SizeMetrics iconSizeMetrics;
private static final RegionEntryComparator regionEntryComparator = new RegionEntryComparator();
//private RectF iconRect = new RectF(0, 0, ICON_WIDTH_DEFAULT, ICON_HEIGHT_DEFAULT);
{
textPaint = new Paint();
textPaint.setColor(Color.LTGRAY);
textPaint.setAntiAlias(true);
iconBorderPaint = new Paint();
iconBorderPaint.setStyle(Paint.Style.STROKE);
//regionEntryComparator = new RegionEntryComparator();
}
public XYLegendWidget(LayoutManager layoutManager, XYPlot plot,
SizeMetrics widgetSizeMetrics,
TableModel tableModel,
SizeMetrics iconSizeMetrics) {
super(layoutManager, widgetSizeMetrics);
this.plot = plot;
setTableModel(tableModel);
this.iconSizeMetrics = iconSizeMetrics;
}
public synchronized void setTableModel(TableModel tableModel) {
this.tableModel = tableModel;
}
private RectF getIconRect(RectF cellRect) {
float cellRectCenterY = cellRect.top + (cellRect.height()/2);
RectF iconRect = iconSizeMetrics.getRectF(cellRect);
// center the icon rect vertically
float centeredIconOriginY = cellRectCenterY - (iconRect.height()/2);
iconRect.offsetTo(cellRect.left + 1, centeredIconOriginY);
return iconRect;
}
private static float getRectCenterY(RectF cellRect) {
return cellRect.top + (cellRect.height()/2);
}
private void beginDrawingCell(Canvas canvas, RectF iconRect) {
Paint bgPaint = plot.getGraphWidget().getGridBackgroundPaint();
if(drawIconBackgroundEnabled && bgPaint != null) {
canvas.drawRect(iconRect, bgPaint);
}
}
private void finishDrawingCell(Canvas canvas, RectF cellRect, RectF iconRect, String text) {
Paint bgPaint = plot.getGraphWidget().getGridBackgroundPaint();
if(drawIconBorderEnabled && bgPaint != null) {
iconBorderPaint.setColor(bgPaint.getColor());
canvas.drawRect(iconRect, iconBorderPaint);
}
float centeredTextOriginY = getRectCenterY(cellRect) + (FontUtils.getFontHeight(textPaint)/2);
if (textPaint.getTextAlign().equals(Paint.Align.RIGHT)) {
canvas.drawText(text, iconRect.left - 2, centeredTextOriginY, textPaint);
} else {
canvas.drawText(text, iconRect.right + 2, centeredTextOriginY, textPaint);
}
}
protected void drawRegionLegendIcon(Canvas canvas, RectF rect, XYRegionFormatter formatter) {
canvas.drawRect(rect, formatter.getPaint());
}
private void drawRegionLegendCell(Canvas canvas, XYRegionFormatter formatter, RectF cellRect, String text) {
RectF iconRect = getIconRect(cellRect);
beginDrawingCell(canvas, iconRect);
drawRegionLegendIcon(
canvas,
iconRect,
formatter
);
finishDrawingCell(canvas, cellRect, iconRect, text);
}
private void drawSeriesLegendCell(Canvas canvas, XYSeriesRenderer renderer, XYSeriesFormatter formatter, RectF cellRect, String seriesTitle) {
RectF iconRect = getIconRect(cellRect);
beginDrawingCell(canvas, iconRect);
renderer.drawSeriesLegendIcon(
canvas,
iconRect,
formatter);
finishDrawingCell(canvas, cellRect, iconRect, seriesTitle);
}
@Override
protected synchronized void doOnDraw(Canvas canvas, RectF widgetRect) {
// TODO: a good amount of iterating could be avoided if
// we create a temporary list of all the legend items up here.
if(plot.isEmpty()) {
return;
}
//Hashtable<XYRegionFormatter, XYSeriesRenderer> regionRendererLookup = new Hashtable<XYRegionFormatter, XYSeriesRenderer>();
// Keep an alphabetically sorted list of regions:
TreeSet<Map.Entry<XYRegionFormatter, String>> sortedRegions = new TreeSet<Map.Entry<XYRegionFormatter, String>>(new RegionEntryComparator());
// Calculate the number of cells needed to draw the Legend:
int seriesCount = 0;
for(XYSeriesRenderer renderer : plot.getRendererList()) {
SeriesAndFormatterList sfl = plot.getSeriesAndFormatterListForRenderer(renderer.getClass());
if(sfl != null) {
seriesCount += sfl.size();
}
// Figure out how many regions need to be added to the legend:
Hashtable<XYRegionFormatter, String> urf = renderer.getUniqueRegionFormatters();
/*for(XYRegionFormatter xyf : urf.keySet()) {
regionRendererLookup.put(xyf, renderer);
}*/
sortedRegions.addAll(urf.entrySet());
//sortedRegions.addAll(renderer.getUniqueRegionFormatters().entrySet());
}
seriesCount += sortedRegions.size();
// Create an iterator specially created to draw the number of cells we calculated:
Iterator<RectF> it = tableModel.getIterator(widgetRect, seriesCount);
RectF cellRect;
// draw each series legend item:
for(XYSeriesRenderer renderer : plot.getRendererList()) {
SeriesAndFormatterList<XYSeries,XYSeriesFormatter> sfl = plot.getSeriesAndFormatterListForRenderer(renderer.getClass());
if (sfl != null) {
// maxIndex is only used if it has been determined.
// if it is 0 then it could not be determined.
for (int i = 0; i < sfl.size() && it.hasNext(); i++) {
cellRect = it.next();
XYSeriesFormatter formatter = sfl.getFormatter(i);
drawSeriesLegendCell(canvas, renderer, formatter, cellRect, sfl.getSeries(i).getTitle());
}
}
}
// draw each region legend item:
for(Map.Entry<XYRegionFormatter, String> entry : sortedRegions) {
if(!it.hasNext()) {
break;
}
cellRect = it.next();
XYRegionFormatter formatter = entry.getKey();
drawRegionLegendCell(canvas, formatter, cellRect, entry.getValue());
}
}
public Paint getTextPaint() {
return textPaint;
}
public void setTextPaint(Paint textPaint) {
this.textPaint = textPaint;
}
public boolean isDrawIconBackgroundEnabled() {
return drawIconBackgroundEnabled;
}
public void setDrawIconBackgroundEnabled(boolean drawIconBackgroundEnabled) {
this.drawIconBackgroundEnabled = drawIconBackgroundEnabled;
}
public boolean isDrawIconBorderEnabled() {
return drawIconBorderEnabled;
}
public void setDrawIconBorderEnabled(boolean drawIconBorderEnabled) {
this.drawIconBorderEnabled = drawIconBorderEnabled;
}
public TableModel getTableModel() {
return tableModel;
}
public SizeMetrics getIconSizeMetrics() {
return iconSizeMetrics;
}
/**
* Set the size of each legend's icon. Note that when using relative sizing,
* the size is calculated against the countaining cell's size, not the plot's size.
* @param iconSizeMetrics
*/
public void setIconSizeMetrics(SizeMetrics iconSizeMetrics) {
this.iconSizeMetrics = iconSizeMetrics;
}
}