Android Open Source - customhellochartdemo Column Chart Renderer






From Project

Back to project page customhellochartdemo.

License

The source code is released under:

Apache License

If you think the Android project customhellochartdemo listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package lecho.lib.hellocharts.renderer;
//  ww  w . ja v a  2 s . c o m
import lecho.lib.hellocharts.ChartComputator;
import lecho.lib.hellocharts.model.Column;
import lecho.lib.hellocharts.model.ColumnChartData;
import lecho.lib.hellocharts.model.ColumnValue;
import lecho.lib.hellocharts.provider.ColumnChartDataProvider;
import lecho.lib.hellocharts.util.Utils;
import lecho.lib.hellocharts.view.Chart;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.PointF;
import android.graphics.RectF;

/**
 * Magic renderer for ColumnChart.
 * 
 */
public class ColumnChartRenderer extends AbstractChartRenderer {
  public static final int DEFAULT_SUBCOLUMN_SPACING_DP = 1;
  public static final int DEFAULT_COLUMN_TOUCH_ADDITIONAL_WIDTH_DP = 4;

  private static final int MODE_DRAW = 0;
  private static final int MODE_CHECK_TOUCH = 1;
  private static final int MODE_HIGHLIGHT = 2;

  private ColumnChartDataProvider dataProvider;

  /** Additional width for hightlighted column, used to give tauch feedback. */
  private int touchAdditionalWidth;

  /** Spacing between sub-columns. */
  private int subcolumnSpacing;

  /** Paint used to draw every column. */
  private Paint columnPaint = new Paint();

  /** Holds coordinates for currently processed column/sub-column. */
  private RectF drawRect = new RectF();

  /** Coordinated of user tauch. */
  private PointF touchedPoint = new PointF();

  /** Used to pass tauched value to tauch listener. */
  private float[] valuesBuff = new float[1];

  private float fillRatio;

  private float baseValue;

