From e2659bcfbf8ac3c1f6d4d45f9dd55768124f3664 Mon Sep 17 00:00:00 2001 From: ett1997 Date: Thu, 10 Apr 2025 17:22:48 -0400 Subject: [PATCH] Enable vertical shading --- .../charting/components/AxisBase.java | 46 ++++ .../charting/components/LimitRange.java | 252 ++++++++++++++++++ .../charting/renderer/AxisRenderer.java | 16 ++ .../charting/renderer/YAxisRenderer.java | 121 +++++++++ .../appdev/chartexample/LineChartActivity.kt | 18 ++ 5 files changed, 453 insertions(+) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitRange.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 71d95c3236..1b8b270fe1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -108,6 +108,11 @@ public abstract class AxisBase extends ComponentBase { */ protected List mLimitLines; + /** + * array of limit ranges that can be set for the axis + */ + protected List mLimitRanges; + /** * flag indicating the limit lines layer depth */ @@ -204,6 +209,7 @@ public AxisBase() { this.mXOffset = Utils.convertDpToPixel(5f); this.mYOffset = Utils.convertDpToPixel(5f); this.mLimitLines = new ArrayList(); + this.mLimitRanges = new ArrayList(); } /** @@ -454,6 +460,21 @@ public void addLimitLine(LimitLine l) { } } + /** + * Adds a new LimitLine to this axis. + * + * @param l + */ + public void addLimitRange(LimitRange l) { + mLimitRanges.add(l); + + if (mLimitRanges.size() > 6) { + Log.e("MPAndroiChart", + "Warning! You have more than 6 LimitLines on your axis, do you really want " + + "that?"); + } + } + /** * Removes the specified LimitLine from the axis. * @@ -470,6 +491,22 @@ public void removeAllLimitLines() { mLimitLines.clear(); } + /** + * Removes the specified LimitRange from the axis. + * + * @param l + */ + public void removeLimitRange(LimitRange l) { + mLimitRanges.remove(l); + } + + /** + * Removes all LimitLines from the axis. + */ + public void removeAllLimitRanges() { + mLimitRanges.clear(); + } + /** * Returns the LimitLines of this axis. * @@ -479,6 +516,15 @@ public List getLimitLines() { return mLimitLines; } + /** + * Returns the LimitRanges of this axis. + * + * @return + */ + public List getLimitRanges() { + return mLimitRanges; + } + /** * If this is set to true, the LimitLines are drawn behind the actual data, * otherwise on top. Default: false diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitRange.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitRange.java new file mode 100644 index 0000000000..13d4b2318b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitRange.java @@ -0,0 +1,252 @@ + +package com.github.mikephil.charting.components; + +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Paint; + +import com.github.mikephil.charting.utils.Utils; + +/** + * The limit line is an additional feature for all Line-, Bar- and + * ScatterCharts. It allows the displaying of an additional line in the chart + * that marks a certain maximum / limit on the specified axis (x- or y-axis). + * + * @author Philipp Jahoda + */ +public class LimitRange extends ComponentBase { + + public static class Range{ + private final float mLow; + private final float mHigh; + Range(float r1, float r2){ + if(r1 < r2){ + mLow = r1; + mHigh = r2; + } else { + mLow = r2; + mHigh = r1; + } + } + public float getLow(){ + return mLow; + } + public float getHigh(){ + return mHigh; + } + } + + /** limit / maximum (the y-value or xIndex) */ + private Range mLimit; + + /** the width of the limit line */ + private float mLineWidth = 0f; + + /** the color of the limit line */ + private int mLineColor = Color.rgb(237, 91, 91); + + /** the color of the Range */ + private int mRangeColor = Color.rgb(128, 128, 128); + + /** the style of the label text */ + private Paint.Style mTextStyle = Paint.Style.FILL; + + /** label string that is drawn next to the limit line */ + private String mLabel = ""; + + /** the path effect of this LimitLine that makes dashed lines possible */ + private DashPathEffect mDashPathEffect = null; + + /** indicates the position of the LimitLine label */ + private LimitLine.LimitLabelPosition mLabelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP; + + /** + * Constructor with limit. + * + * @param firstLimit - the position (the value) on the y-axis (y-value) or x-axis + * (xIndex) where this line should appear + * @param secondLimit - the position (the value) on the y-axis (y-value) or x-axis + * (xIndex) where this line should appear + */ + public LimitRange(float firstLimit, float secondLimit) { + mLimit = new Range(firstLimit, secondLimit); + } + + /** + * Constructor with limit and label. + * + * @param firstLimit - the position (the value) on the y-axis (y-value) or x-axis + * (xIndex) where this line should appear + * @param secondLimit - the position (the value) on the y-axis (y-value) or x-axis + * (xIndex) where this line should appear + * @param label - provide "" if no label is required + */ + public LimitRange(float firstLimit, float secondLimit, String label) { + mLimit = new Range(firstLimit, secondLimit); + mLabel = label; + } + + /** + * Returns the limit that is set for this line. + * + * @return + */ + public Range getLimit() { + return mLimit; + } + + /** + * set the line width of the chart (min = 0.2f, max = 12f); default 2f NOTE: + * thinner line == better performance, thicker line == worse performance + * + * @param width + */ + public void setLineWidth(float width) { + if (width > 12.0f) + width = 12.0f; + mLineWidth = Utils.convertDpToPixel(width); + } + + /** + * returns the width of limit line + * + * @return + */ + public float getLineWidth() { + return mLineWidth; + } + + /** + * Sets the linecolor for this LimitLine. Make sure to use + * getResources().getColor(...) + * + * @param color + */ + public void setLineColor(int color) { + mLineColor = color; + } + + /** + * Sets the range color for this LimitRange. Make sure to use + * getResources().getColor(...) + * + * @param color + */ + public void setRangeColor(int color) { + mRangeColor = color; + } + + /** + * Returns the color that is used for this LimitLine + * + * @return + */ + public int getLineColor() { + return mLineColor; + } + + /** + * Returns the color that is used for this LimitRange + * + * @return + */ + public int getRangeColor() { + return mRangeColor; + } + + /** + * Enables the line to be drawn in dashed mode, e.g. like this "- - - - - -" + * + * @param lineLength the length of the line pieces + * @param spaceLength the length of space inbetween the pieces + * @param phase offset, in degrees (normally, use 0) + */ + public void enableDashedLine(float lineLength, float spaceLength, float phase) { + mDashPathEffect = new DashPathEffect(new float[] { + lineLength, spaceLength + }, phase); + } + + /** + * Disables the line to be drawn in dashed mode. + */ + public void disableDashedLine() { + mDashPathEffect = null; + } + + /** + * Returns true if the dashed-line effect is enabled, false if not. Default: + * disabled + * + * @return + */ + public boolean isDashedLineEnabled() { + return mDashPathEffect == null ? false : true; + } + + /** + * returns the DashPathEffect that is set for this LimitLine + * + * @return + */ + public DashPathEffect getDashPathEffect() { + return mDashPathEffect; + } + + /** + * Sets the color of the value-text that is drawn next to the LimitLine. + * Default: Paint.Style.FILL_AND_STROKE + * + * @param style + */ + public void setTextStyle(Paint.Style style) { + this.mTextStyle = style; + } + + /** + * Returns the color of the value-text that is drawn next to the LimitLine. + * + * @return + */ + public Paint.Style getTextStyle() { + return mTextStyle; + } + + /** + * Sets the position of the LimitLine value label (either on the right or on + * the left edge of the chart). Not supported for RadarChart. + * + * @param pos + */ + public void setLabelPosition(LimitLine.LimitLabelPosition pos) { + mLabelPosition = pos; + } + + /** + * Returns the position of the LimitLine label (value). + * + * @return + */ + public LimitLine.LimitLabelPosition getLabelPosition() { + return mLabelPosition; + } + + /** + * Sets the label that is drawn next to the limit line. Provide "" if no + * label is required. + * + * @param label + */ + public void setLabel(String label) { + mLabel = label; + } + + /** + * Returns the label that is drawn next to the limit line. + * + * @return + */ + public String getLabel() { + return mLabel; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 9d2889d9cb..62705d81d9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -45,6 +45,16 @@ public abstract class AxisRenderer extends Renderer { */ protected Paint mLimitLinePaint; + /** + * paint used for the limit ranges + */ + protected Paint mLimitRangePaint; + + /** + * paint used for the limit range fill + */ + protected Paint mLimitRangePaintFill; + public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis) { super(viewPortHandler); @@ -68,6 +78,12 @@ public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mLimitLinePaint.setStyle(Paint.Style.STROKE); + + mLimitRangePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLimitRangePaint.setStyle(Paint.Style.STROKE); + + mLimitRangePaintFill = new Paint(Paint.ANTI_ALIAS_FLAG); + mLimitRangePaintFill.setStyle(Style.FILL); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index 4a8367bfa4..175c0bbbba 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -8,6 +8,7 @@ import android.graphics.RectF; import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.LimitRange; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; @@ -279,6 +280,7 @@ protected void drawZeroLine(Canvas c) { } protected Path mRenderLimitLines = new Path(); + protected Path mRenderLimitRanges = new Path(); protected float[] mRenderLimitLinesBuffer = new float[2]; protected RectF mLimitLineClippingRect = new RectF(); /** @@ -378,5 +380,124 @@ public void renderLimitLines(Canvas c) { c.restoreToCount(clipRestoreCount); } + + // Now the ranges + + List limitRanges = mYAxis.getLimitRanges(); + + if (limitRanges == null || limitRanges.size() <= 0) + return; + + float[] ptsr = new float[2]; + ptsr[0] = 0; + ptsr[1] = 0; + float[] ptsr2 = new float[2]; + ptsr2[0] = 0; + ptsr2[1] = 0; + Path limitRangePath = mRenderLimitRanges; + Path limitRangePathFill = mRenderLimitRanges; + limitRangePath.reset(); + limitRangePathFill.reset(); + + for (int i = 0; i < limitRanges.size(); i++) { + + LimitRange l = limitRanges.get(i); + + if (!l.isEnabled()) + continue; + + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); + c.clipRect(mLimitLineClippingRect); + + mLimitRangePaint.setStyle(Paint.Style.STROKE); + mLimitRangePaint.setColor(l.getLineColor()); + mLimitRangePaint.setStrokeWidth(l.getLineWidth()); + mLimitRangePaint.setPathEffect(l.getDashPathEffect()); + + mLimitRangePaintFill.setStyle(Paint.Style.FILL); + mLimitRangePaintFill.setColor(l.getRangeColor()); + + ptsr[1] = l.getLimit().getHigh(); + ptsr2[1] = l.getLimit().getLow(); + + mTrans.pointValuesToPixel(ptsr); + mTrans.pointValuesToPixel(ptsr2); + + limitRangePathFill.moveTo(mViewPortHandler.contentLeft(), ptsr[1]); + limitRangePathFill.addRect( + mViewPortHandler.contentLeft(), + ptsr[1], + mViewPortHandler.contentRight(), + ptsr2[1], + Path.Direction.CW + ); + c.drawPath(limitRangePathFill, mLimitRangePaintFill); + limitRangePathFill.reset(); + + if(l.getLineWidth() > 0) { + limitRangePath.moveTo(mViewPortHandler.contentLeft(), ptsr[1]); + limitRangePath.lineTo(mViewPortHandler.contentRight(), ptsr[1]); + c.drawPath(limitRangePath, mLimitRangePaint); + + limitRangePath.moveTo(mViewPortHandler.contentLeft(), ptsr2[1]); + limitRangePath.lineTo(mViewPortHandler.contentRight(), ptsr2[1]); + c.drawPath(limitRangePath, mLimitRangePaint); + } + + limitRangePath.reset(); + + String label = l.getLabel(); + + // if drawing the limit-value label is enabled + if (label != null && !label.equals("")) { + + mLimitRangePaint.setStyle(l.getTextStyle()); + mLimitRangePaint.setPathEffect(null); + mLimitRangePaint.setColor(l.getTextColor()); + mLimitRangePaint.setTypeface(l.getTypeface()); + mLimitRangePaint.setStrokeWidth(0.5f); + mLimitRangePaint.setTextSize(l.getTextSize()); + + final float labelLineHeight = Utils.calcTextHeight(mLimitRangePaint, label); + float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); + float yOffset = l.getLineWidth() + labelLineHeight + l.getYOffset(); + + final LimitLine.LimitLabelPosition position = l.getLabelPosition(); + + if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + mLimitRangePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + ptsr[1] - yOffset + labelLineHeight, mLimitRangePaint); + + } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitRangePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + ptsr[1] + yOffset, mLimitRangePaint); + + } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitRangePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.contentLeft() + xOffset, + ptsr[1] - yOffset + labelLineHeight, mLimitRangePaint); + + } else { + + mLimitRangePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.offsetLeft() + xOffset, + ptsr[1] + yOffset, mLimitRangePaint); + } + } + + c.restoreToCount(clipRestoreCount); + } + } } diff --git a/app/src/main/java/info/appdev/chartexample/LineChartActivity.kt b/app/src/main/java/info/appdev/chartexample/LineChartActivity.kt index 58a3df221d..bae1538aec 100644 --- a/app/src/main/java/info/appdev/chartexample/LineChartActivity.kt +++ b/app/src/main/java/info/appdev/chartexample/LineChartActivity.kt @@ -17,6 +17,7 @@ import com.github.mikephil.charting.animation.Easing import com.github.mikephil.charting.components.Legend.LegendForm import com.github.mikephil.charting.components.LimitLine import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition +import com.github.mikephil.charting.components.LimitRange import com.github.mikephil.charting.data.Entry import com.github.mikephil.charting.data.LineDataSet import com.github.mikephil.charting.highlight.Highlight @@ -108,6 +109,19 @@ class LineChartActivity : DemoBase(), OnSeekBarChangeListener, OnChartValueSelec lineColor = Color.GREEN } + val limitRange = LimitRange(45f, 90f, "Middle Range").apply { + lineWidth = 2f + labelPosition = LimitLabelPosition.RIGHT_TOP + textSize = 10f + typeface = tfRegular + lineColor = Color.RED + rangeColor = Color.argb(30, 255, 235, 0) + } + + val limitRangeLower = LimitRange(45f, 52f).apply { + rangeColor = Color.argb(30, 230, 0, 0) + } + // draw limit lines behind data instead of on top binding.chart1.axisLeft.setDrawLimitLinesBehindData(true) binding.chart1.xAxis.setDrawLimitLinesBehindData(true) @@ -117,6 +131,10 @@ class LineChartActivity : DemoBase(), OnSeekBarChangeListener, OnChartValueSelec binding.chart1.axisLeft.addLimitLine(limitLineLower) // binding.chart1.axisLeft.addLimitLine(llXAxis10) + // add limit range + binding.chart1.axisLeft.addLimitRange(limitRange) + binding.chart1.axisLeft.addLimitRange(limitRangeLower) + // add data binding.seekBarX.progress = 45 binding.seekBarY.progress = 180