/*
* Copyright 2014 Diogo Bernardino
*
* 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.db.chart.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.RelativeLayout;
import com.db.chart.listener.OnEntryClickListener;
import com.db.chart.model.ChartEntry;
import com.db.chart.model.ChartSet;
import com.db.chart.view.animation.Animation;
import com.db.chart.view.animation.style.BaseStyleAnimation;
import com.db.williamchart.R;
import java.text.DecimalFormat;
import java.util.ArrayList;
/**
* Abstract class to be extend to define any chart that implies axis.
*/
public abstract class ChartView extends RelativeLayout {
private static final String TAG = "chart.view.ChartView";
public static enum GridType {
FULL, VERTICAL, HORIZONTAL
}
public static enum Orientation {
HORIZONTAL, VERTICAL
}
protected Orientation orientation;
/**
* Chart borders
*/
protected int chartTop;
protected int chartBottom;
protected int chartLeft;
protected int chartRight;
/**
* Horizontal and Vertical position controllers
*/
protected XController horController;
protected YController verController;
/**
* Chart data to be displayed
*/
protected ArrayList<ChartSet> data;
/**
* Style applied to chart
*/
protected Style style;
/**
* Threshold limit line value
*/
private float mThresholdValue;
/**
* Chart data to be displayed
*/
private ArrayList<ArrayList<Region>> mRegions;
/**
* Index of last point clicked
*/
private int mIndexClicked;
private int mSetClicked;
/**
* Listeners to for touch events
*/
private OnEntryClickListener mEntryListener;
private OnClickListener mChartListener;
/**
* Drawing flag
*/
private boolean mReadyToDraw;
/**
* Drawing flag
*/
private boolean mIsDrawing;
/**
* Chart animation
*/
private Animation mAnim;
/**
* Keep record of data updates to be done
*/
private ArrayList<Pair<Integer, float[]>> mToUpdateValues;
/**
* Executed only before the chart is drawn for the first time.
* . borders are defined
* . digestData(data), to process the data to be drawn
* . defineRegions(), if listener has been registered
* this will define the chart regions to handle by onTouchEvent
*/
private OnPreDrawListener drawListener = new OnPreDrawListener() {
@SuppressLint("NewApi")
@Override
public boolean onPreDraw() {
ChartView.this.getViewTreeObserver().removeOnPreDrawListener(this);
style.init();
// Define chart frame
chartTop = getPaddingTop() + verController.getLabelHeight() / 2;
chartBottom = getMeasuredHeight() - getPaddingBottom();
chartLeft = getPaddingLeft();
chartRight = getMeasuredWidth() - getPaddingRight();
// Initialize controllers now that we have the measures
verController.init();
mThresholdValue = verController.parsePos(0, mThresholdValue);
// Mandatory: X axis after Y axis!
horController.init();
// Process data to define screen positions
digestData();
// Tells view to execute code before starting drawing
onPreDrawChart(data);
// Sets listener if needed
if (mEntryListener != null)
mRegions = defineRegions(data);
// Prepares the animation if needed and gets the first dump
// of data to be drawn
if (mAnim != null)
data = mAnim.prepareEnterAnimation(ChartView.this);
if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.HONEYCOMB)
ChartView.this.setLayerType(LAYER_TYPE_SOFTWARE, null);
return mReadyToDraw = true;
}
};
public ChartView(Context context, AttributeSet attrs) {
super(context, attrs);
horController = new XController(this,
context.getTheme().obtainStyledAttributes(attrs,
R.styleable.ChartAttrs, 0, 0));
verController = new YController(this,
context.getTheme().obtainStyledAttributes(attrs,
R.styleable.ChartAttrs, 0, 0));
style = new Style(context.getTheme().obtainStyledAttributes(attrs,
R.styleable.ChartAttrs, 0, 0));
init();
}
public ChartView(Context context) {
super(context);
horController = new XController(this);
verController = new YController(this);
style = new Style();
init();
}
private void init() {
mReadyToDraw = false;
mSetClicked = -1;
mIndexClicked = -1;
mThresholdValue = 0;
mIsDrawing = false;
data = new ArrayList<ChartSet>();
mRegions = new ArrayList<ArrayList<Region>>();
mToUpdateValues = new ArrayList<Pair<Integer, float[]>>();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
this.setWillNotDraw(false);
style.init();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
style.clean();
}
/*
* -----------------------
* Methods to be overriden
* -----------------------
*/
/**
* Convert {@link ChartEntry} values into screen points.
*/
private void digestData() {
int nEntries = data.get(0).size();
for (ChartSet set : data) {
for (int i = 0; i < nEntries; i++) {
set.getEntry(i)
.setCoordinates(horController.parsePos(i, set.getValue(i)),
verController.parsePos(i, set.getValue(i)));
}
}
}
/**
* (Optional) To be overriden in case the view needs to execute some code before
* starting the drawing.
*
* @param data Array of {@link ChartSet} to do the necessary preparation just before onDraw
*/
protected void onPreDrawChart(ArrayList<ChartSet> data) {
}
/**
* (Optional) To be overridden in order for each chart to define its own clickable regions.
* This way, classes extending ChartView will only define their clickable regions.
* <p/>
* Important: the returned vector must match the order of the data passed
* by the user. This ensures that onTouchEvent will return the correct index.
*
* @param data {@link java.util.ArrayList} of {@link com.db.chart.model.ChartSet}
* to use while defining each region of a {@link com.db.chart.view.BarChartView}
* @return {@link java.util.ArrayList} of {@link android.graphics.Region} with regions
* where click will be detected
*/
protected ArrayList<ArrayList<Region>> defineRegions(ArrayList<ChartSet> data) {
return mRegions;
}
;
/**
* Method responsible to draw bars with the parsed screen points.
*
* @param canvas The canvas to draw on
* @param data {@link java.util.ArrayList} of {@link com.db.chart.model.ChartSet}
* to use while drawing the Chart
*/
abstract protected void onDrawChart(Canvas canvas, ArrayList<ChartSet> data);
/**
* Set new data to the chart and invalidates the view to be then drawn.
*
* @param set {@link ChartSet} object.
*/
public void addData(ChartSet set) {
if (!data.isEmpty() && set.size() != data.get(0).size())
Log.e(TAG, "The number of labels between sets doesn't match.",
new IllegalArgumentException());
data.add(set);
}
/**
* Add full chart data.
*
* @param data An array of {@link ChartSet}
*/
public void addData(ArrayList<ChartSet> data) {
this.data = data;
}
/**
* Base method when a show chart occurs
*/
private void display() {
this.getViewTreeObserver().addOnPreDrawListener(drawListener);
postInvalidate();
}
/**
* Show chart data
*/
public void show() {
for (ChartSet set : data)
set.setVisible(true);
display();
}
/**
* Show only a specific chart dataset.
*
* @param setIndex Dataset's index to be displayed
*/
public void show(int setIndex) {
data.get(setIndex).setVisible(true);
display();
}
/**
* Starts the animation given as parameter.
*
* @param anim Animation used while showing and updating sets
*/
public void show(Animation anim) {
mAnim = anim;
show();
}
/**
* Dismiss chart data.
*/
public void dismiss() {
dismiss(mAnim);
}
/**
* Dismiss a specific chart dataset.
*
* @param setIndex Dataset's index to be dismissed
*/
public void dismiss(int setIndex) {
data.get(setIndex).setVisible(false);
invalidate();
}
/**
* Dismiss chart data with animation.
*
* @param anim Animation used to exit
*/
public void dismiss(Animation anim) {
if (anim != null) {
mAnim = anim;
final Runnable endAction = mAnim.getEndAction();
mAnim.setEndAction(new Runnable() {
@Override
public void run() {
if (endAction != null)
endAction.run();
data.clear();
invalidate();
}
});
data = mAnim.prepareExitAnimation(this);
} else {
data.clear();
}
invalidate();
}
/**
* Method not expected to be used often. More for testing.
* Resets chart state to insert new configuration.
*/
public void reset() {
data.clear();
mRegions.clear();
mToUpdateValues.clear();
verController.minLabelValue = 0;
verController.maxLabelValue = 0;
if (horController.mandatoryBorderSpacing != 0)
horController.mandatoryBorderSpacing = 1;
style.thresholdPaint = null;
style.gridPaint = null;
style.hasHorizontalGrid = false;
style.hasVerticalGrid = false;
}
/**
* Update set values. Animation support in case previously added.
*
* @param setIndex Index of set to be updated
* @param values Array of new values. Array length must match current data
*/
public ChartView updateValues(int setIndex, float[] values) {
if (values.length != data.get(setIndex).size())
Log.e(TAG, "New values size doesn't match current dataset size.", new IllegalArgumentException());
data.get(setIndex).updateValues(values);
return this;
}
/**
* Notify ChartView about updated values. ChartView will be validated.
*/
public void notifyDataUpdate() {
ArrayList<float[][]> oldCoords = new ArrayList<float[][]>(data.size());
ArrayList<float[][]> newCoords = new ArrayList<float[][]>(data.size());
for (ChartSet set : data)
oldCoords.add(set.getScreenPoints());
digestData();
for (ChartSet set : data)
newCoords.add(set.getScreenPoints());
mRegions = defineRegions(data);
if (mAnim != null)
data = mAnim.prepareUpdateAnimation(this, oldCoords, newCoords);
mToUpdateValues.clear();
invalidate();
}
/**
* Adds a tooltip to ChartView. If is not the case already,
* the whole tooltip is forced to be inside ChartView bounds.
*
* @param tooltip Tooltip view to be added
* @param bool False if the tooltip should not be forced to be inside ChartView.
* You may want to take care of it
*/
public void showTooltip(View tooltip, boolean bool) {
if (bool) {
final LayoutParams layoutParams = (LayoutParams) tooltip.getLayoutParams();
if (layoutParams.leftMargin < chartLeft - getPaddingLeft())
layoutParams.leftMargin = (int) chartLeft - getPaddingLeft();
if (layoutParams.topMargin < chartTop - getPaddingTop())
layoutParams.topMargin = (int) chartTop - getPaddingTop();
if (layoutParams.leftMargin + layoutParams.width
> chartRight - getPaddingRight())
layoutParams.leftMargin -= layoutParams.width
- (chartRight - getPaddingRight()
- layoutParams.leftMargin);
if (layoutParams.topMargin + layoutParams.height
> getInnerChartBottom() - getPaddingBottom())
layoutParams.topMargin -= layoutParams.height
- (getInnerChartBottom() - getPaddingBottom()
- layoutParams.topMargin);
tooltip.setLayoutParams(layoutParams);
}
this.addView(tooltip);
}
/**
* Adds a tooltip to ChartView. If is not the case already,
* the whole tooltip is forced to be inside ChartView bounds.
*
* @param tooltip Tooltip view to be added
*/
public void showTooltip(View tooltip) {
showTooltip(tooltip, true);
}
/**
* Removes tooltip from ChartView.
*
* @param tooltip View to be removed
*/
public void dismissTooltip(View tooltip) {
this.removeView(tooltip);
}
/**
* Removes all tooltips from ChartView.
*/
public void dismissAllTooltips() {
this.removeAllViews();
}
/**
* Animate {@link ChartSet}.
*
* @param index Position of {@link ChartSet}
* @param anim Animation extending {@link BaseStyleAnimation}
*/
public void animateSet(int index, BaseStyleAnimation anim) {
anim.play(this, this.data.get(index));
}
/**
* Asks the view if it is able to draw now.
*/
public boolean canIPleaseAskYouToDraw() {
return !mIsDrawing;
}
/*
* -------------
* Draw Methods
* -------------
*/
@Override
protected void onDraw(Canvas canvas) {
try { // we know this will fail when trying to animate
mIsDrawing = true;
super.onDraw(canvas);
if (mReadyToDraw) {
//long time = System.currentTimeMillis();
// Draw grid
if (style.hasVerticalGrid)
drawVerticalGrid(canvas);
if (style.hasHorizontalGrid)
drawHorizontalGrid(canvas);
// Draw Axis Y
verController.draw(canvas);
// Draw data
if (!data.isEmpty())
onDrawChart(canvas, data);
// Draw axis X
horController.draw(canvas);
if (style.thresholdPaint != null)
drawThresholdLine(canvas);
//System.out.println("Time drawing "+(System.currentTimeMillis() - time));
}
mIsDrawing = false;
} catch (Exception e) {
}
}
private void drawThresholdLine(Canvas canvas) {
canvas.drawLine(getInnerChartLeft(),
mThresholdValue,
getInnerChartRight(),
mThresholdValue,
style.thresholdPaint);
}
private void drawVerticalGrid(Canvas canvas) {
// Draw vertical grid lines
for (Float pos : horController.labelsPos) {
canvas.drawLine(pos,
getInnerChartBottom(),
pos,
getInnerChartTop(),
style.gridPaint);
}
// If border diff than 0 inner chart sides must have lines
if (horController.borderSpacing != 0 || horController.mandatoryBorderSpacing != 0) {
if (!verController.hasAxis)
canvas.drawLine(getInnerChartLeft(),
getInnerChartBottom(),
getInnerChartLeft(),
getInnerChartTop(),
style.gridPaint);
canvas.drawLine(getInnerChartRight(),
getInnerChartBottom(),
getInnerChartRight(),
getInnerChartTop(),
style.gridPaint);
}
}
private void drawHorizontalGrid(Canvas canvas) {
// Draw horizontal grid lines
for (Float pos : verController.labelsPos) {
canvas.drawLine(getInnerChartLeft(),
pos,
getInnerChartRight(),
pos,
style.gridPaint);
}
// If there's no axis
if (!horController.hasAxis)
canvas.drawLine(getInnerChartLeft(),
getInnerChartBottom(),
getInnerChartRight(),
getInnerChartBottom(),
style.gridPaint);
}
/*
* --------------
* Click Handler
* --------------
*/
/**
* The method listens chart clicks and checks whether it intercepts
* a known Region. It will then use the registered Listener.onClick
* to return the region's index.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mAnim == null || !mAnim.isPlaying())
if (event.getAction() == MotionEvent.ACTION_DOWN &&
mEntryListener != null && mRegions != null) {
//Check if ACTION_DOWN over any ScreenPoint region.
int nSets = mRegions.size();
int nEntries = mRegions.get(0).size();
for (int i = 0; i < nSets; i++) {
for (int j = 0; j < nEntries; j++) {
if (mRegions.get(i).get(j)
.contains((int) event.getX(),
(int) event.getY())) {
mSetClicked = i;
mIndexClicked = j;
}
}
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (mEntryListener != null &&
mSetClicked != -1 &&
mIndexClicked != -1) {
if (mRegions.get(mSetClicked).get(mIndexClicked)
.contains((int) event.getX(),
(int) event.getY())) {
mEntryListener.onClick(mSetClicked,
mIndexClicked,
new Rect(mRegions.get(mSetClicked)
.get(mIndexClicked)
.getBounds().left - getPaddingLeft(),
mRegions.get(mSetClicked)
.get(mIndexClicked)
.getBounds().top - getPaddingTop(),
mRegions.get(mSetClicked)
.get(mIndexClicked)
.getBounds().right - getPaddingLeft(),
mRegions.get(mSetClicked)
.get(mIndexClicked)
.getBounds().bottom - getPaddingTop()));
}
mSetClicked = -1;
mIndexClicked = -1;
} else if (mChartListener != null) {
mChartListener.onClick(this);
}
}
return true;
}
/*
* --------
* Getters
* --------
*/
public Orientation getOrientation() {
return orientation;
}
/**
* Inner Chart refers only to the area where chart data will be draw,
* excluding labels, axis, etc.
*
* @return Position of the inner bottom side of the chart
*/
public float getInnerChartBottom() {
return verController.getInnerChartBottom();
}
/**
* Inner Chart refers only to the area where chart data will be draw,
* excluding labels, axis, etc.
*
* @return Position of the inner left side of the chart
*/
public float getInnerChartLeft() {
return verController.getInnerChartLeft();
}
/**
* Inner Chart refers only to the area where chart data will be draw,
* excluding labels, axis, etc.
*
* @return Position of the inner right side of the chart
*/
public float getInnerChartRight() {
return horController.getInnerChartRight();
}
/**
* Inner Chart refers only to the area where chart data will be draw,
* excluding labels, axis, etc.
*
* @return Position of the inner top side of the chart
*/
public float getInnerChartTop() {
return chartTop;
}
/**
* Returns the position of 0 value on chart.
*
* @return Position of 0 value on chart
*/
public float getZeroPosition() {
if (orientation == Orientation.VERTICAL)
return verController.parsePos(0, 0);
else
return horController.parsePos(0, 0);
}
/**
* Get the step used between Y values.
*
* @return step
*/
protected int getStep() {
if (orientation == Orientation.VERTICAL)
return verController.step;
else
return horController.step;
}
/**
* Get chart's border spacing.
*
* @return spacing
*/
protected float getBorderSpacing() {
if (orientation == Orientation.VERTICAL)
return verController.borderSpacing;
else
return horController.borderSpacing;
}
/**
* Get the whole data owned by the chart.
*
* @return List of {@link com.db.chart.model.ChartSet} owned by the chart
*/
public ArrayList<ChartSet> getData() {
return data;
}
/**
* Get the list of {@link android.graphics.Region} associated to each entry of a ChartSet.
*
* @param index {@link com.db.chart.model.ChartSet} index
* @return The list of {@link android.graphics.Region} for the specified dataset
*/
public ArrayList<Region> getEntriesRegion(int index) {
return mRegions.get(index);
}
/**
* Get the current {@link com.db.chart.view.animation.Animation}
* held by {@link com.db.chart.view.ChartView}.
* Useful, for instance, to define another endAction.
*
* @return Current {@link com.db.chart.view.animation.Animation}
*/
public Animation getChartAnimation() {
return mAnim;
}
/*
* --------
* Setters
* --------
*/
/**
* Sets the chart's orientation.
*
* @param orien Orientation.HORIZONTAL | Orientation.VERTICAL
*/
protected void setOrientation(Orientation orien) {
orientation = orien;
if (orientation == Orientation.VERTICAL) {
verController.handleValues = true;
} else {
horController.handleValues = true;
}
}
/**
* Show/Hide Y labels and respective axis.
*
* @param position NONE - No labels
* OUTSIDE - Labels will be positioned outside the chart
* INSIDE - Labels will be positioned inside the chart
*/
public ChartView setYLabels(YController.LabelPosition position) {
verController.labelsPositioning = position;
return this;
}
/**
* Show/Hide X labels and respective axis.
*
* @param position NONE - No labels
* OUTSIDE - Labels will be positioned outside the chart
* INSIDE - Labels will be positioned inside the chart
*/
public ChartView setXLabels(XController.LabelPosition position) {
horController.labelsPositioning = position;
return this;
}
/**
* Set the format to be added to Y labels.
*
* @param format Format to be applied
*/
public ChartView setLabelsFormat(DecimalFormat format) {
if (orientation == Orientation.VERTICAL)
verController.labelFormat = format;
else
horController.labelFormat = format;
return this;
}
/**
* @param color
* @return
*/
public ChartView setLabelsColor(int color) {
style.labelsColor = color;
style.labelsPaint.setColor(color);
return this;
}
/**
* @param size
* @return
*/
public ChartView setFontSize(int size) {
style.fontSize = size;
style.labelsPaint.setTextSize(size);
return this;
}
/**
* @param typeface
* @return
*/
public ChartView setTypeface(Typeface typeface) {
style.typeface = typeface;
style.labelsPaint.setTypeface(typeface);
return this;
}
/**
* Show/Hide X axis.
*
* @param bool If true axis won't be visible
*/
public ChartView setXAxis(boolean bool) {
horController.hasAxis = bool;
return this;
}
/**
* Show/Hide Y axis.
*
* @param bool If true axis won't be visible
*/
public ChartView setYAxis(boolean bool) {
verController.hasAxis = bool;
return this;
}
/**
* A step is seen as the step to be defined between 2 labels. As an
* example a step of 2 with a maxAxisValue of 6 will end up with
* {0, 2, 4, 6} as labels.
*
* @param maxValue The maximum value that Y axis will have as a label
* @param step (real) value distance from every label
*/
public ChartView setAxisBorderValues(int minValue, int maxValue, int step) {
if ((maxValue - minValue) % step != 0)
Log.i(TAG, "Step value must be a divisor of distance between " +
"minValue and maxValue");
if (orientation == Orientation.VERTICAL) {
verController.maxLabelValue = maxValue;
verController.minLabelValue = minValue;
verController.step = step;
} else {
horController.maxLabelValue = maxValue;
horController.minLabelValue = minValue;
horController.step = step;
}
return this;
}
/**
* Define the thickness of the axis.
*
* @param thickness size of the thickness
*/
public ChartView setAxisThickness(float thickness) {
style.axisThickness = thickness;
style.chartPaint.setStrokeWidth(thickness);
return this;
}
/**
* Define the color of the axis.
*
* @param color color of the axis
* @return
*/
public ChartView setAxisColor(int color) {
style.axisColor = color;
style.chartPaint.setColor(color);
return this;
}
/**
* A step is seen as the step to be defined between 2 labels.
* As an example a step of 2 with a max label value of 6 will end
* up with {0, 2, 4, 6} as labels.
*
* @param step (real) value distance from every label
*/
public ChartView setStep(int step) {
if (step <= 0)
Log.e(TAG, "Step can't be lower or equal to 0", new IllegalArgumentException());
if (orientation == Orientation.VERTICAL)
verController.step = step;
else
horController.step = step;
return this;
}
/**
* Register a listener to be called when the chart is clicked.
*
* @param listener
*/
public void setOnEntryClickListener(OnEntryClickListener listener) {
this.mEntryListener = listener;
}
/**
* Register a listener to be called when the chart is clicked.
*
* @param listener
*/
@Override
public void setOnClickListener(OnClickListener listener) {
this.mChartListener = listener;
}
/**
* @param spacing Spacing between left/right of the chart and the first/last label
*/
public ChartView setBorderSpacing(float spacing) {
if (orientation == Orientation.VERTICAL)
horController.borderSpacing = spacing;
else
verController.borderSpacing = spacing;
return this;
}
/**
* @param spacing Spacing between top of the chart and the first label
*/
public ChartView setTopSpacing(float spacing) {
if (orientation == Orientation.VERTICAL)
verController.topSpacing = spacing;
else
horController.borderSpacing = spacing;
return this;
}
/**
* Apply grid to chart.
*
* @param type {@link GridType} for grid
* @param paint The Paint instance that will be used to draw the grid
* If null the grid won't be drawn
*/
public ChartView setGrid(GridType type, Paint paint) {
if (type.compareTo(GridType.FULL) == 0) {
style.hasVerticalGrid = true;
style.hasHorizontalGrid = true;
} else if (type.compareTo(GridType.VERTICAL) == 0) {
style.hasVerticalGrid = true;
} else {
style.hasHorizontalGrid = true;
}
style.gridPaint = paint;
return this;
}
/**
* To set a threshold line to the chart.
*
* @param value Threshold value.
* @param paint The Paint instance that will be used to draw the grid
* If null the grid won't be drawn
*/
public ChartView setThresholdLine(float value, Paint paint) {
mThresholdValue = value;
style.thresholdPaint = paint;
return this;
}
/**
* Mandatory horizontal border when necessary (ex: BarCharts)
* Sets the attribute depending on the chart's orientation.
* e.g. If orientation is VERTICAL it means that this attribute must be handled
* by horizontal axis and not the vertical axis.
*/
protected ChartView setMandatoryBorderSpacing() {
if (orientation == Orientation.VERTICAL)
horController.mandatoryBorderSpacing = 1;
else
verController.mandatoryBorderSpacing = 1;
return this;
}
/*
* ----------
* Style
* ----------
*/
/**
* Class responsible to style the Graph!
* Can be instantiated with or without attributes.
*/
class Style {
private final static int DEFAULT_COLOR = -16777216;
/**
* Chart
*/
protected Paint chartPaint;
protected float axisThickness;
protected int axisColor;
/**
* Grid
*/
protected Paint gridPaint;
protected boolean hasHorizontalGrid;
protected boolean hasVerticalGrid;
/**
* Threshold Line *
*/
private Paint thresholdPaint;
/**
* Font
*/
protected Paint labelsPaint;
protected int labelsColor;
protected float fontSize;
protected Typeface typeface;
protected Style() {
hasHorizontalGrid = false;
hasVerticalGrid = false;
axisColor = DEFAULT_COLOR;
axisThickness = (float) getResources().getDimension(R.dimen.grid_thickness);
labelsColor = DEFAULT_COLOR;
fontSize = getResources().getDimension(R.dimen.font_size);
}
protected Style(TypedArray attrs) {
hasHorizontalGrid = false;
hasVerticalGrid = false;
axisColor = attrs.getColor(
R.styleable.ChartAttrs_chart_axisColor,
DEFAULT_COLOR);
axisThickness = attrs.getDimension(
R.styleable.ChartAttrs_chart_axisThickness,
getResources().getDimension(R.dimen.axis_thickness));
labelsColor = attrs.getColor(
R.styleable.ChartAttrs_chart_labelColor, DEFAULT_COLOR);
fontSize = attrs.getDimension(
R.styleable.ChartAttrs_chart_fontSize,
getResources().getDimension(R.dimen.font_size));
String typefaceName = attrs.getString(R.styleable.ChartAttrs_chart_typeface);
if (typefaceName != null)
typeface = Typeface.createFromAsset(getResources().
getAssets(), typefaceName);
}
private void init() {
chartPaint = new Paint();
chartPaint.setColor(axisColor);
chartPaint.setStyle(Paint.Style.STROKE);
chartPaint.setStrokeWidth(axisThickness);
chartPaint.setAntiAlias(true);
labelsPaint = new Paint();
labelsPaint.setColor(labelsColor);
labelsPaint.setStyle(Paint.Style.FILL_AND_STROKE);
labelsPaint.setAntiAlias(true);
labelsPaint.setTextSize(fontSize);
labelsPaint.setTypeface(typeface);
}
public void clean() {
chartPaint = null;
labelsPaint = null;
gridPaint = null;
thresholdPaint = null;
}
protected int getTextHeightBounds(String character) {
if (character != "") {
Rect bounds = new Rect();
style.labelsPaint
.getTextBounds(character,
0,
1,
bounds);
return bounds.height();
}
return 0;
}
}
}