package com.github.mikephil.charting.renderer;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Typeface;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.ChartData;
import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.utils.FSize;
import com.github.mikephil.charting.utils.Utils;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class LegendRenderer extends Renderer {
/**
* paint for the legend labels
*/
protected Paint mLegendLabelPaint;
/**
* paint used for the legend forms
*/
protected Paint mLegendFormPaint;
/**
* the legend object this renderer renders
*/
protected Legend mLegend;
public LegendRenderer(ViewPortHandler viewPortHandler, Legend legend) {
super(viewPortHandler);
this.mLegend = legend;
mLegendLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLegendLabelPaint.setTextSize(Utils.convertDpToPixel(9f));
mLegendLabelPaint.setTextAlign(Align.LEFT);
mLegendFormPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLegendFormPaint.setStyle(Paint.Style.FILL);
mLegendFormPaint.setStrokeWidth(3f);
}
/**
* Returns the Paint object used for drawing the Legend labels.
*
* @return
*/
public Paint getLabelPaint() {
return mLegendLabelPaint;
}
/**
* Returns the Paint object used for drawing the Legend forms.
*
* @return
*/
public Paint getFormPaint() {
return mLegendFormPaint;
}
/**
* Prepares the legend and calculates all needed forms, labels and colors.
*
* @param data
*/
public void computeLegend(ChartData<?> data) {
if (!mLegend.isLegendCustom()) {
List<String> labels = new ArrayList<String>();
List<Integer> colors = new ArrayList<Integer>();
// loop for building up the colors and labels used in the legend
for (int i = 0; i < data.getDataSetCount(); i++) {
DataSet<? extends Entry> dataSet = data.getDataSetByIndex(i);
List<Integer> clrs = dataSet.getColors();
int entryCount = dataSet.getEntryCount();
// if we have a barchart with stacked bars
if (dataSet instanceof BarDataSet && ((BarDataSet) dataSet).isStacked()) {
BarDataSet bds = (BarDataSet) dataSet;
String[] sLabels = bds.getStackLabels();
for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) {
labels.add(sLabels[j % sLabels.length]);
colors.add(clrs.get(j));
}
if (bds.getLabel() != null) {
// add the legend description label
colors.add(ColorTemplate.COLOR_SKIP);
labels.add(bds.getLabel());
}
} else if (dataSet instanceof PieDataSet) {
List<String> xVals = data.getXVals();
PieDataSet pds = (PieDataSet) dataSet;
for (int j = 0; j < clrs.size() && j < entryCount && j < xVals.size(); j++) {
labels.add(xVals.get(j));
colors.add(clrs.get(j));
}
if (pds.getLabel() != null) {
// add the legend description label
colors.add(ColorTemplate.COLOR_SKIP);
labels.add(pds.getLabel());
}
} else { // all others
for (int j = 0; j < clrs.size() && j < entryCount; j++) {
// if multiple colors are set for a DataSet, group them
if (j < clrs.size() - 1 && j < entryCount - 1) {
labels.add(null);
} else { // add label to the last entry
String label = data.getDataSetByIndex(i).getLabel();
labels.add(label);
}
colors.add(clrs.get(j));
}
}
}
if (mLegend.getExtraColors() != null && mLegend.getExtraLabels() != null) {
for (int color : mLegend.getExtraColors())
colors.add(color);
Collections.addAll(labels, mLegend.getExtraLabels());
}
mLegend.setComputedColors(colors);
mLegend.setComputedLabels(labels);
}
Typeface tf = mLegend.getTypeface();
if (tf != null)
mLegendLabelPaint.setTypeface(tf);
mLegendLabelPaint.setTextSize(mLegend.getTextSize());
mLegendLabelPaint.setColor(mLegend.getTextColor());
// calculate all dimensions of the mLegend
mLegend.calculateDimensions(mLegendLabelPaint, mViewPortHandler);
}
public void renderLegend(Canvas c) {
if (!mLegend.isEnabled())
return;
Typeface tf = mLegend.getTypeface();
if (tf != null)
mLegendLabelPaint.setTypeface(tf);
mLegendLabelPaint.setTextSize(mLegend.getTextSize());
mLegendLabelPaint.setColor(mLegend.getTextColor());
float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint);
float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint) + mLegend.getYEntrySpace();
float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f;
String[] labels = mLegend.getLabels();
int[] colors = mLegend.getColors();
float formToTextSpace = mLegend.getFormToTextSpace();
float xEntrySpace = mLegend.getXEntrySpace();
Legend.LegendDirection direction = mLegend.getDirection();
float formSize = mLegend.getFormSize();
// space between the entries
float stackSpace = mLegend.getStackSpace();
float posX, posY;
float yoffset = mLegend.getYOffset();
float xoffset = mLegend.getXOffset();
Legend.LegendPosition legendPosition = mLegend.getPosition();
switch (legendPosition) {
case BELOW_CHART_LEFT:
case BELOW_CHART_RIGHT:
case BELOW_CHART_CENTER:
case ABOVE_CHART_LEFT:
case ABOVE_CHART_RIGHT:
case ABOVE_CHART_CENTER: {
float contentWidth = mViewPortHandler.contentWidth();
float originPosX;
if (legendPosition == Legend.LegendPosition.BELOW_CHART_LEFT
|| legendPosition == Legend.LegendPosition.ABOVE_CHART_LEFT) {
originPosX = mViewPortHandler.contentLeft() + xoffset;
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
originPosX += mLegend.mNeededWidth;
} else if (legendPosition == Legend.LegendPosition.BELOW_CHART_RIGHT
|| legendPosition == Legend.LegendPosition.ABOVE_CHART_RIGHT) {
originPosX = mViewPortHandler.contentRight() - xoffset;
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
originPosX -= mLegend.mNeededWidth;
} else // BELOW_CHART_CENTER || ABOVE_CHART_CENTER
originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f;
FSize[] calculatedLineSizes = mLegend.getCalculatedLineSizes();
FSize[] calculatedLabelSizes = mLegend.getCalculatedLabelSizes();
Boolean[] calculatedLabelBreakPoints = mLegend.getCalculatedLabelBreakPoints();
posX = originPosX;
if (legendPosition == Legend.LegendPosition.ABOVE_CHART_LEFT ||
legendPosition == Legend.LegendPosition.ABOVE_CHART_RIGHT ||
legendPosition == Legend.LegendPosition.ABOVE_CHART_CENTER) {
posY = 0.f;
} else {
posY = mViewPortHandler.getChartHeight() - yoffset - mLegend.mNeededHeight;
}
int lineIndex = 0;
for (int i = 0, count = labels.length; i < count; i++) {
if (i < calculatedLabelBreakPoints.length && calculatedLabelBreakPoints[i]) {
posX = originPosX;
posY += labelLineHeight + labelLineSpacing;
}
if (posX == originPosX && legendPosition == Legend.LegendPosition.BELOW_CHART_CENTER && lineIndex < calculatedLineSizes.length) {
posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT ? calculatedLineSizes[lineIndex].width : -calculatedLineSizes[lineIndex].width) / 2.f;
lineIndex++;
}
boolean drawingForm = colors[i] != ColorTemplate.COLOR_SKIP;
boolean isStacked = labels[i] == null; // grouped forms have null labels
if (drawingForm) {
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
posX -= formSize;
drawForm(c, posX, posY + formYOffset, i, mLegend);
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
posX += formSize;
}
if (!isStacked) {
if (drawingForm)
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -formToTextSpace : formToTextSpace;
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
posX -= calculatedLabelSizes[i].width;
drawLabel(c, posX, posY + labelLineHeight, labels[i]);
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
posX += calculatedLabelSizes[i].width;
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -xEntrySpace : xEntrySpace;
} else
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace;
}
}
break;
case PIECHART_CENTER:
case RIGHT_OF_CHART:
case RIGHT_OF_CHART_CENTER:
case RIGHT_OF_CHART_INSIDE:
case LEFT_OF_CHART:
case LEFT_OF_CHART_CENTER:
case LEFT_OF_CHART_INSIDE: {
// contains the stacked legend size in pixels
float stack = 0f;
boolean wasStacked = false;
if (legendPosition == Legend.LegendPosition.PIECHART_CENTER) {
posX = mViewPortHandler.getChartWidth() / 2f
+ (direction == Legend.LegendDirection.LEFT_TO_RIGHT ? -mLegend.mTextWidthMax / 2f
: mLegend.mTextWidthMax / 2f);
posY = mViewPortHandler.getChartHeight() / 2f - mLegend.mNeededHeight / 2f
+ mLegend.getYOffset();
} else {
boolean isRightAligned = legendPosition == Legend.LegendPosition.RIGHT_OF_CHART
||
legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_CENTER ||
legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_INSIDE;
if (isRightAligned) {
posX = mViewPortHandler.getChartWidth() - xoffset;
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
posX -= mLegend.mTextWidthMax;
} else {
posX = xoffset;
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
posX += mLegend.mTextWidthMax;
}
if (legendPosition == Legend.LegendPosition.RIGHT_OF_CHART ||
legendPosition == Legend.LegendPosition.LEFT_OF_CHART) {
posY = mViewPortHandler.contentTop() + yoffset;
} else if (legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_CENTER ||
legendPosition == Legend.LegendPosition.LEFT_OF_CHART_CENTER) {
posY = mViewPortHandler.getChartHeight() / 2f - mLegend.mNeededHeight / 2f;
} else /*
* if (legendPosition ==
* Legend.LegendPosition.RIGHT_OF_CHART_INSIDE ||
* legendPosition ==
* Legend.LegendPosition.LEFT_OF_CHART_INSIDE)
*/ {
posY = mViewPortHandler.contentTop() + yoffset;
}
}
for (int i = 0; i < labels.length; i++) {
Boolean drawingForm = colors[i] != ColorTemplate.COLOR_SKIP;
float x = posX;
if (drawingForm) {
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
x += stack;
else
x -= formSize - stack;
drawForm(c, x, posY + formYOffset, i, mLegend);
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
x += formSize;
}
if (labels[i] != null) {
if (drawingForm && !wasStacked)
x += direction == Legend.LegendDirection.LEFT_TO_RIGHT ? formToTextSpace
: -formToTextSpace;
else if (wasStacked)
x = posX;
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
x -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]);
if (!wasStacked) {
drawLabel(c, x, posY + labelLineHeight, labels[i]);
} else {
posY += labelLineHeight + labelLineSpacing;
drawLabel(c, x, posY + labelLineHeight, labels[i]);
}
// make a step down
posY += labelLineHeight + labelLineSpacing;
stack = 0f;
} else {
stack += formSize + stackSpace;
wasStacked = true;
}
}
}
break;
}
}
/**
* Draws the Legend-form at the given position with the color at the given
* index.
*
* @param c canvas to draw with
* @param x position
* @param y position
* @param index the index of the color to use (in the colors array)
*/
protected void drawForm(Canvas c, float x, float y, int index, Legend legend) {
if (legend.getColors()[index] == ColorTemplate.COLOR_SKIP)
return;
mLegendFormPaint.setColor(legend.getColors()[index]);
float formsize = legend.getFormSize();
float half = formsize / 2f;
switch (legend.getForm()) {
case CIRCLE:
c.drawCircle(x + half, y, half, mLegendFormPaint);
break;
case SQUARE:
c.drawRect(x, y - half, x + formsize, y + half, mLegendFormPaint);
break;
case LINE:
c.drawLine(x, y, x + formsize, y, mLegendFormPaint);
break;
}
}
/**
* Draws the provided label at the given position.
*
* @param c canvas to draw with
* @param x
* @param y
* @param label the label to draw
*/
protected void drawLabel(Canvas c, float x, float y, String label) {
c.drawText(label, x, y, mLegendLabelPaint);
}
}