How I hacked together a line chart with an inverted fill.
Looks like this:
| package com.example.ui.charting.charts; | |
| import android.content.Context; | |
| import android.util.AttributeSet; | |
| import com.github.mikephil.charting.charts.LineChart; | |
| import com.github.mikephil.charting.data.LineData; | |
| import com.github.mikephil.charting.data.LineDataSet; | |
| import com.github.mikephil.charting.utils.FillFormatter; | |
| import com.example.ui.charting.renderer.InvertedFillLineChartRenderer; | |
| public class InvertedFillLineChart extends LineChart { | |
| public InvertedFillLineChart(Context context) { | |
| super(context); | |
| } | |
| public InvertedFillLineChart(Context context, AttributeSet attrs) { | |
| super(context, attrs); | |
| } | |
| public InvertedFillLineChart(Context context, AttributeSet attrs, int defStyle) { | |
| super(context, attrs, defStyle); | |
| } | |
| @Override | |
| protected void init() { | |
| super.init(); | |
| mRenderer = new InvertedFillLineChartRenderer(this, mAnimator, mViewPortHandler); | |
| setFillFormatter(new DefaultFillFormatter()); | |
| } | |
| // Copied from MPAndroidChart because it is not accessible publicly | |
| protected class DefaultFillFormatter implements FillFormatter { | |
| @Override | |
| public float getFillLinePosition(LineDataSet dataSet, LineData data, | |
| float chartMaxY, float chartMinY) { | |
| float fillMin = 0f; | |
| if (dataSet.getYMax() > 0 && dataSet.getYMin() < 0) { | |
| fillMin = 0f; | |
| } else { | |
| if (!getAxis(dataSet.getAxisDependency()).isStartAtZeroEnabled()) { | |
| float max, min; | |
| if (data.getYMax() > 0) | |
| max = 0f; | |
| else | |
| max = chartMaxY; | |
| if (data.getYMin() < 0) | |
| min = 0f; | |
| else | |
| min = chartMinY; | |
| fillMin = dataSet.getYMin() >= 0 ? min : max; | |
| } else { | |
| fillMin = 0f; | |
| } | |
| } | |
| return fillMin; | |
| } | |
| } | |
| } |
| package com.example.ui.charting.renderer; | |
| import android.graphics.Canvas; | |
| import android.graphics.Paint; | |
| import android.graphics.Path; | |
| import com.github.mikephil.charting.animation.ChartAnimator; | |
| import com.github.mikephil.charting.data.Entry; | |
| import com.github.mikephil.charting.data.LineDataSet; | |
| import com.github.mikephil.charting.interfaces.LineDataProvider; | |
| import com.github.mikephil.charting.renderer.LineChartRenderer; | |
| import com.github.mikephil.charting.utils.Transformer; | |
| import com.github.mikephil.charting.utils.ViewPortHandler; | |
| import java.util.List; | |
| public class InvertedFillLineChartRenderer extends LineChartRenderer { | |
| public InvertedFillLineChartRenderer(LineDataProvider chart, ChartAnimator animator, | |
| ViewPortHandler viewPortHandler) { | |
| super(chart, animator, viewPortHandler); | |
| } | |
| // Mostly copied verbatim except for line 35. | |
| @Override | |
| protected void drawLinearFill(Canvas c, LineDataSet dataSet, List<Entry> entries, int minx, int maxx, | |
| Transformer trans) { | |
| mRenderPaint.setStyle(Paint.Style.FILL); | |
| mRenderPaint.setColor(dataSet.getFillColor()); | |
| // filled is drawn with less alpha | |
| mRenderPaint.setAlpha(dataSet.getFillAlpha()); | |
| // Use highest point on chart for background fill limit | |
| Path filled = generateFilledPath( | |
| entries, | |
| mChart.getYChartMax(), | |
| minx, | |
| maxx | |
| ); | |
| trans.pathValueToPixel(filled); | |
| c.drawPath(filled, mRenderPaint); | |
| // restore alpha | |
| mRenderPaint.setAlpha(255); | |
| } | |
| /** | |
| * Copied verbatim from MPAndroidChart because method is private | |
| */ | |
| private Path generateFilledPath(List<Entry> entries, float fillMin, int from, int to) { | |
| float phaseX = mAnimator.getPhaseX(); | |
| float phaseY = mAnimator.getPhaseY(); | |
| Path filled = new Path(); | |
| filled.moveTo(entries.get(from).getXIndex(), fillMin); | |
| filled.lineTo(entries.get(from).getXIndex(), entries.get(from).getVal() * phaseY); | |
| // create a new path | |
| for (int x = from + 1, count = (int) Math.ceil((to - from) * phaseX + from); x < count; x++) { | |
| Entry e = entries.get(x); | |
| filled.lineTo(e.getXIndex(), e.getVal() * phaseY); | |
| } | |
| // close up | |
| filled.lineTo( | |
| entries.get( | |
| Math.max( | |
| Math.min((int) Math.ceil((to - from) * phaseX + from) - 1, | |
| entries.size() - 1), 0)).getXIndex(), fillMin); | |
| filled.close(); | |
| return filled; | |
| } | |
| } |