/*
* 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 java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.Log;
import com.db.williamchart.R;
import com.db.chart.model.ChartEntry;
import com.db.chart.model.ChartSet;
/**
* 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;
// All given set values are 0
if(minLabelValue == maxLabelValue)
maxLabelValue += step;
}
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;
try {
for (ChartEntry e : chartView.data.get(0).getEntries()) {
result = chartView.style.getTextHeightBounds(e.getLabel());
if (result != 0)
break;
}
} catch (IndexOutOfBoundsException e) {
// Small and dirty hack;
result = 16;
}
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);
}