I would like to have the following order of drawing in my MPAndroidChart (from bottom to top):
- Data connecting line
- Limit line
- Data points
Is it possible? I am aware of the method com.github.mikephil.charting.components.AxisBase#setDrawLimitLinesBehindData
. It is working as expected except for one case. When the Y value of all data points is the same, the effect is:
or this:
I would like it to look like:
The first 2 pictures are from MPAndroidChart Android library. The 3rd one is from iOS port of the library: Charts
I looked at the order or drawing the chart in Android and iOS versions and they look the same.
Questions:
- Is it possible to control the drawing order?
- What comes the difference from between system versions?
- Is there any other open source library that can do that?
Additional info: all images, lines, circles are drawn by the library, custom images are not used.
Similar to a previous answer here there is no public API exposed to directly set the z-index of the various drawing features.
Instead, components are drawn in order on the canvas with later components being drawn over earlier ones. This means you change the rendering order, you can change the z-index.
You say you would like the following drawing order:
- Data connecting line
- Limit line
- Data points
Let's find the methods in the source code that deal with each of those:
Data connecting line is drawn inside LineChartRenderer
in a method called:
protected drawLinear(Canvas c, ILineDataSet dataSet)
The limit line is drawn inside XAxisRenderer
called:
public void renderLimitLines(Canvas c)
The data points (circles) are drawn inside LineChartRenderer
in a method that looks like this:
public void drawExtras(Canvas c)
The calling order of these three methods is determined inside BarLineChartBase
in the method overriden from Android's View
:
protected onDraw(Canvas canvas);
So to get the order you want you will have to simply re-arrange the order of calling of the above 3 methods inside onDraw(Canvas canvas)
:
Here is the complete code for a custom line chart that should meet the requirement. By design, you will still have to call:
com.github.mikephil.charting.components.AxisBase#setDrawLimitLinesBehindData
But you could easily remove the 3 if
statements and hard-code the order should you so desire.
CustomZIndexBarLineBase.java
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import com.github.mikephil.charting.charts.BarLineChartBase;
import com.github.mikephil.charting.data.LineData;
/**
* Created by David on 11/01/2017.
*/
public class CustomZIndexLineChartBase extends BarLineChartBase<LineData> {
public CustomZIndexLineChartBase(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomZIndexLineChartBase(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomZIndexLineChartBase(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mData == null)
return;
// execute all drawing commands
drawGridBackground(canvas);
if (mAxisLeft.isEnabled())
mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted());
if (mAxisRight.isEnabled())
mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted());
if (mXAxis.isEnabled())
mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false);
mXAxisRenderer.renderAxisLine(canvas);
mAxisRendererLeft.renderAxisLine(canvas);
mAxisRendererRight.renderAxisLine(canvas);
if (mAutoScaleMinMaxEnabled) {
autoScale();
}
mXAxisRenderer.renderGridLines(canvas);
mAxisRendererLeft.renderGridLines(canvas);
mAxisRendererRight.renderGridLines(canvas);
if (mXAxis.isDrawLimitLinesBehindDataEnabled())
mXAxisRenderer.renderLimitLines(canvas);
if (mAxisLeft.isDrawLimitLinesBehindDataEnabled())
mAxisRendererLeft.renderLimitLines(canvas);
if (mAxisRight.isDrawLimitLinesBehindDataEnabled())
mAxisRendererRight.renderLimitLines(canvas);
int clipRestoreCount = canvas.save();
canvas.clipRect(mViewPortHandler.getContentRect());
mRenderer.drawData(canvas); //NOTE: draws line between points
if (valuesToHighlight())
mRenderer.drawHighlighted(canvas, mIndicesToHighlight);
canvas.restoreToCount(clipRestoreCount);
//NOTE: draws limit line
if (!mXAxis.isDrawLimitLinesBehindDataEnabled())
mXAxisRenderer.renderLimitLines(canvas);
if (!mAxisLeft.isDrawLimitLinesBehindDataEnabled())
mAxisRendererLeft.renderLimitLines(canvas);
if (!mAxisRight.isDrawLimitLinesBehindDataEnabled())
mAxisRendererRight.renderLimitLines(canvas);
mRenderer.drawExtras(canvas); //NOTE: draws circles
mXAxisRenderer.renderAxisLabels(canvas);
mAxisRendererLeft.renderAxisLabels(canvas);
mAxisRendererRight.renderAxisLabels(canvas);
if (isClipValuesToContentEnabled()) {
clipRestoreCount = canvas.save();
canvas.clipRect(mViewPortHandler.getContentRect());
mRenderer.drawValues(canvas);
canvas.restoreToCount(clipRestoreCount);
} else {
mRenderer.drawValues(canvas);
}
mLegendRenderer.renderLegend(canvas);
drawDescription(canvas);
drawMarkers(canvas);
}
}
CustomZIndexLineChart.java
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
import com.github.mikephil.charting.renderer.LineChartRenderer;
/**
* Created by David on 11/01/2017.
*/
public class CustomZIndexLineChart extends CustomZIndexLineChartBase implements LineDataProvider {
public CustomZIndexLineChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomZIndexLineChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomZIndexLineChart(Context context) {
super(context);
}
@Override
protected void init() {
super.init();
mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
public LineData getLineData() {
return mData;
}
@Override
protected void onDetachedFromWindow() {
// releases the bitmap in the renderer to avoid oom error
if (mRenderer != null && mRenderer instanceof LineChartRenderer) {
((LineChartRenderer) mRenderer).releaseBitmap();
}
super.onDetachedFromWindow();
}
}