  public ColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider dataProvider) {
    super(context, chart);
    this.dataProvider = dataProvider;
    subcolumnSpacing = Utils.dp2px(density, DEFAULT_SUBCOLUMN_SPACING_DP);
    touchAdditionalWidth = Utils.dp2px(density, DEFAULT_COLUMN_TOUCH_ADDITIONAL_WIDTH_DP);

    columnPaint.setAntiAlias(true);
    columnPaint.setStyle(Paint.Style.FILL);
    columnPaint.setStrokeCap(Cap.SQUARE);
  }

  @Override
  public void initMaxViewport() {
    if (isViewportCalculationEnabled) {
      calculateMaxViewport();
      chart.getChartComputator().setMaxViewport(tempMaxViewport);
    }
  }

  @Override
  public void initDataMeasuremetns() {
    chart.getChartComputator().setInternalMargin(labelMargin);// Using label margin because I'm lazy:P
  }

  @Override
  public void initDataAttributes() {
    super.initDataAttributes();

    ColumnChartData data = dataProvider.getColumnChartData();
    fillRatio = data.getFillRatio();
    baseValue = data.getBaseValue();

  }

  public void draw(Canvas canvas) {
    final ColumnChartData data = dataProvider.getColumnChartData();
    if (data.isStacked()) {
      drawColumnForStacked(canvas);
      if (isTouched()) {
        highlightColumnForStacked(canvas);
      }
    } else {
      drawColumnsForSubcolumns(canvas);
      if (isTouched()) {
        highlightColumnsForSubcolumns(canvas);
      }
    }
  }

  @Override
  public void drawUnclipped(Canvas canvas) {
    // Do nothing, for this kind of chart there is nothing to draw beyond clipped area
  }

  public boolean checkTouch(float touchX, float touchY) {
    selectedValue.clear();
    final ColumnChartData data = dataProvider.getColumnChartData();
    if (data.isStacked()) {
      checkTouchForStacked(touchX, touchY);
    } else {
      checkTouchForSubcolumns(touchX, touchY);
    }
    return isTouched();
  }

  private void calculateMaxViewport() {
    final ColumnChartData data = dataProvider.getColumnChartData();
    // Column chart always has X values from 0 to numColumns-1, to add some margin on the left and right I added
    // extra 0.5 to the each side, that margins will be negative scaled according to number of columns, so for more
    // columns there will be less margin.
    tempMaxViewport.set(-0.5f, baseValue, data.getColumns().size() - 0.5f, baseValue);
    if (data.isStacked()) {
      calculateMaxViewportForStacked(data);
    } else {
      calculateMaxViewportForSubcolumns(data);
    }
  }

  private void calculateMaxViewportForSubcolumns(ColumnChartData data) {
    for (Column column : data.getColumns()) {
      for (ColumnValue columnValue : column.getValues()) {
        if (columnValue.getValue() >= baseValue && columnValue.getValue() > tempMaxViewport.top) {
          tempMaxViewport.top = columnValue.getValue();
        }
        if (columnValue.getValue() < baseValue && columnValue.getValue() < tempMaxViewport.bottom) {
          tempMaxViewport.bottom = columnValue.getValue();
        }
      }
    }
  }

  private void calculateMaxViewportForStacked(ColumnChartData data) {
    for (Column column : data.getColumns()) {
      float sumPositive = baseValue;
      float sumNegative = baseValue;
      for (ColumnValue columnValue : column.getValues()) {
        if (columnValue.getValue() >= baseValue) {
          sumPositive += columnValue.getValue();
        } else {
          sumNegative += columnValue.getValue();
        }
      }
      if (sumPositive > tempMaxViewport.top) {
        tempMaxViewport.top = sumPositive;
      }
      if (sumNegative < tempMaxViewport.bottom) {
        tempMaxViewport.bottom = sumNegative;
      }
    }
  }

  private void drawColumnsForSubcolumns(Canvas canvas) {
    final ColumnChartData data = dataProvider.getColumnChartData();
    final float columnWidth = calculateColumnWidth();
    int columnIndex = 0;
    for (Column column : data.getColumns()) {
      processColumnForSubcolumns(canvas, column, columnWidth, columnIndex, MODE_DRAW);
      ++columnIndex;
    }
  }

  private void highlightColumnsForSubcolumns(Canvas canvas) {
    final ColumnChartData data = dataProvider.getColumnChartData();
    final float columnWidth = calculateColumnWidth();
    Column column = data.getColumns().get(selectedValue.getFirstIndex());
    processColumnForSubcolumns(canvas, column, columnWidth, selectedValue.getFirstIndex(), MODE_HIGHLIGHT);
  }

  private void checkTouchForSubcolumns(float touchX, float touchY) {
    // Using member variable to hold touch point to avoid too much parameters in methods.
    touchedPoint.x = touchX;
    touchedPoint.y = touchY;
    final ColumnChartData data = dataProvider.getColumnChartData();
    final float columnWidth = calculateColumnWidth();
    int columnIndex = 0;
    for (Column column : data.getColumns()) {
      // canvas is not needed for checking touch
      processColumnForSubcolumns(null, column, columnWidth, columnIndex, MODE_CHECK_TOUCH);
      ++columnIndex;
    }
  }

  private void processColumnForSubcolumns(Canvas canvas, Column column, float columnWidth, int columnIndex, int mode) {
    final ChartComputator computator = chart.getChartComputator();

    // For n subcolumns there will be n-1 spacing and there will be one
    // subcolumn for every columnValue
    float subcolumnWidth = (columnWidth - (subcolumnSpacing * (column.getValues().size() - 1)))
        / column.getValues().size();
    if (subcolumnWidth < 1) {
      subcolumnWidth = 1;
    }
    // Columns are indexes from 0 to n, column index is also column X value
    final float rawX = computator.computeRawX(columnIndex);
    final float halfColumnWidth = columnWidth / 2;
    final float baseRawY = computator.computeRawY(baseValue);
    // First subcolumn will starts at the left edge of current column,
    // rawValueX is horizontal center of that column
    float subcolumnRawX = rawX - halfColumnWidth;
    int valueIndex = 0;
    for (ColumnValue columnValue : column.getValues()) {
      columnPaint.setColor(columnValue.getColor());
      if (subcolumnRawX > rawX + halfColumnWidth) {
        break;
      }
      final float rawY = computator.computeRawY(columnValue.getValue());
      calculateRectToDraw(columnValue, subcolumnRawX, subcolumnRawX + subcolumnWidth, baseRawY, rawY);
      switch (mode) {
      case MODE_DRAW:
        drawSubcolumn(canvas, column, columnValue, false);
        break;
      case MODE_HIGHLIGHT:
        highlightSubcolumn(canvas, column, columnValue, valueIndex, false);
        break;
      case MODE_CHECK_TOUCH:
        checkRectToDraw(columnIndex, valueIndex);
        break;
      default:
        // There no else, every case should be handled or exception will
        // be thrown
        throw new IllegalStateException("Cannot process column in mode: " + mode);
      }
      subcolumnRawX += subcolumnWidth + subcolumnSpacing;
      ++valueIndex;
    }
  }

  private void drawColumnForStacked(Canvas canvas) {
    final ColumnChartData data = dataProvider.getColumnChartData();
    final float columnWidth = calculateColumnWidth();
    // Columns are indexes from 0 to n, column index is also column X value
    int columnIndex = 0;
    for (Column column : data.getColumns()) {
      processColumnForStacked(canvas, column, columnWidth, columnIndex, MODE_DRAW);
      ++columnIndex;
    }
  }

  private void highlightColumnForStacked(Canvas canvas) {
    final ColumnChartData data = dataProvider.getColumnChartData();
    final float columnWidth = calculateColumnWidth();
    // Columns are indexes from 0 to n, column index is also column X value
    Column column = data.getColumns().get(selectedValue.getFirstIndex());
    processColumnForStacked(canvas, column, columnWidth, selectedValue.getFirstIndex(), MODE_HIGHLIGHT);
  }

  private void checkTouchForStacked(float touchX, float touchY) {
    touchedPoint.x = touchX;
    touchedPoint.y = touchY;
    final ColumnChartData data = dataProvider.getColumnChartData();
    final float columnWidth = calculateColumnWidth();
    int columnIndex = 0;
    for (Column column : data.getColumns()) {
      // canvas is not needed for checking touch
      processColumnForStacked(null, column, columnWidth, columnIndex, MODE_CHECK_TOUCH);
      ++columnIndex;
    }
  }

  private void processColumnForStacked(Canvas canvas, Column column, float columnWidth, int columnIndex, int mode) {
    final ChartComputator computator = chart.getChartComputator();
    final float rawX = computator.computeRawX(columnIndex);
    final float halfColumnWidth = columnWidth / 2;
    float mostPositiveValue = baseValue;
    float mostNegativeValue = baseValue;
    float subcolumnBaseValue = baseValue;
    int valueIndex = 0;
    for (ColumnValue columnValue : column.getValues()) {
      columnPaint.setColor(columnValue.getColor());
      if (columnValue.getValue() >= baseValue) {
        // Using values instead of raw pixels make code easier to
        // understand(for me)
        subcolumnBaseValue = mostPositiveValue;
        mostPositiveValue += columnValue.getValue();
      } else {
        subcolumnBaseValue = mostNegativeValue;
        mostNegativeValue += columnValue.getValue();
      }
      final float rawBaseY = computator.computeRawY(subcolumnBaseValue);
      final float rawY = computator.computeRawY(subcolumnBaseValue + columnValue.getValue());
      calculateRectToDraw(columnValue, rawX - halfColumnWidth, rawX + halfColumnWidth, rawBaseY, rawY);
      switch (mode) {
      case MODE_DRAW:
        drawSubcolumn(canvas, column, columnValue, true);
        break;
      case MODE_HIGHLIGHT:
        highlightSubcolumn(canvas, column, columnValue, valueIndex, true);
        break;
      case MODE_CHECK_TOUCH:
        checkRectToDraw(columnIndex, valueIndex);
        break;
      default:
        // There no else, every case should be handled or exception will
        // be thrown
        throw new IllegalStateException("Cannot process column in mode: " + mode);
      }
      ++valueIndex;
    }
  }

  private void drawSubcolumn(Canvas canvas, Column column, ColumnValue columnValue, boolean isStacked) {
    canvas.drawRect(drawRect, columnPaint);
    if (column.hasLabels()) {
      drawLabel(canvas, column, columnValue, isStacked, labelOffset);
    }
  }

  private void highlightSubcolumn(Canvas canvas, Column column, ColumnValue columnValue, int valueIndex,
      boolean isStacked) {
    if (selectedValue.getSecondIndex() == valueIndex) {
      columnPaint.setColor(columnValue.getDarkenColor());
      canvas.drawRect(drawRect.left - touchAdditionalWidth, drawRect.top, drawRect.right + touchAdditionalWidth,
          drawRect.bottom, columnPaint);
      if (column.hasLabels() || column.hasLabelsOnlyForSelected()) {
        drawLabel(canvas, column, columnValue, isStacked, labelOffset);
      }
    }
  }

  private void checkRectToDraw(int columnIndex, int valueIndex) {
    if (drawRect.contains(touchedPoint.x, touchedPoint.y)) {
      selectedValue.set(columnIndex, valueIndex, 0);
    }
  }

  private float calculateColumnWidth() {
    final ChartComputator computator = chart.getChartComputator();
    // columnWidht should be at least 2 px
    float columnWidth = fillRatio * computator.getContentRect().width() / computator.getVisibleViewport().width();
    if (columnWidth < 2) {
      columnWidth = 2;
    }
    return columnWidth;
  }

  private void calculateRectToDraw(ColumnValue columnValue, float left, float right, float rawBaseY, float rawY) {
    // Calculate rect that will be drawn as column, subcolumn or label background.
    drawRect.left = left;
    drawRect.right = right;
    if (columnValue.getValue() >= baseValue) {
      drawRect.top = rawY;
      drawRect.bottom = rawBaseY - subcolumnSpacing;
    } else {
      drawRect.bottom = rawY;
      drawRect.top = rawBaseY + subcolumnSpacing;
    }
  }

  private void drawLabel(Canvas canvas, Column column, ColumnValue columnValue, boolean isStacked, float offset) {
    final ChartComputator computator = chart.getChartComputator();
    valuesBuff[0] = columnValue.getValue();
    final int numChars = column.getFormatter().formatValue(labelBuffer, valuesBuff, columnValue.getLabel());

    if (numChars == 0) {
      // No need to draw empty label
      return;
    }

    final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars);
    final int labelHeight = Math.abs(fontMetrics.ascent);
    float left = drawRect.centerX() - labelWidth / 2 - labelMargin;
    float right = drawRect.centerX() + labelWidth / 2 + labelMargin;
    float top;
    float bottom;
    if (isStacked && labelHeight < drawRect.height() - (2 * labelMargin)) {
      // For stacked columns draw label only if label height is less than subcolumn height - (2 * labelMargin).
      if (columnValue.getValue() >= baseValue) {
        top = drawRect.top;
        bottom = drawRect.top + labelHeight + labelMargin * 2;
      } else {
        top = drawRect.bottom - labelHeight - labelMargin * 2;
        bottom = drawRect.bottom;
      }
    } else if (!isStacked) {
      // For not stacked draw label at the top for positive and at the bottom for negative values
      if (columnValue.getValue() >= baseValue) {
        top = drawRect.top - offset - labelHeight - labelMargin * 2;
        if (top < computator.getContentRect().top) {
          top = drawRect.top + offset;
          bottom = drawRect.top + offset + labelHeight + labelMargin * 2;
        } else {
          bottom = drawRect.top - offset;
        }
      } else {
        bottom = drawRect.bottom + offset + labelHeight + labelMargin * 2;
        if (bottom > computator.getContentRect().bottom) {
          top = drawRect.bottom - offset - labelHeight - labelMargin * 2;
          bottom = drawRect.bottom - offset;
        } else {
          top = drawRect.bottom + offset;
        }
      }
    } else {
      // Draw nothing.
      return;
    }

    labelBackgroundRect.set(left, top, right, bottom);
    drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars,
        columnValue.getDarkenColor());

  }

}




