package com.github.mikephil.charting.renderer; import android.graphics.Canvas; import android.graphics.Paint.Align; import android.graphics.RectF; 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.BarEntry; import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; import java.util.List; /** * Renderer for the HorizontalBarChart. * * @author Philipp Jahoda */ public class HorizontalBarChartRenderer extends BarChartRenderer { 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++) { IBarDataSet set = barData.getDataSetByIndex(i); mBarBuffers[i] = new HorizontalBarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), barData.getDataSetCount(), set.isStacked()); } } private RectF mBarShadowRectBuffer = new RectF(); @Override protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); mBarBorderPaint.setColor(dataSet.getBarBorderColor()); mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); // draw the bar shadow before the values if (mChart.isDrawBarShadowEnabled()) { mShadowPaint.setColor(dataSet.getBarShadowColor()); BarData barData = mChart.getBarData(); final float barWidth = barData.getBarWidth(); final float barWidthHalf = barWidth / 2.0f; float x; for (int i = 0, count = Math.min((int)(Math.ceil((float)(dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount()); i < count; i++) { BarEntry e = dataSet.getEntryForIndex(i); x = e.getX(); mBarShadowRectBuffer.top = x - barWidthHalf; mBarShadowRectBuffer.bottom = x + barWidthHalf; trans.rectValueToPixel(mBarShadowRectBuffer); if (!mViewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) continue; if (!mViewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) break; mBarShadowRectBuffer.left = mViewPortHandler.contentLeft(); mBarShadowRectBuffer.right = mViewPortHandler.contentRight(); c.drawRect(mBarShadowRectBuffer, mShadowPaint); } } // initialize the buffer BarBuffer buffer = mBarBuffers[index]; buffer.setPhases(phaseX, phaseY); buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); buffer.setBarWidth(mChart.getBarData().getBarWidth()); buffer.feed(dataSet); trans.pointValuesToPixel(buffer.buffer); final boolean isSingleColor = dataSet.getColors().size() == 1; if (isSingleColor) { mRenderPaint.setColor(dataSet.getColor()); } 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 (!isSingleColor) { // 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); if (drawBorder) { c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3], mBarBorderPaint); } } } @Override public void drawValues(Canvas c) { // if values are drawn if (isDrawingValuesAllowed(mChart)) { List<IBarDataSet> dataSets = mChart.getBarData().getDataSets(); final float valueOffsetPlus = Utils.convertDpToPixel(5f); float posOffset = 0f; float negOffset = 0f; final boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { IBarDataSet dataSet = dataSets.get(i); if (!shouldDrawValues(dataSet)) continue; boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); final float halfTextHeight = Utils.calcTextHeight(mValuePaint, "10") / 2f; IValueFormatter formatter = dataSet.getValueFormatter(); // get the buffer BarBuffer buffer = mBarBuffers[i]; final float phaseY = mAnimator.getPhaseY(); // if only single values are drawn (sum) if (!dataSet.isStacked()) { for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { float y = (buffer.buffer[j + 1] + buffer.buffer[j + 3]) / 2f; if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1])) break; if (!mViewPortHandler.isInBoundsX(buffer.buffer[j])) continue; if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) continue; BarEntry e = dataSet.getEntryForIndex(j / 4); float val = e.getY(); String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); if (isInverted) { posOffset = -posOffset - valueTextWidth; negOffset = -negOffset - valueTextWidth; } drawValue(c, formattedValue, buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset), y + halfTextHeight, dataSet.getValueTextColor(j / 2)); } // if each value of a potential stack should be drawn } else { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); int bufferIndex = 0; int index = 0; while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) { BarEntry e = dataSet.getEntryForIndex(index); int color = dataSet.getValueTextColor(index); float[] vals = e.getYVals(); // we still draw stacked bars, but there is one // non-stacked // in between if (vals == null) { if (!mViewPortHandler.isInBoundsTop(buffer.buffer[bufferIndex + 1])) break; if (!mViewPortHandler.isInBoundsX(buffer.buffer[bufferIndex])) continue; if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) continue; float val = e.getY(); String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); if (isInverted) { posOffset = -posOffset - valueTextWidth; negOffset = -negOffset - valueTextWidth; } drawValue(c, formattedValue, buffer.buffer[bufferIndex + 2] + (e.getY() >= 0 ? posOffset : negOffset), buffer.buffer[bufferIndex + 1] + halfTextHeight, color); } else { float[] transformed = new float[vals.length * 2]; float posY = 0f; float negY = -e.getNegativeSum(); for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) { float value = vals[idx]; float y; if (value >= 0f) { posY += value; y = posY; } else { y = negY; negY -= value; } transformed[k] = y * phaseY; } trans.pointValuesToPixel(transformed); for (int k = 0; k < transformed.length; k += 2) { float val = vals[k / 2]; String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); 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 = (buffer.buffer[bufferIndex + 1] + buffer.buffer[bufferIndex + 3]) / 2f; if (!mViewPortHandler.isInBoundsTop(y)) break; if (!mViewPortHandler.isInBoundsX(x)) continue; if (!mViewPortHandler.isInBoundsBottom(y)) continue; drawValue(c, formattedValue, x, y + halfTextHeight, color); } } bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length; index++; } } } } } protected void drawValue(Canvas c, String valueText, float x, float y, int color) { mValuePaint.setColor(color); c.drawText(valueText, x, y, mValuePaint); } @Override protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) { float top = x - barWidthHalf; float bottom = x + barWidthHalf; float left = y1; float right = y2; mBarRect.set(left, top, right, bottom); trans.rectToPixelPhaseHorizontal(mBarRect, mAnimator.getPhaseY()); } @Override protected void setHighlightDrawPos(Highlight high, RectF bar) { high.setDraw(bar.centerY(), bar.right); } @Override protected boolean isDrawingValuesAllowed(ChartInterface chart) { return chart.getData().getEntryCount() < chart.getMaxVisibleCount() * mViewPortHandler.getScaleY(); } }