|
@@ -0,0 +1,825 @@
|
|
|
+package com.github.mikephil.charting.components;
|
|
|
+
|
|
|
+import android.graphics.DashPathEffect;
|
|
|
+import android.graphics.Paint;
|
|
|
+
|
|
|
+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.List;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Class representing the legend of the chart. The legend will contain one entry
|
|
|
+ * per color and DataSet. Multiple colors in one DataSet are grouped together.
|
|
|
+ * The legend object is NOT available before setting data to the chart.
|
|
|
+ *
|
|
|
+ * @author Philipp Jahoda
|
|
|
+ */
|
|
|
+public class Legend extends ComponentBase {
|
|
|
+
|
|
|
+ public enum LegendForm {
|
|
|
+ /**
|
|
|
+ * Avoid drawing a form
|
|
|
+ */
|
|
|
+ NONE,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Do not draw the a form, but leave space for it
|
|
|
+ */
|
|
|
+ EMPTY,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Use default (default dataset's form to the legend's form)
|
|
|
+ */
|
|
|
+ DEFAULT,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Draw a square
|
|
|
+ */
|
|
|
+ SQUARE,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Draw a circle
|
|
|
+ */
|
|
|
+ CIRCLE,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Draw a horizontal line
|
|
|
+ */
|
|
|
+ LINE
|
|
|
+ }
|
|
|
+
|
|
|
+ public enum LegendHorizontalAlignment {
|
|
|
+ LEFT, CENTER, RIGHT
|
|
|
+ }
|
|
|
+
|
|
|
+ public enum LegendVerticalAlignment {
|
|
|
+ TOP, CENTER, BOTTOM
|
|
|
+ }
|
|
|
+
|
|
|
+ public enum LegendOrientation {
|
|
|
+ HORIZONTAL, VERTICAL
|
|
|
+ }
|
|
|
+
|
|
|
+ public enum LegendDirection {
|
|
|
+ LEFT_TO_RIGHT, RIGHT_TO_LEFT
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The legend entries array
|
|
|
+ */
|
|
|
+ private LegendEntry[] mEntries = new LegendEntry[]{};
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Entries that will be appended to the end of the auto calculated entries after calculating the legend.
|
|
|
+ * (if the legend has already been calculated, you will need to call notifyDataSetChanged() to let the changes take effect)
|
|
|
+ */
|
|
|
+ private LegendEntry[] mExtraEntries;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Are the legend labels/colors a custom value or auto calculated? If false,
|
|
|
+ * then it's auto, if true, then custom. default false (automatic legend)
|
|
|
+ */
|
|
|
+ private boolean mIsLegendCustom = false;
|
|
|
+
|
|
|
+ private LegendHorizontalAlignment mHorizontalAlignment = LegendHorizontalAlignment.LEFT;
|
|
|
+ private LegendVerticalAlignment mVerticalAlignment = LegendVerticalAlignment.BOTTOM;
|
|
|
+ private LegendOrientation mOrientation = LegendOrientation.HORIZONTAL;
|
|
|
+ private boolean mDrawInside = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the text direction for the legend
|
|
|
+ */
|
|
|
+ private LegendDirection mDirection = LegendDirection.LEFT_TO_RIGHT;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the shape/form the legend colors are drawn in
|
|
|
+ */
|
|
|
+ private LegendForm mShape = LegendForm.SQUARE;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the size of the legend forms/shapes
|
|
|
+ */
|
|
|
+ private float mFormSize = 8f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the size of the legend forms/shapes
|
|
|
+ */
|
|
|
+ private float mFormLineWidth = 3f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Line dash path effect used for shapes that consist of lines.
|
|
|
+ */
|
|
|
+ private DashPathEffect mFormLineDashEffect = null;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the space between the legend entries on a horizontal axis, default 6f
|
|
|
+ */
|
|
|
+ private float mXEntrySpace = 6f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the space between the legend entries on a vertical axis, default 5f
|
|
|
+ */
|
|
|
+ private float mYEntrySpace = 0f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the space between the legend entries on a vertical axis, default 2f
|
|
|
+ * private float mYEntrySpace = 2f; /** the space between the form and the
|
|
|
+ * actual label/text
|
|
|
+ */
|
|
|
+ private float mFormToTextSpace = 5f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the space that should be left between stacked forms
|
|
|
+ */
|
|
|
+ private float mStackSpace = 3f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the maximum relative size out of the whole chart view in percent
|
|
|
+ */
|
|
|
+ private float mMaxSizePercent = 0.95f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * default constructor
|
|
|
+ */
|
|
|
+ public Legend() {
|
|
|
+
|
|
|
+ this.mTextSize = Utils.convertDpToPixel(10f);
|
|
|
+ this.mXOffset = Utils.convertDpToPixel(5f);
|
|
|
+ this.mYOffset = Utils.convertDpToPixel(3f); // 2
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Constructor. Provide entries for the legend.
|
|
|
+ *
|
|
|
+ * @param entries
|
|
|
+ */
|
|
|
+ public Legend(LegendEntry[] entries) {
|
|
|
+ this();
|
|
|
+
|
|
|
+ if (entries == null) {
|
|
|
+ throw new IllegalArgumentException("entries array is NULL");
|
|
|
+ }
|
|
|
+
|
|
|
+ this.mEntries = entries;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors.
|
|
|
+ *
|
|
|
+ * @param entries
|
|
|
+ */
|
|
|
+ public void setEntries(List<LegendEntry> entries) {
|
|
|
+ mEntries = entries.toArray(new LegendEntry[entries.size()]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public LegendEntry[] getEntries() {
|
|
|
+ return mEntries;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the maximum length in pixels across all legend labels + formsize
|
|
|
+ * + formtotextspace
|
|
|
+ *
|
|
|
+ * @param p the paint object used for rendering the text
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getMaximumEntryWidth(Paint p) {
|
|
|
+
|
|
|
+ float max = 0f;
|
|
|
+ float maxFormSize = 0f;
|
|
|
+ float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace);
|
|
|
+
|
|
|
+ for (LegendEntry entry : mEntries) {
|
|
|
+ final float formSize = Utils.convertDpToPixel(
|
|
|
+ Float.isNaN(entry.formSize)
|
|
|
+ ? mFormSize : entry.formSize);
|
|
|
+ if (formSize > maxFormSize)
|
|
|
+ maxFormSize = formSize;
|
|
|
+
|
|
|
+ String label = entry.label;
|
|
|
+ if (label == null) continue;
|
|
|
+
|
|
|
+ float length = (float) Utils.calcTextWidth(p, label);
|
|
|
+
|
|
|
+ if (length > max)
|
|
|
+ max = length;
|
|
|
+ }
|
|
|
+
|
|
|
+ return max + maxFormSize + formToTextSpace;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the maximum height in pixels across all legend labels
|
|
|
+ *
|
|
|
+ * @param p the paint object used for rendering the text
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getMaximumEntryHeight(Paint p) {
|
|
|
+
|
|
|
+ float max = 0f;
|
|
|
+
|
|
|
+ for (LegendEntry entry : mEntries) {
|
|
|
+ String label = entry.label;
|
|
|
+ if (label == null) continue;
|
|
|
+
|
|
|
+ float length = (float) Utils.calcTextHeight(p, label);
|
|
|
+
|
|
|
+ if (length > max)
|
|
|
+ max = length;
|
|
|
+ }
|
|
|
+
|
|
|
+ return max;
|
|
|
+ }
|
|
|
+
|
|
|
+ public LegendEntry[] getExtraEntries() {
|
|
|
+
|
|
|
+ return mExtraEntries;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setExtra(List<LegendEntry> entries) {
|
|
|
+ mExtraEntries = entries.toArray(new LegendEntry[entries.size()]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setExtra(LegendEntry[] entries) {
|
|
|
+ if (entries == null)
|
|
|
+ entries = new LegendEntry[]{};
|
|
|
+ mExtraEntries = entries;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Entries that will be appended to the end of the auto calculated
|
|
|
+ * entries after calculating the legend.
|
|
|
+ * (if the legend has already been calculated, you will need to call notifyDataSetChanged()
|
|
|
+ * to let the changes take effect)
|
|
|
+ */
|
|
|
+ public void setExtra(int[] colors, String[] labels) {
|
|
|
+
|
|
|
+ List<LegendEntry> entries = new ArrayList<>();
|
|
|
+
|
|
|
+ for (int i = 0; i < Math.min(colors.length, labels.length); i++) {
|
|
|
+ final LegendEntry entry = new LegendEntry();
|
|
|
+ entry.formColor = colors[i];
|
|
|
+ entry.label = labels[i];
|
|
|
+
|
|
|
+ if (entry.formColor == ColorTemplate.COLOR_SKIP ||
|
|
|
+ entry.formColor == 0)
|
|
|
+ entry.form = LegendForm.NONE;
|
|
|
+ else if (entry.formColor == ColorTemplate.COLOR_NONE)
|
|
|
+ entry.form = LegendForm.EMPTY;
|
|
|
+
|
|
|
+ entries.add(entry);
|
|
|
+ }
|
|
|
+
|
|
|
+ mExtraEntries = entries.toArray(new LegendEntry[entries.size()]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sets a custom legend's entries array.
|
|
|
+ * * A null label will start a group.
|
|
|
+ * This will disable the feature that automatically calculates the legend
|
|
|
+ * entries from the datasets.
|
|
|
+ * Call resetCustom() to re-enable automatic calculation (and then
|
|
|
+ * notifyDataSetChanged() is needed to auto-calculate the legend again)
|
|
|
+ */
|
|
|
+ public void setCustom(LegendEntry[] entries) {
|
|
|
+
|
|
|
+ mEntries = entries;
|
|
|
+ mIsLegendCustom = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sets a custom legend's entries array.
|
|
|
+ * * A null label will start a group.
|
|
|
+ * This will disable the feature that automatically calculates the legend
|
|
|
+ * entries from the datasets.
|
|
|
+ * Call resetCustom() to re-enable automatic calculation (and then
|
|
|
+ * notifyDataSetChanged() is needed to auto-calculate the legend again)
|
|
|
+ */
|
|
|
+ public void setCustom(List<LegendEntry> entries) {
|
|
|
+
|
|
|
+ mEntries = entries.toArray(new LegendEntry[entries.size()]);
|
|
|
+ mIsLegendCustom = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Calling this will disable the custom legend entries (set by
|
|
|
+ * setCustom(...)). Instead, the entries will again be calculated
|
|
|
+ * automatically (after notifyDataSetChanged() is called).
|
|
|
+ */
|
|
|
+ public void resetCustom() {
|
|
|
+ mIsLegendCustom = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return true if a custom legend entries has been set default
|
|
|
+ * false (automatic legend)
|
|
|
+ */
|
|
|
+ public boolean isLegendCustom() {
|
|
|
+ return mIsLegendCustom;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the horizontal alignment of the legend
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public LegendHorizontalAlignment getHorizontalAlignment() {
|
|
|
+ return mHorizontalAlignment;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the horizontal alignment of the legend
|
|
|
+ *
|
|
|
+ * @param value
|
|
|
+ */
|
|
|
+ public void setHorizontalAlignment(LegendHorizontalAlignment value) {
|
|
|
+ mHorizontalAlignment = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the vertical alignment of the legend
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public LegendVerticalAlignment getVerticalAlignment() {
|
|
|
+ return mVerticalAlignment;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the vertical alignment of the legend
|
|
|
+ *
|
|
|
+ * @param value
|
|
|
+ */
|
|
|
+ public void setVerticalAlignment(LegendVerticalAlignment value) {
|
|
|
+ mVerticalAlignment = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the orientation of the legend
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public LegendOrientation getOrientation() {
|
|
|
+ return mOrientation;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the orientation of the legend
|
|
|
+ *
|
|
|
+ * @param value
|
|
|
+ */
|
|
|
+ public void setOrientation(LegendOrientation value) {
|
|
|
+ mOrientation = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns whether the legend will draw inside the chart or outside
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public boolean isDrawInsideEnabled() {
|
|
|
+ return mDrawInside;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets whether the legend will draw inside the chart or outside
|
|
|
+ *
|
|
|
+ * @param value
|
|
|
+ */
|
|
|
+ public void setDrawInside(boolean value) {
|
|
|
+ mDrawInside = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the text direction of the legend
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public LegendDirection getDirection() {
|
|
|
+ return mDirection;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the text direction of the legend
|
|
|
+ *
|
|
|
+ * @param pos
|
|
|
+ */
|
|
|
+ public void setDirection(LegendDirection pos) {
|
|
|
+ mDirection = pos;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the current form/shape that is set for the legend
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public LegendForm getForm() {
|
|
|
+ return mShape;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the form/shape of the legend forms
|
|
|
+ *
|
|
|
+ * @param shape
|
|
|
+ */
|
|
|
+ public void setForm(LegendForm shape) {
|
|
|
+ mShape = shape;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the size in dp of the legend forms, default 8f
|
|
|
+ *
|
|
|
+ * @param size
|
|
|
+ */
|
|
|
+ public void setFormSize(float size) {
|
|
|
+ mFormSize = size;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the size in dp of the legend forms
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getFormSize() {
|
|
|
+ return mFormSize;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the line width in dp for forms that consist of lines, default 3f
|
|
|
+ *
|
|
|
+ * @param size
|
|
|
+ */
|
|
|
+ public void setFormLineWidth(float size) {
|
|
|
+ mFormLineWidth = size;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the line width in dp for drawing forms that consist of lines
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getFormLineWidth() {
|
|
|
+ return mFormLineWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sets the line dash path effect used for shapes that consist of lines.
|
|
|
+ *
|
|
|
+ * @param dashPathEffect
|
|
|
+ */
|
|
|
+ public void setFormLineDashEffect(DashPathEffect dashPathEffect) {
|
|
|
+ mFormLineDashEffect = dashPathEffect;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return The line dash path effect used for shapes that consist of lines.
|
|
|
+ */
|
|
|
+ public DashPathEffect getFormLineDashEffect() {
|
|
|
+ return mFormLineDashEffect;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the space between the legend entries on a horizontal axis in
|
|
|
+ * pixels
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getXEntrySpace() {
|
|
|
+ return mXEntrySpace;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the space between the legend entries on a horizontal axis in pixels,
|
|
|
+ * converts to dp internally
|
|
|
+ *
|
|
|
+ * @param space
|
|
|
+ */
|
|
|
+ public void setXEntrySpace(float space) {
|
|
|
+ mXEntrySpace = space;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the space between the legend entries on a vertical axis in pixels
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getYEntrySpace() {
|
|
|
+ return mYEntrySpace;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the space between the legend entries on a vertical axis in pixels,
|
|
|
+ * converts to dp internally
|
|
|
+ *
|
|
|
+ * @param space
|
|
|
+ */
|
|
|
+ public void setYEntrySpace(float space) {
|
|
|
+ mYEntrySpace = space;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the space between the form and the actual label/text
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getFormToTextSpace() {
|
|
|
+ return mFormToTextSpace;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the space between the form and the actual label/text, converts to dp
|
|
|
+ * internally
|
|
|
+ *
|
|
|
+ * @param space
|
|
|
+ */
|
|
|
+ public void setFormToTextSpace(float space) {
|
|
|
+ this.mFormToTextSpace = space;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * returns the space that is left out between stacked forms (with no label)
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getStackSpace() {
|
|
|
+ return mStackSpace;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sets the space that is left out between stacked forms (with no label)
|
|
|
+ *
|
|
|
+ * @param space
|
|
|
+ */
|
|
|
+ public void setStackSpace(float space) {
|
|
|
+ mStackSpace = space;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the total width of the legend (needed width space)
|
|
|
+ */
|
|
|
+ public float mNeededWidth = 0f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * the total height of the legend (needed height space)
|
|
|
+ */
|
|
|
+ public float mNeededHeight = 0f;
|
|
|
+
|
|
|
+ public float mTextHeightMax = 0f;
|
|
|
+
|
|
|
+ public float mTextWidthMax = 0f;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * flag that indicates if word wrapping is enabled
|
|
|
+ */
|
|
|
+ private boolean mWordWrapEnabled = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Should the legend word wrap? / this is currently supported only for:
|
|
|
+ * BelowChartLeft, BelowChartRight, BelowChartCenter. / note that word
|
|
|
+ * wrapping a legend takes a toll on performance. / you may want to set
|
|
|
+ * maxSizePercent when word wrapping, to set the point where the text wraps.
|
|
|
+ * / default: false
|
|
|
+ *
|
|
|
+ * @param enabled
|
|
|
+ */
|
|
|
+ public void setWordWrapEnabled(boolean enabled) {
|
|
|
+ mWordWrapEnabled = enabled;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * If this is set, then word wrapping the legend is enabled. This means the
|
|
|
+ * legend will not be cut off if too long.
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public boolean isWordWrapEnabled() {
|
|
|
+ return mWordWrapEnabled;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The maximum relative size out of the whole chart view. / If the legend is
|
|
|
+ * to the right/left of the chart, then this affects the width of the
|
|
|
+ * legend. / If the legend is to the top/bottom of the chart, then this
|
|
|
+ * affects the height of the legend. / If the legend is the center of the
|
|
|
+ * piechart, then this defines the size of the rectangular bounds out of the
|
|
|
+ * size of the "hole". / default: 0.95f (95%)
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public float getMaxSizePercent() {
|
|
|
+ return mMaxSizePercent;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The maximum relative size out of the whole chart view. / If
|
|
|
+ * the legend is to the right/left of the chart, then this affects the width
|
|
|
+ * of the legend. / If the legend is to the top/bottom of the chart, then
|
|
|
+ * this affects the height of the legend. / default: 0.95f (95%)
|
|
|
+ *
|
|
|
+ * @param maxSize
|
|
|
+ */
|
|
|
+ public void setMaxSizePercent(float maxSize) {
|
|
|
+ mMaxSizePercent = maxSize;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<FSize> mCalculatedLabelSizes = new ArrayList<>(16);
|
|
|
+ private List<Boolean> mCalculatedLabelBreakPoints = new ArrayList<>(16);
|
|
|
+ private List<FSize> mCalculatedLineSizes = new ArrayList<>(16);
|
|
|
+
|
|
|
+ public List<FSize> getCalculatedLabelSizes() {
|
|
|
+ return mCalculatedLabelSizes;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<Boolean> getCalculatedLabelBreakPoints() {
|
|
|
+ return mCalculatedLabelBreakPoints;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<FSize> getCalculatedLineSizes() {
|
|
|
+ return mCalculatedLineSizes;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Calculates the dimensions of the Legend. This includes the maximum width
|
|
|
+ * and height of a single entry, as well as the total width and height of
|
|
|
+ * the Legend.
|
|
|
+ *
|
|
|
+ * @param labelpaint
|
|
|
+ */
|
|
|
+ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) {
|
|
|
+
|
|
|
+ float defaultFormSize = Utils.convertDpToPixel(mFormSize);
|
|
|
+ float stackSpace = Utils.convertDpToPixel(mStackSpace);
|
|
|
+ float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace);
|
|
|
+ float xEntrySpace = Utils.convertDpToPixel(mXEntrySpace);
|
|
|
+ float yEntrySpace = Utils.convertDpToPixel(mYEntrySpace);
|
|
|
+ boolean wordWrapEnabled = mWordWrapEnabled;
|
|
|
+ LegendEntry[] entries = mEntries;
|
|
|
+ int entryCount = entries.length;
|
|
|
+
|
|
|
+ mTextWidthMax = getMaximumEntryWidth(labelpaint);
|
|
|
+ mTextHeightMax = getMaximumEntryHeight(labelpaint);
|
|
|
+
|
|
|
+ switch (mOrientation) {
|
|
|
+ case VERTICAL: {
|
|
|
+
|
|
|
+ float maxWidth = 0f, maxHeight = 0f, width = 0f;
|
|
|
+ float labelLineHeight = Utils.getLineHeight(labelpaint);
|
|
|
+ boolean wasStacked = false;
|
|
|
+
|
|
|
+ for (int i = 0; i < entryCount; i++) {
|
|
|
+
|
|
|
+ LegendEntry e = entries[i];
|
|
|
+ boolean drawingForm = e.form != LegendForm.NONE;
|
|
|
+ float formSize = Float.isNaN(e.formSize)
|
|
|
+ ? defaultFormSize
|
|
|
+ : Utils.convertDpToPixel(e.formSize);
|
|
|
+ String label = e.label;
|
|
|
+
|
|
|
+ if (!wasStacked)
|
|
|
+ width = 0.f;
|
|
|
+
|
|
|
+ if (drawingForm) {
|
|
|
+ if (wasStacked)
|
|
|
+ width += stackSpace;
|
|
|
+ width += formSize;
|
|
|
+ }
|
|
|
+
|
|
|
+ // grouped forms have null labels
|
|
|
+ if (label != null) {
|
|
|
+
|
|
|
+ // make a step to the left
|
|
|
+ if (drawingForm && !wasStacked)
|
|
|
+ width += formToTextSpace;
|
|
|
+ else if (wasStacked) {
|
|
|
+ maxWidth = Math.max(maxWidth, width);
|
|
|
+ maxHeight += labelLineHeight + yEntrySpace;
|
|
|
+ width = 0.f;
|
|
|
+ wasStacked = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ width += Utils.calcTextWidth(labelpaint, label);
|
|
|
+
|
|
|
+ maxHeight += labelLineHeight + yEntrySpace;
|
|
|
+ } else {
|
|
|
+ wasStacked = true;
|
|
|
+ width += formSize;
|
|
|
+ if (i < entryCount - 1)
|
|
|
+ width += stackSpace;
|
|
|
+ }
|
|
|
+
|
|
|
+ maxWidth = Math.max(maxWidth, width);
|
|
|
+ }
|
|
|
+
|
|
|
+ mNeededWidth = maxWidth;
|
|
|
+ mNeededHeight = maxHeight;
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case HORIZONTAL: {
|
|
|
+
|
|
|
+ float labelLineHeight = Utils.getLineHeight(labelpaint);
|
|
|
+ float labelLineSpacing = Utils.getLineSpacing(labelpaint) + yEntrySpace;
|
|
|
+ float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent;
|
|
|
+
|
|
|
+ // Start calculating layout
|
|
|
+ float maxLineWidth = 0.f;
|
|
|
+ float currentLineWidth = 0.f;
|
|
|
+ float requiredWidth = 0.f;
|
|
|
+ int stackedStartIndex = -1;
|
|
|
+
|
|
|
+ mCalculatedLabelBreakPoints.clear();
|
|
|
+ mCalculatedLabelSizes.clear();
|
|
|
+ mCalculatedLineSizes.clear();
|
|
|
+
|
|
|
+ for (int i = 0; i < entryCount; i++) {
|
|
|
+
|
|
|
+ LegendEntry e = entries[i];
|
|
|
+ boolean drawingForm = e.form != LegendForm.NONE;
|
|
|
+ float formSize = Float.isNaN(e.formSize)
|
|
|
+ ? defaultFormSize
|
|
|
+ : Utils.convertDpToPixel(e.formSize);
|
|
|
+ String label = e.label;
|
|
|
+
|
|
|
+ mCalculatedLabelBreakPoints.add(false);
|
|
|
+
|
|
|
+ if (stackedStartIndex == -1) {
|
|
|
+ // we are not stacking, so required width is for this label
|
|
|
+ // only
|
|
|
+ requiredWidth = 0.f;
|
|
|
+ } else {
|
|
|
+ // add the spacing appropriate for stacked labels/forms
|
|
|
+ requiredWidth += stackSpace;
|
|
|
+ }
|
|
|
+
|
|
|
+ // grouped forms have null labels
|
|
|
+ if (label != null) {
|
|
|
+
|
|
|
+ mCalculatedLabelSizes.add(Utils.calcTextSize(labelpaint, label));
|
|
|
+ requiredWidth += drawingForm ? formToTextSpace + formSize : 0.f;
|
|
|
+ requiredWidth += mCalculatedLabelSizes.get(i).width;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ mCalculatedLabelSizes.add(FSize.getInstance(0.f, 0.f));
|
|
|
+ requiredWidth += drawingForm ? formSize : 0.f;
|
|
|
+
|
|
|
+ if (stackedStartIndex == -1) {
|
|
|
+ // mark this index as we might want to break here later
|
|
|
+ stackedStartIndex = i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (label != null || i == entryCount - 1) {
|
|
|
+
|
|
|
+ float requiredSpacing = currentLineWidth == 0.f ? 0.f : xEntrySpace;
|
|
|
+
|
|
|
+ if (!wordWrapEnabled // No word wrapping, it must fit.
|
|
|
+ // The line is empty, it must fit
|
|
|
+ || currentLineWidth == 0.f
|
|
|
+ // It simply fits
|
|
|
+ || (contentWidth - currentLineWidth >=
|
|
|
+ requiredSpacing + requiredWidth)) {
|
|
|
+ // Expand current line
|
|
|
+ currentLineWidth += requiredSpacing + requiredWidth;
|
|
|
+ } else { // It doesn't fit, we need to wrap a line
|
|
|
+
|
|
|
+ // Add current line size to array
|
|
|
+ mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight));
|
|
|
+ maxLineWidth = Math.max(maxLineWidth, currentLineWidth);
|
|
|
+
|
|
|
+ // Start a new line
|
|
|
+ mCalculatedLabelBreakPoints.set(
|
|
|
+ stackedStartIndex > -1 ? stackedStartIndex
|
|
|
+ : i, true);
|
|
|
+ currentLineWidth = requiredWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i == entryCount - 1) {
|
|
|
+ // Add last line size to array
|
|
|
+ mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight));
|
|
|
+ maxLineWidth = Math.max(maxLineWidth, currentLineWidth);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ stackedStartIndex = label != null ? -1 : stackedStartIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ mNeededWidth = maxLineWidth;
|
|
|
+ mNeededHeight = labelLineHeight
|
|
|
+ * (float) (mCalculatedLineSizes.size())
|
|
|
+ + labelLineSpacing *
|
|
|
+ (float) (mCalculatedLineSizes.size() == 0
|
|
|
+ ? 0
|
|
|
+ : (mCalculatedLineSizes.size() - 1));
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mNeededHeight += mYOffset;
|
|
|
+ mNeededWidth += mXOffset;
|
|
|
+ }
|
|
|
+}
|