package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
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.data.DataSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.BarDataProvider;
import com.github.mikephil.charting.renderer.BarChartRenderer;
import com.github.mikephil.charting.renderer.XAxisRendererBarChart;
import com.github.mikephil.charting.utils.Highlight;
/**
* Chart that draws bars.
*
* @author Philipp Jahoda
*/
public class BarChart extends BarLineChartBase<BarData> implements BarDataProvider {
/** flag that enables or disables the highlighting arrow */
private boolean mDrawHighlightArrow = false;
/**
* if set to true, all values are drawn above their bars, instead of below
* their top
*/
private boolean mDrawValueAboveBar = true;
/**
* if set to true, all values of a stack are drawn individually, and not
* just their sum
*/
private boolean mDrawValuesForWholeStack = true;
/**
* if set to true, a grey area is drawn behind each bar that indicates the
* maximum value
*/
private boolean mDrawBarShadow = false;
public BarChart(Context context) {
super(context);
}
public BarChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BarChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler);
mXAxisRenderer = new XAxisRendererBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer,
this);
mXChartMin = -0.5f;
}
@Override
protected void calcMinMax() {
super.calcMinMax();
// increase deltax by 1 because the bars have a width of 1
mDeltaX += 0.5f;
// extend xDelta to make space for multiple datasets (if ther are one)
mDeltaX *= mData.getDataSetCount();
int maxEntry = 0;
for (int i = 0; i < mData.getDataSetCount(); i++) {
DataSet<? extends Entry> set = mData.getDataSetByIndex(i);
if (maxEntry < set.getEntryCount())
maxEntry = set.getEntryCount();
}
float groupSpace = mData.getGroupSpace();
mDeltaX += maxEntry * groupSpace;
mXChartMax = mDeltaX - mXChartMin;
}
/**
* Returns the Highlight object (contains x-index and DataSet index) of the
* selected value at the given touch point inside the BarChart.
*
* @param x
* @param y
* @return
*/
@Override
public Highlight getHighlightByTouchPoint(float x, float y) {
if (mDataNotSet || mData == null) {
Log.e(LOG_TAG, "Can't select by touch. No data set.");
return null;
}
// create an array of the touch-point
float[] pts = new float[2];
pts[0] = x;
pts[1] = y;
mLeftAxisTransformer.pixelsToValue(pts);
if (pts[0] < mXChartMin || pts[0] > mXChartMax)
return null;
return getHighlight(pts[0], pts[1]);
}
/**
* Returns the correct Highlight object (including xIndex and dataSet-index)
* for the specified touch position.
*
* @param xPosition
* @return
*/
protected Highlight getHighlight(double xPosition, double yPosition) {
int setCount = mData.getDataSetCount();
int valCount = mData.getXValCount();
int dataSetIndex = 0;
int xIndex = 0;
// only one dataset exists
if (!mData.isGrouped()) {
xIndex = (int) Math.round(xPosition);
// check bounds
if (xIndex < 0) {
xIndex = 0;
} else if (xIndex >= valCount) {
xIndex = valCount - 1;
}
// if this bardata is grouped into more datasets
} else {
// calculate how often the group-space appears
int steps = (int) ((float) xPosition / ((float) setCount + mData.getGroupSpace()));
float groupSpaceSum = mData.getGroupSpace() * (float) steps;
float baseNoSpace = (float) xPosition - groupSpaceSum;
if (mLogEnabled)
Log.i(LOG_TAG, "base: " + xPosition + ", steps: " + steps + ", groupSpaceSum: "
+ groupSpaceSum
+ ", baseNoSpace: " + baseNoSpace);
dataSetIndex = (int) baseNoSpace % setCount;
xIndex = (int) baseNoSpace / setCount;
if (mLogEnabled)
Log.i(LOG_TAG, "xIndex: " + xIndex + ", dataSet: " + dataSetIndex);
// check bounds
if (xIndex < 0) {
xIndex = 0;
dataSetIndex = 0;
} else if (xIndex >= valCount) {
xIndex = valCount - 1;
dataSetIndex = setCount - 1;
}
// check bounds
if (dataSetIndex < 0)
dataSetIndex = 0;
else if (dataSetIndex >= setCount)
dataSetIndex = setCount - 1;
}
if (!mData.getDataSetByIndex(dataSetIndex).isStacked())
return new Highlight(xIndex, dataSetIndex);
else
return getStackedHighlight(xIndex, dataSetIndex, yPosition);
}
/**
* This method creates the Highlight object that also indicates which value
* of a stacked BarEntry has been selected.
*
* @param xIndex
* @param dataSet
* @param yValue
* @return
*/
protected Highlight getStackedHighlight(int xIndex, int dataSet, double yValue) {
BarEntry entry = mData.getDataSetByIndex(dataSet).getEntryForXIndex(xIndex);
if (entry != null) {
int stackIndex = entry.getClosestIndexAbove((float) yValue);
Highlight h = new Highlight(xIndex, dataSet, stackIndex);
return h;
} else
return null;
}
/**
* Returns the bounding box of the specified Entry in the specified DataSet.
* Returns null if the Entry could not be found in the charts data.
*
* @param e
* @param dataSetIndex
* @return
*/
public RectF getBarBounds(BarEntry e) {
BarDataSet set = mData.getDataSetForEntry(e);
if (set == null)
return null;
float barspace = set.getBarSpace();
float y = e.getVal();
float x = e.getXIndex();
float barWidth = 0.5f;
float spaceHalf = barspace / 2f;
float left = x - barWidth + spaceHalf;
float right = x + barWidth - spaceHalf;
float top = y >= 0 ? y : 0;
float bottom = y <= 0 ? y : 0;
RectF bounds = new RectF(left, top, right, bottom);
getTransformer(set.getAxisDependency()).rectValueToPixel(bounds);
return bounds;
}
/**
* set this to true to draw the highlightning arrow
*
* @param enabled
*/
public void setDrawHighlightArrow(boolean enabled) {
mDrawHighlightArrow = enabled;
}
/**
* returns true if drawing the highlighting arrow is enabled, false if not
*
* @return
*/
public boolean isDrawHighlightArrowEnabled() {
return mDrawHighlightArrow;
}
/**
* If set to true, all values are drawn above their bars, instead of below
* their top.
*
* @param enabled
*/
public void setDrawValueAboveBar(boolean enabled) {
mDrawValueAboveBar = enabled;
}
/**
* returns true if drawing values above bars is enabled, false if not
*
* @return
*/
public boolean isDrawValueAboveBarEnabled() {
return mDrawValueAboveBar;
}
/**
* if set to true, all values of a stack are drawn individually, and not
* just their sum
*
* @param enabled
*/
public void setDrawValuesForWholeStack(boolean enabled) {
mDrawValuesForWholeStack = enabled;
}
/**
* returns true if all values of a stack are drawn, and not just their sum
*
* @return
*/
public boolean isDrawValuesForWholeStackEnabled() {
return mDrawValuesForWholeStack;
}
/**
* If set to true, a grey area is drawn behind each bar that indicates the
* maximum value. Enabling his will reduce performance by about 50%.
*
* @param enabled
*/
public void setDrawBarShadow(boolean enabled) {
mDrawBarShadow = enabled;
}
/**
* returns true if drawing shadows (maxvalue) for each bar is enabled, false
* if not
*
* @return
*/
public boolean isDrawBarShadowEnabled() {
return mDrawBarShadow;
}
@Override
public BarData getBarData() {
return mData;
}
}