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; | |
} | |
} |