1 package com.github.mikephil.charting.charts; 2 3 import android.content.Context; 4 import android.graphics.RectF; 5 import android.util.AttributeSet; 6 import android.util.Log; 7 8 import com.github.mikephil.charting.components.XAxis.XAxisPosition; 9 import com.github.mikephil.charting.components.YAxis.AxisDependency; 10 import com.github.mikephil.charting.data.BarEntry; 11 import com.github.mikephil.charting.data.Entry; 12 import com.github.mikephil.charting.highlight.Highlight; 13 import com.github.mikephil.charting.highlight.HorizontalBarHighlighter; 14 import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; 15 import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer; 16 import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart; 17 import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart; 18 import com.github.mikephil.charting.utils.HorizontalViewPortHandler; 19 import com.github.mikephil.charting.utils.MPPointF; 20 import com.github.mikephil.charting.utils.TransformerHorizontalBarChart; 21 import com.github.mikephil.charting.utils.Utils; 22 23 /** 24 * BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched, meaning the YAxis class 25 * represents the horizontal values and the XAxis class represents the vertical values. 26 * 27 * @author Philipp Jahoda 28 */ 29 public class HorizontalBarChart extends BarChart { 30 HorizontalBarChart(Context context)31 public HorizontalBarChart(Context context) { 32 super(context); 33 } 34 HorizontalBarChart(Context context, AttributeSet attrs)35 public HorizontalBarChart(Context context, AttributeSet attrs) { 36 super(context, attrs); 37 } 38 HorizontalBarChart(Context context, AttributeSet attrs, int defStyle)39 public HorizontalBarChart(Context context, AttributeSet attrs, int defStyle) { 40 super(context, attrs, defStyle); 41 } 42 43 @Override init()44 protected void init() { 45 46 mViewPortHandler = new HorizontalViewPortHandler(); 47 48 super.init(); 49 50 mLeftAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); 51 mRightAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); 52 53 mRenderer = new HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler); 54 setHighlighter(new HorizontalBarHighlighter(this)); 55 56 mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); 57 mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer); 58 mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this); 59 } 60 61 private RectF mOffsetsBuffer = new RectF(); 62 calculateLegendOffsets(RectF offsets)63 protected void calculateLegendOffsets(RectF offsets) { 64 65 offsets.left = 0.f; 66 offsets.right = 0.f; 67 offsets.top = 0.f; 68 offsets.bottom = 0.f; 69 70 if (mLegend == null || !mLegend.isEnabled() || mLegend.isDrawInsideEnabled()) 71 return; 72 73 switch (mLegend.getOrientation()) { 74 case VERTICAL: 75 76 switch (mLegend.getHorizontalAlignment()) { 77 case LEFT: 78 offsets.left += Math.min(mLegend.mNeededWidth, 79 mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) 80 + mLegend.getXOffset(); 81 break; 82 83 case RIGHT: 84 offsets.right += Math.min(mLegend.mNeededWidth, 85 mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) 86 + mLegend.getXOffset(); 87 break; 88 89 case CENTER: 90 91 switch (mLegend.getVerticalAlignment()) { 92 case TOP: 93 offsets.top += Math.min(mLegend.mNeededHeight, 94 mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) 95 + mLegend.getYOffset(); 96 break; 97 98 case BOTTOM: 99 offsets.bottom += Math.min(mLegend.mNeededHeight, 100 mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) 101 + mLegend.getYOffset(); 102 break; 103 104 default: 105 break; 106 } 107 } 108 109 break; 110 111 case HORIZONTAL: 112 113 switch (mLegend.getVerticalAlignment()) { 114 case TOP: 115 offsets.top += Math.min(mLegend.mNeededHeight, 116 mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) 117 + mLegend.getYOffset(); 118 119 if (mAxisLeft.isEnabled() && mAxisLeft.isDrawLabelsEnabled()) 120 offsets.top += mAxisLeft.getRequiredHeightSpace( 121 mAxisRendererLeft.getPaintAxisLabels()); 122 break; 123 124 case BOTTOM: 125 offsets.bottom += Math.min(mLegend.mNeededHeight, 126 mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) 127 + mLegend.getYOffset(); 128 129 if (mAxisRight.isEnabled() && mAxisRight.isDrawLabelsEnabled()) 130 offsets.bottom += mAxisRight.getRequiredHeightSpace( 131 mAxisRendererRight.getPaintAxisLabels()); 132 break; 133 134 default: 135 break; 136 } 137 break; 138 } 139 } 140 141 @Override calculateOffsets()142 public void calculateOffsets() { 143 144 float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; 145 146 calculateLegendOffsets(mOffsetsBuffer); 147 148 offsetLeft += mOffsetsBuffer.left; 149 offsetTop += mOffsetsBuffer.top; 150 offsetRight += mOffsetsBuffer.right; 151 offsetBottom += mOffsetsBuffer.bottom; 152 153 // offsets for y-labels 154 if (mAxisLeft.needsOffset()) { 155 offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels()); 156 } 157 158 if (mAxisRight.needsOffset()) { 159 offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.getPaintAxisLabels()); 160 } 161 162 float xlabelwidth = mXAxis.mLabelRotatedWidth; 163 164 if (mXAxis.isEnabled()) { 165 166 // offsets for x-labels 167 if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { 168 169 offsetLeft += xlabelwidth; 170 171 } else if (mXAxis.getPosition() == XAxisPosition.TOP) { 172 173 offsetRight += xlabelwidth; 174 175 } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { 176 177 offsetLeft += xlabelwidth; 178 offsetRight += xlabelwidth; 179 } 180 } 181 182 offsetTop += getExtraTopOffset(); 183 offsetRight += getExtraRightOffset(); 184 offsetBottom += getExtraBottomOffset(); 185 offsetLeft += getExtraLeftOffset(); 186 187 float minOffset = Utils.convertDpToPixel(mMinOffset); 188 189 mViewPortHandler.restrainViewPort( 190 Math.max(minOffset, offsetLeft), 191 Math.max(minOffset, offsetTop), 192 Math.max(minOffset, offsetRight), 193 Math.max(minOffset, offsetBottom)); 194 195 if (mLogEnabled) { 196 Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + ", offsetRight: " + 197 offsetRight + ", offsetBottom: " 198 + offsetBottom); 199 Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString()); 200 } 201 202 prepareOffsetMatrix(); 203 prepareValuePxMatrix(); 204 } 205 206 @Override prepareValuePxMatrix()207 protected void prepareValuePxMatrix() { 208 mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange, 209 mXAxis.mAxisMinimum); 210 mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange, 211 mXAxis.mAxisMinimum); 212 } 213 214 @Override getMarkerPosition(Highlight high)215 protected float[] getMarkerPosition(Highlight high) { 216 return new float[]{high.getDrawY(), high.getDrawX()}; 217 } 218 219 @Override getBarBounds(BarEntry e, RectF outputRect)220 public void getBarBounds(BarEntry e, RectF outputRect) { 221 222 RectF bounds = outputRect; 223 IBarDataSet set = mData.getDataSetForEntry(e); 224 225 if (set == null) { 226 outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); 227 return; 228 } 229 230 float y = e.getY(); 231 float x = e.getX(); 232 233 float barWidth = mData.getBarWidth(); 234 235 float top = x - barWidth / 2f; 236 float bottom = x + barWidth / 2f; 237 float left = y >= 0 ? y : 0; 238 float right = y <= 0 ? y : 0; 239 240 bounds.set(left, top, right, bottom); 241 242 getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); 243 244 } 245 246 protected float[] mGetPositionBuffer = new float[2]; 247 248 /** 249 * Returns a recyclable MPPointF instance. 250 * 251 * @param e 252 * @param axis 253 * @return 254 */ 255 @Override getPosition(Entry e, AxisDependency axis)256 public MPPointF getPosition(Entry e, AxisDependency axis) { 257 258 if (e == null) 259 return null; 260 261 float[] vals = mGetPositionBuffer; 262 vals[0] = e.getY(); 263 vals[1] = e.getX(); 264 265 getTransformer(axis).pointValuesToPixel(vals); 266 267 return MPPointF.getInstance(vals[0], vals[1]); 268 } 269 270 /** 271 * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point 272 * inside the BarChart. 273 * 274 * @param x 275 * @param y 276 * @return 277 */ 278 @Override getHighlightByTouchPoint(float x, float y)279 public Highlight getHighlightByTouchPoint(float x, float y) { 280 281 if (mData == null) { 282 if (mLogEnabled) 283 Log.e(LOG_TAG, "Can't select by touch. No data set."); 284 return null; 285 } else 286 return getHighlighter().getHighlight(y, x); // switch x and y 287 } 288 289 @Override getLowestVisibleX()290 public float getLowestVisibleX() { 291 getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), 292 mViewPortHandler.contentBottom(), posForGetLowestVisibleX); 293 float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.y); 294 return result; 295 } 296 297 @Override getHighestVisibleX()298 public float getHighestVisibleX() { 299 getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), 300 mViewPortHandler.contentTop(), posForGetHighestVisibleX); 301 float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.y); 302 return result; 303 } 304 305 /** 306 * ###### VIEWPORT METHODS BELOW THIS ###### 307 */ 308 309 @Override setVisibleXRangeMaximum(float maxXRange)310 public void setVisibleXRangeMaximum(float maxXRange) { 311 float xScale = mXAxis.mAxisRange / (maxXRange); 312 mViewPortHandler.setMinimumScaleY(xScale); 313 } 314 315 @Override setVisibleXRangeMinimum(float minXRange)316 public void setVisibleXRangeMinimum(float minXRange) { 317 float xScale = mXAxis.mAxisRange / (minXRange); 318 mViewPortHandler.setMaximumScaleY(xScale); 319 } 320 321 @Override setVisibleXRange(float minXRange, float maxXRange)322 public void setVisibleXRange(float minXRange, float maxXRange) { 323 float minScale = mXAxis.mAxisRange / minXRange; 324 float maxScale = mXAxis.mAxisRange / maxXRange; 325 mViewPortHandler.setMinMaxScaleY(minScale, maxScale); 326 } 327 328 @Override setVisibleYRangeMaximum(float maxYRange, AxisDependency axis)329 public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { 330 float yScale = getAxisRange(axis) / maxYRange; 331 mViewPortHandler.setMinimumScaleX(yScale); 332 } 333 334 @Override setVisibleYRangeMinimum(float minYRange, AxisDependency axis)335 public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { 336 float yScale = getAxisRange(axis) / minYRange; 337 mViewPortHandler.setMaximumScaleX(yScale); 338 } 339 340 @Override setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis)341 public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { 342 float minScale = getAxisRange(axis) / minYRange; 343 float maxScale = getAxisRange(axis) / maxYRange; 344 mViewPortHandler.setMinMaxScaleX(minScale, maxScale); 345 } 346 } 347