package com.cloud.charts4a.chart.render;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import com.cloud.charts4a.chart.attrib.AbstractXAxisAttribs;
import com.cloud.charts4a.chart.attrib.YAxisAttribs;
import com.cloud.charts4a.chart.attrib.YRangeHighLightAttribs;
import com.cloud.charts4a.data.AbstractChartData;
import com.cloud.charts4a.data.ChartDataSeries;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
/**
* Abstract Chart Render<p/>
*
* @param <T1> subclass of AbstractXAxisAttribs class
* @param <T2> subclass of AbstractChartData class
*
* @version
* <ol>
* <li>2010/07/09 CloudTuFirst Release</li>
* </ol>
*
* @author cloudtu
*/
abstract class AbstractChartRender<T1 extends AbstractXAxisAttribs<?, ?>,T2 extends AbstractChartData<T2>> {
/**
*
*/
protected static final DecimalFormat tipDecimalFormat = new DecimalFormat("#.##");
/**
* Canvas
*/
protected Canvas canvas;
/**
* Canvas width
*/
protected float canvasWidth;
/**
* Canvas Height
*/
protected float canvasHeight;
/**
* X
*/
protected String[] xAxisLabels;
/**
* Y
*/
protected int[] yAxisLabels;
/**
*
*/
protected String[] legends;
/**
* legends's color
*/
protected int[] legendColors;
/**
* X
*/
protected boolean isShowXUnitDesc;
/**
* Y
*/
protected boolean isShowYUnitDesc;
/**
*
*/
protected boolean isShowLegend;
/**
*
*/
protected boolean isOnlyShowDot;
/**
* X-Axis attributes in chart
*/
protected T1 xAxisAttribs;
/**
* Y-Axis attributes in chart
*/
protected YAxisAttribs yAxisAttribs;
/**
* Y-Axis range highlight attributes
*/
protected YRangeHighLightAttribs yRangeHighLightAttribs;
/**
* ChartDataSeries list
*/
protected List<ChartDataSeries<T2>> chartDataSeriesList = new ArrayList<ChartDataSeries<T2>>();
/**
* XYpaint object
*/
protected Paint xyAxisPaint;
/**
* XYpaint object
*/
protected Paint xyAxisBackgroundGridPaint;
/**
* XY paint object
*/
protected Paint xyAxisLabelPaint;
/**
* XYPaint object
*/
protected Paint xyAxisUnitDescPaint;
/**
* X-Axis interval pixels of two xAxisLabel unit
*/
protected float xAxisIntervalForTwoXAxisLabelUnit;
/**
* Y-Axis interval pixels of two yAxisLabel unit
*/
protected float yAxisIntervalForTwoYAxisLabelUnit;
/**
* XPixel
*/
protected float xAxisUnitDescHeight;
/**
* XPixel
*/
protected float xAxisUnitDescHeightWithTopAndBottomPad;
/**
* YPixel
*/
protected float yAxisUnitDescHeightWithTopAndBottomPad;
/**
* xyAxisLabel height pixels
*/
protected float xyAxisLabelHeight;
/**
* xAxisLabelPixel
*/
protected float xAxisLabelHeightWithTopAndBottomPad;
/**
* yAxisLabelPixel
*/
protected float yAxisLabelWidthWithLeftAndRightPad;
/**
* one legend height pixels with top and bottom padding
*/
protected float oneLegendUnitHeightWithTopAndBottomPad;
/**
* total legends height pixels with top and bottom padding
*/
protected float totalLegendsHeightWithTopAndBottomPad;
protected ChartRenderBo chartRenderBo;
/**
* Draw chart
*/
public void drawChart(){
drawXYAxisBackgroundGrid();
drawContents();
drawBlankRectAboveMaxYAxisValueAndBelowMinYAxisValue();
drawXAxisLabels();
drawYAxisLabels();
drawXAxisUnitDesc();
drawYAxisUnitDesc();
drawLegends();
}
/**
* initial xAxisLabels variable
*/
abstract protected void initXAxisLabelsValue();
/**
* initial yAxisLabels variable
*/
protected void initYAxisLabelsValue(){
//(yAxisAttribs.getMinYValue() != null && yAxisAttribs.getMaxYValue() != null)yAxisLabels
//chartDataSeriesListyAxisLabels
if(yAxisAttribs.getMinYValue() != null && yAxisAttribs.getMaxYValue() != null){
yAxisLabels = new int[(yAxisAttribs.getMaxYValue() - yAxisAttribs.getMinYValue())/yAxisAttribs.getYValueInterval() + 1];
for(int i=0;i<yAxisLabels.length;i++){
yAxisLabels[i] = yAxisAttribs.getMinYValue() + yAxisAttribs.getYValueInterval() * i;
}
}
else{
//(chartDataSeriesList.size() == 0 && yRangeHighLightAttribs == null)yAxisLabels
if(chartDataSeriesList.size() == 0 && yRangeHighLightAttribs == null){
yAxisLabels = new int[2];
yAxisLabels[1] = yAxisAttribs.getYValueInterval();
return;
}
int minYValue = Integer.MAX_VALUE;
int maxYValue = Integer.MIN_VALUE;
if(chartDataSeriesList.size() > 0){
for (ChartDataSeries<T2> chartDataSeries : chartDataSeriesList) {
if(chartDataSeries.getMinYValueInAllData() < minYValue){
minYValue = (int)chartDataSeries.getMinYValueInAllData();
}
if(chartDataSeries.getMaxYValueInAllData() > maxYValue){
maxYValue = (int)Math.ceil(chartDataSeries.getMaxYValueInAllData());
}
}
}
if(yRangeHighLightAttribs != null && yRangeHighLightAttribs.getMinYValue() < minYValue){
minYValue = yRangeHighLightAttribs.getMinYValue();
}
if(yRangeHighLightAttribs != null && yRangeHighLightAttribs.getMaxYValue() > maxYValue){
maxYValue = yRangeHighLightAttribs.getMaxYValue();
}
if(yAxisAttribs.getYValueInterval() >= 5){
//minYValue5
minYValue = (int)Math.floor((double)minYValue / 5) * 5;
}
//minYValuemaxYValueyIntervalmaxYValue
maxYValue = minYValue + (int)Math.abs(Math.ceil(((double)maxYValue - minYValue) / yAxisAttribs.getYValueInterval())) * yAxisAttribs.getYValueInterval();
yAxisLabels = new int[(maxYValue - minYValue)/yAxisAttribs.getYValueInterval() + 1];
for(int i=0;i<yAxisLabels.length;i++){
yAxisLabels[i] = minYValue + yAxisAttribs.getYValueInterval() * i;
}
}
}
/**
* initial legends and legendColors variable
*/
protected void initLegendsValue(){
if(isShowLegend == true && chartDataSeriesList.size() > 0){
legends = new String[chartDataSeriesList.size()];
legendColors = new int[chartDataSeriesList.size()];
for(int i=0;i<chartDataSeriesList.size();i++){
legends[i] = " " + chartDataSeriesList.get(i).getName();
legendColors[i] = chartDataSeriesList.get(i).getColor();
}
}
}
/**
* Draw yAxisLabels
*/
private void drawYAxisLabels(){
float xPosition = yAxisUnitDescHeightWithTopAndBottomPad + yAxisLabelWidthWithLeftAndRightPad / 2;
float yPosition = xAxisLabelHeightWithTopAndBottomPad + xyAxisLabelHeight / 2;
int counter = 0;
for(int i= yAxisLabels.length -1;i>=0;i--){
canvas.drawText("" + yAxisLabels[i], xPosition, yPosition + counter * yAxisIntervalForTwoYAxisLabelUnit, xyAxisLabelPaint);
counter++;
}
}
/**
* Draw xAxisLabels
*/
private void drawXAxisLabels(){
float xPosition = yAxisUnitDescHeightWithTopAndBottomPad + yAxisLabelWidthWithLeftAndRightPad + (xAxisIntervalForTwoXAxisLabelUnit / 2);
float yPosition = xAxisLabelHeightWithTopAndBottomPad + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1) + chartRenderBo.textPadding + xyAxisLabelHeight;
for(int i= 0;i<xAxisLabels.length;i++){
canvas.drawText(xAxisLabels[i], xPosition + i * xAxisIntervalForTwoXAxisLabelUnit, yPosition, xyAxisLabelPaint);
}
}
/**
* Draw xAxisUnitDesc
*/
private void drawXAxisUnitDesc(){
if(isShowXUnitDesc == true){
float xPosition = canvasWidth / 2;
float yPosition = xAxisLabelHeightWithTopAndBottomPad * 2 + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1) + chartRenderBo.textPadding + xAxisUnitDescHeight;
canvas.drawText(xAxisAttribs.getXUnitDesc(), xPosition, yPosition, xyAxisUnitDescPaint);
}
}
/**
* Draw yAxisUnitDesc
*/
private void drawYAxisUnitDesc(){
if(isShowYUnitDesc == true){
canvas.save();
float xPosition = (canvasHeight - totalLegendsHeightWithTopAndBottomPad) / 2;
float yPosition = - chartRenderBo.textPadding;
canvas.rotate(90);
canvas.drawText(yAxisAttribs.getYUnitDesc(), xPosition, yPosition, xyAxisUnitDescPaint);
canvas.restore();
}
}
/**
* Draw legends
*/
private void drawLegends(){
if(isShowLegend == true && chartDataSeriesList.size() > 0){
float checkedWidth = 0;
float xPosition = 0;
float yPosition = xAxisLabelHeightWithTopAndBottomPad * 2 + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1) +
xAxisUnitDescHeightWithTopAndBottomPad + oneLegendUnitHeightWithTopAndBottomPad - chartRenderBo.textPadding;
for(int i=0;i<legends.length;i++){
float oneLegendUnitWidthWithLeftAndRightPad = chartRenderBo.calSpecifiedLegendWidthWithLeftAndRightPad(legends[i]);
checkedWidth = checkedWidth + oneLegendUnitWidthWithLeftAndRightPad;
if(canvasWidth >= checkedWidth){
xPosition = xPosition + oneLegendUnitWidthWithLeftAndRightPad / 2;
}
else{
xPosition = oneLegendUnitWidthWithLeftAndRightPad / 2;
yPosition = yPosition + oneLegendUnitHeightWithTopAndBottomPad;
checkedWidth = oneLegendUnitWidthWithLeftAndRightPad;
}
Paint legendPaint = chartRenderBo.createLegendPaint(legendColors[i]);
canvas.drawText(legends[i], xPosition, yPosition, legendPaint);
xPosition = xPosition + oneLegendUnitWidthWithLeftAndRightPad / 2;
}
}
}
/**
* Draw XYAxis background grid
*/
private void drawXYAxisBackgroundGrid(){
float xPosition = yAxisUnitDescHeightWithTopAndBottomPad + yAxisLabelWidthWithLeftAndRightPad;
float yPosition = xAxisLabelHeightWithTopAndBottomPad;
//X
canvas.drawLine(xPosition, yPosition + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit,
xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * (xAxisLabels.length - 1),
yPosition + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit , xyAxisPaint);
//Y
canvas.drawLine(xPosition, yPosition , xPosition, yPosition + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1), xyAxisPaint);
//X
for(int i=0;i<yAxisLabels.length-1;i++){
canvas.drawLine(xPosition, yPosition + i * yAxisIntervalForTwoYAxisLabelUnit,
xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * (xAxisLabels.length - 1),
yPosition + i * yAxisIntervalForTwoYAxisLabelUnit, xyAxisBackgroundGridPaint);
}
//Y
for(int j=0;j<xAxisLabels.length;j++){
canvas.drawLine(xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * j, yPosition ,
xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * j,
yPosition + yAxisIntervalForTwoYAxisLabelUnit * (yAxisLabels.length - 1), xyAxisBackgroundGridPaint);
}
}
/**
* YY
*/
private void drawBlankRectAboveMaxYAxisValueAndBelowMinYAxisValue(){
Paint blackPaint = new Paint();
blackPaint.setColor(Color.BLACK);
RectF aboveRect = new RectF(0,0,canvasWidth,xAxisLabelHeightWithTopAndBottomPad);
canvas.drawRect(aboveRect, blackPaint); //Y
RectF belowRect = new RectF(0,xAxisLabelHeightWithTopAndBottomPad + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit,canvasWidth,canvasHeight);
canvas.drawRect(belowRect, blackPaint); //Y
float xPosition = yAxisUnitDescHeightWithTopAndBottomPad + yAxisLabelWidthWithLeftAndRightPad;
float yPosition = xAxisLabelHeightWithTopAndBottomPad;
//X
canvas.drawLine(xPosition, yPosition,
xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * (xAxisLabels.length - 1),
yPosition, xyAxisBackgroundGridPaint);
//X
canvas.drawLine(xPosition, yPosition + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit,
xPosition + (xAxisIntervalForTwoXAxisLabelUnit / 2) + xAxisIntervalForTwoXAxisLabelUnit * (xAxisLabels.length - 1),
yPosition + (yAxisLabels.length - 1) * yAxisIntervalForTwoYAxisLabelUnit , xyAxisPaint);
}
/**
* Draw all contents in XY-Axis
*/
abstract protected void drawContents();
/**
*
*
* @param chartData ChartData
*/
public void drawTipOnChartWhenDotIsClicked(T2 chartData){
//YY
if(chartData.getYValue() < yAxisLabels[0] || chartData.getYValue() > yAxisLabels[yAxisLabels.length - 1]){
return;
}
Rect tipRect = chartData.getTipRect();
Path tipBackGroundPath = new Path();
Paint tipBackGroundPaint = chartRenderBo.createTipBackgroundRectPaint();
Paint tipTextPaint = chartRenderBo.createTipTextPaint();
if(tipRect.top > chartData.getYPosition()){
tipBackGroundPath.moveTo(chartData.getXPosition(), chartData.getYPosition() + 2 * chartRenderBo.dataSeriesDotRadias);
tipBackGroundPath.lineTo(chartData.getXPosition() - 2 * chartRenderBo.dataSeriesDotRadias, ((float)tipRect.top + tipRect.bottom)/2);
tipBackGroundPath.lineTo(chartData.getXPosition() + 2 * chartRenderBo.dataSeriesDotRadias, ((float)tipRect.top + tipRect.bottom)/2);
tipBackGroundPath.close();
tipBackGroundPath.addRoundRect(new RectF(tipRect.left,((float)tipRect.top + tipRect.bottom)/2,tipRect.right,tipRect.bottom),
chartRenderBo.tipRectCornerRadius, chartRenderBo.tipRectCornerRadius, Path.Direction.CW);
canvas.drawPath(tipBackGroundPath, tipBackGroundPaint);
canvas.drawText(chartData.getTipText(), ((float)tipRect.left + tipRect.right)/2, tipRect.bottom - chartRenderBo.tipTextPadding, tipTextPaint);
}
else{
tipBackGroundPath.addRoundRect(new RectF(tipRect.left,tipRect.top,tipRect.right,((float)tipRect.top + tipRect.bottom)/2),
chartRenderBo.tipRectCornerRadius, chartRenderBo.tipRectCornerRadius, Path.Direction.CW);
tipBackGroundPath.moveTo(chartData.getXPosition(), chartData.getYPosition() - 2 * chartRenderBo.dataSeriesDotRadias);
tipBackGroundPath.lineTo(chartData.getXPosition() - 2 * chartRenderBo.dataSeriesDotRadias, ((float)tipRect.top + tipRect.bottom)/2);
tipBackGroundPath.lineTo(chartData.getXPosition() + 2 * chartRenderBo.dataSeriesDotRadias, ((float)tipRect.top + tipRect.bottom)/2);
tipBackGroundPath.close();
canvas.drawPath(tipBackGroundPath, tipBackGroundPaint);
canvas.drawText(chartData.getTipText(), ((float)tipRect.left + tipRect.right)/2,
((float)tipRect.top + tipRect.bottom)/2 - chartRenderBo.tipTextPadding, tipTextPaint);
}
}
}
|