/*
* 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.util.ArrayList;
import com.db.chart.model.Bar;
import com.db.chart.model.BarSet;
import com.db.chart.model.ChartSet;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
/**
* Implements a StackBar chart extending {@link BarChartView}
*/
public class StackBarChartView extends BaseStackBarChartView{
public StackBarChartView(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(Orientation.VERTICAL);
setMandatoryBorderSpacing();
}
public StackBarChartView(Context context) {
super(context);
setOrientation(Orientation.VERTICAL);
setMandatoryBorderSpacing();
}
/**
* 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
*/
@Override
public void onDrawChart(Canvas canvas, ArrayList<ChartSet> data) {
float verticalOffset;
float nextBottomY;
float dist;
int bottomSetIndex;
int topSetIndex;
float cornersFix;
float x;
float y;
BarSet barSet;
Bar bar;
int dataSize = data.size();
int setSize = data.get(0).size();
float innerChartBottom = this.getInnerChartBottom();
for (int i = 0; i < setSize; i++) {
// If bar needs background
if(style.hasBarBackground) {
drawBarBackground(canvas,
(int) (data.get(0).getEntry(i).getX() - barWidth / 2),
(int) this.getInnerChartTop(),
(int) (data.get(0).getEntry(i).getX() - barWidth / 2 + barWidth),
(int) this.getInnerChartBottom());
}
// Vertical offset to keep drawing bars on top of the others
verticalOffset = 0;
// Bottom of the next bar to be drawn
nextBottomY = innerChartBottom;
// Unfortunately necessary to discover which set is the bottom and top in case there
// are entries with value 0. To better understand check one of the methods.
bottomSetIndex = discoverBottomSet(i, data);
topSetIndex = discoverTopSet(i, data);
for(int j = 0; j < dataSize; j++){
barSet = (BarSet) data.get(j);
bar = (Bar) barSet.getEntry(i);
// If entry value is 0 it won't be drawn
if(!barSet.isVisible() || bar.getValue() <= 0)
continue;
x = bar.getX();
y = bar.getY();
style.barPaint.setColor(bar.getColor());
style.applyAlpha(style.barPaint, barSet.getAlpha());
// Distance from bottom to top of the bar
dist = innerChartBottom - y;
// Draw bar
if(j == bottomSetIndex){
drawBar(canvas,
(int) (x - barWidth/2), (int) (innerChartBottom - (dist + verticalOffset)),
(int) (x + barWidth/2), (int) nextBottomY);
if(bottomSetIndex != topSetIndex && style.cornerRadius != 0){
// Patch top corners of bar
cornersFix = (nextBottomY - (innerChartBottom - (dist + verticalOffset)))/2;
canvas.drawRect(new Rect((int) (x - barWidth/2),
(int) (innerChartBottom - (dist + verticalOffset)),
(int) (x + barWidth/2),
(int) (innerChartBottom - (dist + verticalOffset) + cornersFix)),
style.barPaint);
}
}else if(j == topSetIndex){
drawBar(canvas,
(int) (x - barWidth/2), (int) (innerChartBottom - (dist + verticalOffset)),
(int) (x + barWidth/2), (int) (nextBottomY));
// Patch bottom corners of bar
cornersFix = (nextBottomY - (innerChartBottom - (dist + verticalOffset))) / 2;
canvas.drawRect(new Rect((int) (x - barWidth/2),
(int) (nextBottomY - cornersFix),
(int) (x + barWidth/2),
(int) (nextBottomY)),
style.barPaint);
}else{// if(j != bottomSetIndex && j != topSetIndex){ // Middle sets
canvas.drawRect(new Rect((int) (x - barWidth/2),
(int) (innerChartBottom - (dist + verticalOffset)),
(int) (x + barWidth/2),
(int) (nextBottomY)),
style.barPaint);
}
nextBottomY = innerChartBottom - (dist + verticalOffset);
// In case bar is still hidden
if(dist != 0)
// Sum 2 to compensate the loss of precision in float
verticalOffset += dist + 2;
}
}
}
/**
* (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
*/
@Override
public void onPreDrawChart(ArrayList<ChartSet> data){
// Doing calculations here to avoid doing several times while drawing
// in case of animation
if(data.get(0).size() == 1)
barWidth = (this.getInnerChartRight() - this.getInnerChartLeft()
- this.horController.borderSpacing * 2);
else
calculateBarsWidth(-1, data.get(0).getEntry(0).getX(), data.get(0).getEntry(1).getX());
}
/**
* (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.
*
* 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
*/
@Override
public ArrayList<ArrayList<Region>> defineRegions(ArrayList<ChartSet> data) {
int dataSize = data.size();
int setSize = data.get(0).size();
float innerChartBottom = this.getInnerChartBottom();
ArrayList<ArrayList<Region>> result = new ArrayList<ArrayList<Region>>(dataSize);
for(int i = 0; i < dataSize; i++)
result.add(new ArrayList<Region>(setSize));
float verticalOffset;
float nextBottomY;
float dist;
BarSet barSet;
Bar bar;
for (int i = 0; i < setSize; i++) {
// Vertical offset to keep drawing bars on top of the others
verticalOffset = 0;
// Bottom of the next bar to be drawn
nextBottomY = innerChartBottom;
for(int j = 0; j < dataSize; j++){
barSet = (BarSet) data.get(j);
bar = (Bar) barSet.getEntry(i);
// Distance from bottom to top of the bar
dist = innerChartBottom - bar.getY();
result.get(j).add(new Region(
(int) (bar.getX() - barWidth/2),
(int) (innerChartBottom - (dist + verticalOffset)),
(int) (bar.getX() + barWidth/2),
(int)(nextBottomY)));
nextBottomY = innerChartBottom - (dist + verticalOffset);
// Sum X to compensate the loss of precision in float
verticalOffset += dist + 2;
}
}
return result;
}
}