/*
* 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.content.res.TypedArray;
import android.graphics.Canvas;
import com.db.chart.model.ChartEntry;
import com.db.chart.model.ChartSet;
import com.db.williamchart.R;
import java.text.DecimalFormat;
import java.util.ArrayList;
/**
* Class responsible to control vertical measures, positions, yadda yadda.
* If the drawing is requested it will also take care of it.
*/
public abstract class AxisController {
public static enum LabelPosition {
NONE, OUTSIDE, INSIDE
}
/**
* Default step between labels
*/
private static final int DEFAULT_STEP = 1;
/**
* ChartView object
*/
protected ChartView chartView;
/**
* Distance between axis and label
*/
protected int distLabelToAxis;
/**
* Label's values formatted
*/
protected ArrayList<String> labels;
/**
* Label's values
*/
protected ArrayList<Integer> labelsValues;
/**
* Labels position
*/
protected ArrayList<Float> labelsPos;
/**
* Number of labels
*/
protected int nLabels;
/**
* none/inside/outside
*/
protected LabelPosition labelsPositioning;
/**
* Labels Metric to draw together with labels
*/
protected DecimalFormat labelFormat;
/**
* Maximum height that a label can have
*/
protected int labelHeight;
/**
* Maximum value of labels
*/
protected int maxLabelValue;
/**
* Minimum value of labels
*/
protected int minLabelValue;
/**
* Step between labels
*/
protected int step;
/**
* Screen step between labels
*/
protected float screenStep;
/**
* Whether the chart has Y Axis or not
*/
protected boolean hasAxis;
/**
* Starting X point of the axis
*/
protected float axisHorPosition;
/**
* Spacing for top label
*/
protected float topSpacing;
/**
* Horizontal border spacing for labels
*/
protected float borderSpacing;
/**
* Mandatory horizontal border when necessary (ex: BarCharts)
*/
protected float mandatoryBorderSpacing;
/**
* Define whether labels must be taken from data or calculated from values
*/
protected boolean handleValues;
public AxisController(ChartView view) {
chartView = view;
//Set DEFAULTS
distLabelToAxis = (int) chartView.getResources().getDimension(R.dimen.axis_dist_from_label);
mandatoryBorderSpacing = 0;
borderSpacing = 0;
topSpacing = 0;
step = DEFAULT_STEP;
labelsPositioning = LabelPosition.OUTSIDE;
labelFormat = new DecimalFormat();
axisHorPosition = 0;
minLabelValue = 0;
maxLabelValue = 0;
labelHeight = -1;
hasAxis = true;
handleValues = false;
}
public AxisController(ChartView chartView, TypedArray attrs) {
this(chartView);
}
/**
* Defines what will be the axis labels
*/
protected void defineLabels() {
labelsValues = calcLabels();
if (handleValues)
labels = getLabelsFromValues();
else
labels = getLabelsFromData();
nLabels = labels.size();
}
/**
* In case of a Chart that requires a mandatory border spacing (ex. BarChart)
*/
protected void defineMandatoryBorderSpacing(float innerStart, float innerEnd) {
if (mandatoryBorderSpacing == 1)
mandatoryBorderSpacing = (innerEnd - innerStart - borderSpacing * 2) / nLabels / 2;
}
/**
* Calculates the position of each label along the axis.
*
* @param innerStart Start inner position the chart
* @param innerEnd End inned position of chart
*/
protected void defineLabelsPos(float innerStart, float innerEnd) {
labelsPos = new ArrayList<Float>(nLabels);
screenStep = (innerEnd
- innerStart
- topSpacing
- borderSpacing * 2
- mandatoryBorderSpacing * 2)
/ (nLabels - 1);
float currPos = innerStart + borderSpacing + mandatoryBorderSpacing;
for (int i = 0; i < nLabels; i++) {
labelsPos.add(currPos);
currPos += screenStep;
}
}
/**
* Get labels from values calculated before.
*/
private ArrayList<String> getLabelsFromValues() {
int size = labelsValues.size();
ArrayList<String> result = new ArrayList<String>(size);
for (int i = 0; i < size; i++)
result.add(labelFormat.format(labelsValues.get(i)));
return result;
}
/**
* Get labels from chart data.
*/
protected ArrayList<String> getLabelsFromData() {
int size = chartView.data.get(0).size();
ArrayList<String> result = new ArrayList<String>(size);
for (int i = 0; i < size; i++)
result.add(chartView.data.get(0).getLabel(i));
return result;
}
/**
* Calculates the min/max value.
*
* @return {min, max} value
*/
private float[] calcBorderValues() {
float max = Integer.MIN_VALUE;
float min = Integer.MAX_VALUE;
for (ChartSet set : chartView.data) {
for (ChartEntry e : set.getEntries()) {
if (e.getValue() >= max)
max = e.getValue();
if (e.getValue() <= min)
min = e.getValue();
}
}
float[] result = {min, max};
return result;
}
/**
* Get labels based on the maximum value displayed
*
* @return result
*/
private ArrayList<Integer> calcLabels() {
float[] borderValues = calcBorderValues();
float minValue = borderValues[0];
float maxValue = borderValues[1];
//If not specified then calculate border labels
if (minLabelValue == 0 && maxLabelValue == 0) {
if (maxValue < 0)
maxLabelValue = 0;
else
maxLabelValue = (int) Math.ceil(maxValue);
if (minValue > 0)
minLabelValue = 0;
else
minLabelValue = (int) Math.floor(minValue);
while ((maxLabelValue - minLabelValue) % step != 0)
maxLabelValue += 1;
}
ArrayList<Integer> result = new ArrayList<Integer>();
int pos = minLabelValue;
while (pos <= maxLabelValue) {
result.add(pos);
pos += step;
}
//Set max Y axis label in case isn't already there
if (result.get(result.size() - 1) < maxLabelValue)
result.add(maxLabelValue);
return result;
}
protected int getLabelHeight() {
if (labelHeight == -1) {
int result = 0;
for (ChartEntry e : chartView.data.get(0).getEntries()) {
result = chartView.style.getTextHeightBounds(e.getLabel());
if (result != 0)
break;
}
labelHeight = result;
}
return labelHeight;
}
/**
* Method called from onDraw method to draw AxisController data.
*
* @param canvas {@link android.graphics.Canvas} to use while drawing the data
*/
abstract protected void draw(Canvas canvas);
}