blob: a4d537024c30be9023b7b5fbe9cff501a9aaa1f1 [file] [log] [blame]
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.Utils;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.util.ArrayList;
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) {
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).getStackSize() > 1) {
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));
}
// add the legend description label
colors.add(-2);
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));
}
// add the legend description label
colors.add(-2);
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));
}
}
}
mLegend.setColors(colors);
mLegend.setLabels(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);
}
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());
String[] labels = mLegend.getLegendLabels();
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();
// the amount of pixels the text needs to be set down to be on the same
// height as the form
float textDrop = (Utils.calcTextHeight(mLegendLabelPaint, "AQJ") + formSize) / 2f;
float posX, posY;
// contains the stacked legend size in pixels
float stack = 0f;
boolean wasStacked = false;
float yoffset = mLegend.getYOffset();
float xoffset = mLegend.getXOffset();
switch (mLegend.getPosition()) {
case BELOW_CHART_LEFT:
posX = mViewPortHandler.contentLeft() + xoffset;
posY = mViewPortHandler.getChartHeight() - yoffset;
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) {
posX += mLegend.mNeededWidth;
}
for (int i = 0, count = labels.length; i < count; i++) {
boolean drawingForm = colors[i] != -2;
if (drawingForm) {
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
posX -= formSize;
drawForm(c, posX, posY - mLegend.mTextHeightMax / 2f, i, mLegend);
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
posX += formSize;
}
// grouped forms have null labels
if (labels[i] != null) {
// spacing between form and label
if (drawingForm)
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ?
-formToTextSpace :
formToTextSpace;
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]);
drawLabel(c, posX, posY, labels[i]);
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
posX += Utils.calcTextWidth(mLegendLabelPaint, labels[i]);
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ?
-xEntrySpace :
xEntrySpace;
} else {
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace;
}
}
break;
case BELOW_CHART_RIGHT:
posX = mViewPortHandler.contentRight() - xoffset;
posY = mViewPortHandler.getChartHeight() - yoffset;
for (int i = 0, count = labels.length; i < count; i++) {
boolean drawingForm = colors[i] != -2;
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT && drawingForm) {
posX -= formSize;
drawForm(c, posX, posY - mLegend.mTextHeightMax / 2f, i, mLegend);
posX -= formToTextSpace;
}
if (labels[i] != null) {
posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]);
drawLabel(c, posX, posY, labels[i]);
}
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT && drawingForm) {
posX -= formToTextSpace + formSize;
drawForm(c, posX, posY - mLegend.mTextHeightMax / 2f, i, mLegend);
}
posX -= labels[i] != null ? xEntrySpace : stackSpace;
}
break;
case BELOW_CHART_CENTER:
posX = mViewPortHandler.getChartWidth() / 2f + (direction == Legend.LegendDirection.LEFT_TO_RIGHT ? -mLegend.mNeededWidth / 2f : mLegend.mNeededWidth / 2f);
posY = mViewPortHandler.getChartHeight() - yoffset;
for (int i = 0; i < labels.length; i++) {
boolean drawingForm = colors[i] != -2;
if (drawingForm) {
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
posX -= formSize;
drawForm(c, posX, posY - mLegend.mTextHeightMax / 2f, i, mLegend);
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
posX += formSize;
}
// grouped forms have null labels
if (labels[i] != null) {
// spacing between form and label
if (drawingForm)
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ?
-formToTextSpace :
formToTextSpace;
if (direction == Legend.LegendDirection.RIGHT_TO_LEFT)
posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]);
drawLabel(c, posX, posY, labels[i]);
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
posX += Utils.calcTextWidth(mLegendLabelPaint, labels[i]);
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ?
-xEntrySpace :
xEntrySpace;
} else {
posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace;
}
}
break;
case 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;
for (int i = 0; i < labels.length; i++) {
boolean drawingForm = colors[i] != -2;
float x = posX;
if (drawingForm) {
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
x += stack;
else
x -= formSize - stack;
drawForm(c, x, posY, 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 + mLegend.mTextHeightMax / 2f, mLegend.getLabel(i));
posY += textDrop;
} else {
posY += mLegend.mTextHeightMax * 3f;
drawLabel(c, x, posY - mLegend.mTextHeightMax, mLegend.getLabel(i));
}
// make a step down
posY += mLegend.getYEntrySpace();
stack = 0f;
} else {
stack += formSize + stackSpace;
wasStacked = true;
}
}
break;
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:
boolean isRightAligned = mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART ||
mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART_CENTER ||
mLegend.getPosition() == 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 (mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART ||
mLegend.getPosition() == Legend.LegendPosition.LEFT_OF_CHART) {
posY = mViewPortHandler.contentTop() + yoffset;
} else if (mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART_CENTER ||
mLegend.getPosition() == Legend.LegendPosition.LEFT_OF_CHART_CENTER) {
posY = mViewPortHandler.getChartHeight() / 2f - mLegend.mNeededHeight / 2f;
} else /*if (mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART_INSIDE ||
mLegend.getPosition() == Legend.LegendPosition.LEFT_OF_CHART_INSIDE)*/ {
posY = mViewPortHandler.contentTop() + yoffset;
}
for (int i = 0; i < labels.length; i++) {
Boolean drawingForm = colors[i] != -2;
float x = posX;
if (drawingForm) {
if (direction == Legend.LegendDirection.LEFT_TO_RIGHT)
x += stack;
else
x -= formSize - stack;
drawForm(c, x, posY, 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 + mLegend.mTextHeightMax / 2f, mLegend.getLabel(i));
posY += textDrop;
} else {
posY += mLegend.mTextHeightMax * 3f;
drawLabel(c, x, posY - mLegend.mTextHeightMax, mLegend.getLabel(i));
}
// make a step down
posY += mLegend.getYEntrySpace();
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
* @param y
* @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] == -2)
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);
}
}