/**
* GraphView
* Copyright 2016 Jonas Gehring
*
* 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.jjoe64.graphview;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.TypedValue;
import com.jjoe64.graphview.series.Series;
import java.util.ArrayList;
import java.util.List;
/**
* The default renderer for the legend box
*
* @author jjoe64
*/
public class LegendRenderer {
/**
* wrapped styles regarding to the
* legend
*/
private final class Styles {
float textSize;
int spacing;
int padding;
int width;
int backgroundColor;
int textColor;
int margin;
LegendAlign align;
Point fixedPosition;
}
/**
* alignment of the legend
*/
public enum LegendAlign {
/**
* top right corner
*/
TOP,
/**
* middle right
*/
MIDDLE,
/**
* bottom right corner
*/
BOTTOM
}
/**
* wrapped styles
*/
private Styles mStyles;
/**
* reference to the graphview
*/
private final GraphView mGraphView;
/**
* flag whether legend will be
* drawn
*/
private boolean mIsVisible;
/**
* paint for the drawing
*/
private Paint mPaint;
/**
* cached legend width
* this will be filled in the drawing.
* Can be cleared via {@link #resetStyles()}
*/
private int cachedLegendWidth;
/**
* creates legend renderer
*
* @param graphView regarding graphview
*/
public LegendRenderer(GraphView graphView) {
mGraphView = graphView;
mIsVisible = false;
mPaint = new Paint();
mPaint.setTextAlign(Paint.Align.LEFT);
mStyles = new Styles();
cachedLegendWidth = 0;
resetStyles();
}
/**
* resets the styles to the defaults
* and clears the legend width cache
*/
public void resetStyles() {
mStyles.align = LegendAlign.MIDDLE;
mStyles.textSize = mGraphView.getGridLabelRenderer().getTextSize();
mStyles.spacing = (int) (mStyles.textSize / 5);
mStyles.padding = (int) (mStyles.textSize / 2);
mStyles.width = 0;
mStyles.backgroundColor = Color.argb(180, 100, 100, 100);
mStyles.margin = (int) (mStyles.textSize / 5);
// get matching styles from theme
TypedValue typedValue = new TypedValue();
mGraphView.getContext().getTheme().resolveAttribute(android.R.attr.textAppearanceSmall, typedValue, true);
int color1;
try {
TypedArray array = mGraphView.getContext().obtainStyledAttributes(typedValue.data, new int[]{
android.R.attr.textColorPrimary});
color1 = array.getColor(0, Color.BLACK);
array.recycle();
} catch (Exception e) {
color1 = Color.BLACK;
}
mStyles.textColor = color1;
cachedLegendWidth = 0;
}
/**
* draws the legend if it is visible
*
* @param canvas canvas
* @see #setVisible(boolean)
*/
public void draw(Canvas canvas) {
if (!mIsVisible) return;
mPaint.setTextSize(mStyles.textSize);
int shapeSize = (int) (mStyles.textSize*0.8d);
List<Series> allSeries = new ArrayList<Series>();
allSeries.addAll(mGraphView.getSeries());
if (mGraphView.mSecondScale != null) {
allSeries.addAll(mGraphView.getSecondScale().getSeries());
}
// width
int legendWidth = mStyles.width;
if (legendWidth == 0) {
// auto
legendWidth = cachedLegendWidth;
if (legendWidth == 0) {
Rect textBounds = new Rect();
for (Series s : allSeries) {
if (s.getTitle() != null) {
mPaint.getTextBounds(s.getTitle(), 0, s.getTitle().length(), textBounds);
legendWidth = Math.max(legendWidth, textBounds.width());
}
}
if (legendWidth == 0) legendWidth = 1;
// add shape size
legendWidth += shapeSize+mStyles.padding*2 + mStyles.spacing;
cachedLegendWidth = legendWidth;
}
}
// rect
float legendHeight = (mStyles.textSize+mStyles.spacing)*allSeries.size() -mStyles.spacing;
float lLeft;
float lTop;
if (mStyles.fixedPosition != null) {
// use fied position
lLeft = mGraphView.getGraphContentLeft() + mStyles.margin + mStyles.fixedPosition.x;
lTop = mGraphView.getGraphContentTop() + mStyles.margin + mStyles.fixedPosition.y;
} else {
lLeft = mGraphView.getGraphContentLeft() + mGraphView.getGraphContentWidth() - legendWidth - mStyles.margin;
switch (mStyles.align) {
case TOP:
lTop = mGraphView.getGraphContentTop() + mStyles.margin;
break;
case MIDDLE:
lTop = mGraphView.getHeight() / 2 - legendHeight / 2;
break;
default:
lTop = mGraphView.getGraphContentTop() + mGraphView.getGraphContentHeight() - mStyles.margin - legendHeight - 2*mStyles.padding;
}
}
float lRight = lLeft+legendWidth;
float lBottom = lTop+legendHeight+2*mStyles.padding;
mPaint.setColor(mStyles.backgroundColor);
canvas.drawRoundRect(new RectF(lLeft, lTop, lRight, lBottom), 8, 8, mPaint);
int i=0;
for (Series series : allSeries) {
mPaint.setColor(series.getColor());
canvas.drawRect(new RectF(lLeft+mStyles.padding, lTop+mStyles.padding+(i*(mStyles.textSize+mStyles.spacing)), lLeft+mStyles.padding+shapeSize, lTop+mStyles.padding+(i*(mStyles.textSize+mStyles.spacing))+shapeSize), mPaint);
if (series.getTitle() != null) {
mPaint.setColor(mStyles.textColor);
canvas.drawText(series.getTitle(), lLeft+mStyles.padding+shapeSize+mStyles.spacing, lTop+mStyles.padding+mStyles.textSize+(i*(mStyles.textSize+mStyles.spacing)), mPaint);
}
i++;
}
}
/**
* @return the flag whether the legend will be drawn
*/
public boolean isVisible() {
return mIsVisible;
}
/**
* set the flag whether the legend will be drawn
*
* @param mIsVisible visible flag
*/
public void setVisible(boolean mIsVisible) {
this.mIsVisible = mIsVisible;
}
/**
* @return font size
*/
public float getTextSize() {
return mStyles.textSize;
}
/**
* sets the font size. this will clear
* the internal legend width cache
*
* @param textSize font size
*/
public void setTextSize(float textSize) {
mStyles.textSize = textSize;
cachedLegendWidth = 0;
}
/**
* @return the spacing between the text lines
*/
public int getSpacing() {
return mStyles.spacing;
}
/**
* set the spacing between the text lines
*
* @param spacing the spacing between the text lines
*/
public void setSpacing(int spacing) {
mStyles.spacing = spacing;
}
/**
* padding is the space between the edge of the box
* and the beginning of the text
*
* @return padding from edge to text
*/
public int getPadding() {
return mStyles.padding;
}
/**
* padding is the space between the edge of the box
* and the beginning of the text
*
* @param padding padding from edge to text
*/
public void setPadding(int padding) {
mStyles.padding = padding;
}
/**
* the width of the box exclusive padding
*
* @return the width of the box
* 0 => auto
*/
public int getWidth() {
return mStyles.width;
}
/**
* the width of the box exclusive padding
* @param width the width of the box exclusive padding
* 0 => auto
*/
public void setWidth(int width) {
mStyles.width = width;
}
/**
* @return background color of the box
* it is recommended to use semi-transparent
* color.
*/
public int getBackgroundColor() {
return mStyles.backgroundColor;
}
/**
* @param backgroundColor background color of the box
* it is recommended to use semi-transparent
* color.
*/
public void setBackgroundColor(int backgroundColor) {
mStyles.backgroundColor = backgroundColor;
}
/**
* @return margin from the edge of the box
* to the corner of the graphview
*/
public int getMargin() {
return mStyles.margin;
}
/**
* @param margin margin from the edge of the box
* to the corner of the graphview
*/
public void setMargin(int margin) {
mStyles.margin = margin;
}
/**
* @return the vertical alignment of the box
*/
public LegendAlign getAlign() {
return mStyles.align;
}
/**
* @param align the vertical alignment of the box
*/
public void setAlign(LegendAlign align) {
mStyles.align = align;
}
/**
* @return font color
*/
public int getTextColor() {
return mStyles.textColor;
}
/**
* @param textColor font color
*/
public void setTextColor(int textColor) {
mStyles.textColor = textColor;
}
/**
* Use fixed coordinates to position the legend.
* This will override the align setting.
*
* @param x x coordinates in pixel
* @param y y coordinates in pixel
*/
public void setFixedPosition(int x, int y) {
mStyles.fixedPosition = new Point(x, y);
}
}