package com.github.mikephil.charting.renderer; import android.graphics.Canvas; import android.graphics.Paint.Align; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.buffer.BarBuffer; import com.github.mikephil.charting.buffer.HorizontalBarBuffer; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.interfaces.BarDataProvider; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import java.util.List; /** * Renderer for the HorizontalBarChart. * * @author Philipp Jahoda */ public class HorizontalBarChartRenderer extends BarChartRenderer { private float mYOffset = 0f; public HorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { super(chart, animator, viewPortHandler); mValuePaint.setTextAlign(Align.LEFT); } @Override public void initBuffers() { BarData barData = mChart.getBarData(); mBarBuffers = new HorizontalBarBuffer[barData.getDataSetCount()]; for (int i = 0; i < mBarBuffers.length; i++) { BarDataSet set = barData.getDataSetByIndex(i); mBarBuffers[i] = new HorizontalBarBuffer(set.getValueCount() * 4 * set.getStackSize(), barData.getGroupSpace(), barData.getDataSetCount(), set.isStacked()); } } protected void drawDataSet(Canvas c, BarDataSet dataSet, int index) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); calcXBounds(trans); mShadowPaint.setColor(dataSet.getBarShadowColor()); float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); List<BarEntry> entries = dataSet.getYVals(); // initialize the buffer BarBuffer buffer = mBarBuffers[index]; buffer.setPhases(phaseX, phaseY); buffer.setBarSpace(dataSet.getBarSpace()); buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); buffer.feed(entries); trans.pointValuesToPixel(buffer.buffer); for (int j = 0; j < buffer.size(); j += 4) { if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) break; if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) continue; if (mChart.isDrawBarShadowEnabled()) { c.drawRect(mViewPortHandler.contentLeft(), buffer.buffer[j + 1], mViewPortHandler.contentRight(), buffer.buffer[j + 3], mShadowPaint); } // Set the color for the currently drawn value. If the index // is // out of bounds, reuse colors. mRenderPaint.setColor(dataSet.getColor(j / 4)); c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3], mRenderPaint); } } @Override public void drawValues(Canvas c) { // if values are drawn if (passesCheck()) { List<BarDataSet> dataSets = mChart.getBarData().getDataSets(); final float valueOffsetPlus = Utils.convertDpToPixel(5f); float posOffset = 0f; float negOffset = 0f; boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); if (drawValueAboveBar) mValuePaint.setTextAlign(Align.LEFT); else mValuePaint.setTextAlign(Align.RIGHT); for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { BarDataSet dataSet = dataSets.get(i); if (!dataSet.isDrawValuesEnabled()) continue; boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); mYOffset = Utils.calcTextHeight(mValuePaint, "10") / 2f; ValueFormatter formatter = dataSet.getValueFormatter(); Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); List<BarEntry> entries = dataSet.getYVals(); float[] valuePoints = getTransformedValues(trans, entries, i); // if only single values are drawn (sum) if (!mChart.isDrawValuesForWholeStackEnabled()) { for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { if (!mViewPortHandler.isInBoundsX(valuePoints[j])) continue; if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) break; if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) continue; float val = entries.get(j / 2).getVal(); String valueText = formatter.getFormattedValue(val); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, valueText); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); if (isInverted) { posOffset = -posOffset - valueTextWidth; negOffset = -negOffset - valueTextWidth; } drawValue(c, valueText, valuePoints[j] + (val >= 0 ? posOffset : negOffset), valuePoints[j + 1]); } // if each value of a potential stack should be drawn } else { for (int j = 0; j < (valuePoints.length - 1) * mAnimator.getPhaseX(); j += 2) { BarEntry e = entries.get(j / 2); float[] vals = e.getVals(); // we still draw stacked bars, but there is one // non-stacked // in between if (vals == null) { if (!mViewPortHandler.isInBoundsX(valuePoints[j])) continue; if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) break; if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) continue; float val = e.getVal(); String valueText = formatter.getFormattedValue(val); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, valueText); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); if (isInverted) { posOffset = -posOffset - valueTextWidth; negOffset = -negOffset - valueTextWidth; } drawValue(c, valueText, valuePoints[j] + (e.getVal() >= 0 ? posOffset : negOffset), valuePoints[j + 1]); } else { float[] transformed = new float[vals.length * 2]; int cnt = 0; float add = e.getVal(); for (int k = 0; k < transformed.length; k += 2) { add -= vals[cnt]; transformed[k] = (vals[cnt] + add) * mAnimator.getPhaseY(); cnt++; } trans.pointValuesToPixel(transformed); for (int k = 0; k < transformed.length; k += 2) { float val = vals[k / 2]; String valueText = formatter.getFormattedValue(val); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, valueText); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); if (isInverted) { posOffset = -posOffset - valueTextWidth; negOffset = -negOffset - valueTextWidth; } float x = transformed[k] + (val >= 0 ? posOffset : negOffset); float y = valuePoints[j + 1]; if (!mViewPortHandler.isInBoundsX(x)) continue; if (!mViewPortHandler.isInBoundsTop(y)) break; if (!mViewPortHandler.isInBoundsBottom(y)) continue; drawValue(c, valueText, x, y); } } } } } } } @Override protected void prepareBarHighlight(float x, float y, float barspaceHalf, float from, Transformer trans) { float top = x - 0.5f + barspaceHalf; float bottom = x + 0.5f - barspaceHalf; float left = y >= from ? y : from; float right = y <= from ? y : from; mBarRect.set(left, top, right, bottom); trans.rectValueToPixelHorizontal(mBarRect, mAnimator.getPhaseY()); } @Override public float[] getTransformedValues(Transformer trans, List<BarEntry> entries, int dataSetIndex) { return trans.generateTransformedValuesHorizontalBarChart(entries, dataSetIndex, mChart.getBarData(), mAnimator.getPhaseY()); } @Override protected void drawValue(Canvas c, String value, float xPos, float yPos) { super.drawValue(c, value, xPos, yPos + mYOffset); } @Override protected boolean passesCheck() { return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() * mViewPortHandler.getScaleY(); } }