Java Source Code List

lecho.lib.hellocharts.ChartComputator.java
lecho.lib.hellocharts.DummyChartAnimationListener.java
lecho.lib.hellocharts.DummyVieportChangeListener.java
lecho.lib.hellocharts.PreviewChartComputator.java
lecho.lib.hellocharts.ViewportChangeListener.java
lecho.lib.hellocharts.animation.ChartAnimationListener.java
lecho.lib.hellocharts.animation.ChartDataAnimatorV14.java
lecho.lib.hellocharts.animation.ChartDataAnimatorV8.java
lecho.lib.hellocharts.animation.ChartDataAnimator.java
lecho.lib.hellocharts.animation.ChartViewportAnimatorV14.java
lecho.lib.hellocharts.animation.ChartViewportAnimatorV8.java
lecho.lib.hellocharts.animation.ChartViewportAnimator.java
lecho.lib.hellocharts.animation.PieChartRotationAnimatorV14.java
lecho.lib.hellocharts.animation.PieChartRotationAnimatorV8.java
lecho.lib.hellocharts.animation.PieChartRotationAnimator.java
lecho.lib.hellocharts.gesture.ChartScroller.java
lecho.lib.hellocharts.gesture.ChartTouchHandler.java
lecho.lib.hellocharts.gesture.ChartZoomer.java
lecho.lib.hellocharts.gesture.ContainerScrollType.java
lecho.lib.hellocharts.gesture.PieChartTouchHandler.java
lecho.lib.hellocharts.gesture.PreviewChartTouchHandler.java
lecho.lib.hellocharts.gesture.ZoomType.java
lecho.lib.hellocharts.gesture.ZoomerCompat.java
lecho.lib.hellocharts.model.AbstractChartData.java
lecho.lib.hellocharts.model.ArcValue.java
lecho.lib.hellocharts.model.AxisValue.java
lecho.lib.hellocharts.model.Axis.java
lecho.lib.hellocharts.model.BubbleChartData.java
lecho.lib.hellocharts.model.BubbleValue.java
lecho.lib.hellocharts.model.ChartData.java
lecho.lib.hellocharts.model.ColumnChartData.java
lecho.lib.hellocharts.model.ColumnValue.java
lecho.lib.hellocharts.model.Column.java
lecho.lib.hellocharts.model.ComboLineColumnChartData.java
lecho.lib.hellocharts.model.LineChartData.java
lecho.lib.hellocharts.model.Line.java
lecho.lib.hellocharts.model.PieChartData.java
lecho.lib.hellocharts.model.PointValue.java
lecho.lib.hellocharts.model.SelectedValue.java
lecho.lib.hellocharts.model.SimpleValueFormatter.java
lecho.lib.hellocharts.model.ValueFormatter.java
lecho.lib.hellocharts.model.ValueShape.java
lecho.lib.hellocharts.model.Viewport.java
lecho.lib.hellocharts.provider.BubbleChartDataProvider.java
lecho.lib.hellocharts.provider.ColumnChartDataProvider.java
lecho.lib.hellocharts.provider.ComboLineColumnChartDataProvider.java
lecho.lib.hellocharts.provider.LineChartDataProvider.java
lecho.lib.hellocharts.provider.PieChartDataProvider.java
lecho.lib.hellocharts.renderer.AbstractChartRenderer.java
lecho.lib.hellocharts.renderer.AxesRenderer.java
lecho.lib.hellocharts.renderer.BubbleChartRenderer.java
lecho.lib.hellocharts.renderer.ChartRenderer.java
lecho.lib.hellocharts.renderer.ColumnChartRenderer.java
lecho.lib.hellocharts.renderer.ComboLineColumnChartRenderer.java
lecho.lib.hellocharts.renderer.LineChartRenderer.java
lecho.lib.hellocharts.renderer.PieChartRenderer.java
lecho.lib.hellocharts.renderer.PreviewColumnChartRenderer.java
lecho.lib.hellocharts.renderer.PreviewLineChartRenderer.java
lecho.lib.hellocharts.samples.AboutActivity.java
lecho.lib.hellocharts.samples.BrokenLineChartActivity.java
lecho.lib.hellocharts.samples.BrokenLineView.java
lecho.lib.hellocharts.samples.BubbleChartActivity.java
lecho.lib.hellocharts.samples.ColumnChartActivity.java
lecho.lib.hellocharts.samples.ComboLineColumnChartActivity.java
lecho.lib.hellocharts.samples.GoodBadChartActivity.java
lecho.lib.hellocharts.samples.LineChartActivity.java
lecho.lib.hellocharts.samples.LineColumnDependencyActivity.java
lecho.lib.hellocharts.samples.MainActivity.java
lecho.lib.hellocharts.samples.PieChartActivity.java
lecho.lib.hellocharts.samples.PreviewColumnChartActivity.java
lecho.lib.hellocharts.samples.PreviewLineChartActivity.java
lecho.lib.hellocharts.samples.SpeedChartActivity.java
lecho.lib.hellocharts.samples.TempoChartActivity.java
lecho.lib.hellocharts.samples.ViewPagerChartsActivity.java
lecho.lib.hellocharts.util.AxisAutoValues.java
lecho.lib.hellocharts.util.Utils.java
lecho.lib.hellocharts.view.AbstractChartView.java
lecho.lib.hellocharts.view.BubbleChartView.java
lecho.lib.hellocharts.view.Chart.java
lecho.lib.hellocharts.view.ColumnChartView.java
lecho.lib.hellocharts.view.ComboLineColumnChartView.java
lecho.lib.hellocharts.view.LineChartView.java
lecho.lib.hellocharts.view.PieChartView.java
lecho.lib.hellocharts.view.PreviewColumnChartView.java
lecho.lib.hellocharts.view.PreviewLineChartView.java
lecho.lib.hellocharts.view.hack.HackyDrawerLayout.java
lecho.lib.hellocharts.view.hack.HackyViewPager.java