MPAndroidChart - 是否可以控制图表元素的 z-index?
MPAndroidChart - is it possible to control z-index of the chart elements?
我想在我的 MPAndroidChart 中按以下顺序绘制(从下到上):
- 数据连接线
- 极限线
- 数据点
可能吗?我知道方法 com.github.mikephil.charting.components.AxisBase#setDrawLimitLinesBehindData
。除一种情况外,它按预期工作。当所有数据点的Y值都相同时,效果为:
或者这个:
我希望它看起来像:
前2张图片来自MPAndroidChartAndroid图库。第 3 个来自库的 iOS 端口:Charts
我查看了 Android and iOS 版本的顺序或绘制图表,它们看起来一样。
问题:
- 是否可以控制绘制顺序?
- 系统版本之间有什么区别?
- 是否有任何其他开源库可以做到这一点?
附加信息:所有图像、线条、圆圈均由库绘制,未使用自定义图像。
类似于a previous answer here,没有public API直接设置各种绘图功能的z-index。
相反,组件在 canvas 上按顺序绘制,后面的组件绘制在前面的组件之上。这意味着你改变渲染顺序,你可以改变 z-index。
您说您想要以下绘图顺序:
- Data connecting line
- Limit line
- Data points
让我们在源代码中找到处理每个问题的方法:
数据连接线是在LineChartRenderer
里面画的方法叫:
protected drawLinear(Canvas c, ILineDataSet dataSet)
限制线画在里面XAxisRenderer
叫做:
public void renderLimitLines(Canvas c)
数据点(圆圈)在 LineChartRenderer
内部绘制,方法如下所示:
public void drawExtras(Canvas c)
这三个方法的调用顺序在 BarLineChartBase
中由 Android 的 View
:
覆盖的方法中确定
protected onDraw(Canvas canvas);
因此,要获得您想要的顺序,您只需重新安排上述 3 个方法在 onDraw(Canvas canvas)
:
中的调用顺序
下面是应该满足要求的自定义折线图的完整代码。按照设计,您仍然需要调用:
com.github.mikephil.charting.components.AxisBase#setDrawLimitLinesBehindData
但如果您愿意,您可以轻松删除 3 个 if
语句并硬编码顺序。
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();
}
}
我想在我的 MPAndroidChart 中按以下顺序绘制(从下到上):
- 数据连接线
- 极限线
- 数据点
可能吗?我知道方法 com.github.mikephil.charting.components.AxisBase#setDrawLimitLinesBehindData
。除一种情况外,它按预期工作。当所有数据点的Y值都相同时,效果为:
或者这个:
我希望它看起来像:
前2张图片来自MPAndroidChartAndroid图库。第 3 个来自库的 iOS 端口:Charts
我查看了 Android and iOS 版本的顺序或绘制图表,它们看起来一样。
问题:
- 是否可以控制绘制顺序?
- 系统版本之间有什么区别?
- 是否有任何其他开源库可以做到这一点?
附加信息:所有图像、线条、圆圈均由库绘制,未使用自定义图像。
类似于a previous answer here,没有public API直接设置各种绘图功能的z-index。
相反,组件在 canvas 上按顺序绘制,后面的组件绘制在前面的组件之上。这意味着你改变渲染顺序,你可以改变 z-index。
您说您想要以下绘图顺序:
- Data connecting line
- Limit line
- Data points
让我们在源代码中找到处理每个问题的方法:
数据连接线是在
LineChartRenderer
里面画的方法叫:protected drawLinear(Canvas c, ILineDataSet dataSet)
限制线画在里面
XAxisRenderer
叫做:public void renderLimitLines(Canvas c)
数据点(圆圈)在
LineChartRenderer
内部绘制,方法如下所示:public void drawExtras(Canvas c)
这三个方法的调用顺序在 BarLineChartBase
中由 Android 的 View
:
protected onDraw(Canvas canvas);
因此,要获得您想要的顺序,您只需重新安排上述 3 个方法在 onDraw(Canvas canvas)
:
下面是应该满足要求的自定义折线图的完整代码。按照设计,您仍然需要调用:
com.github.mikephil.charting.components.AxisBase#setDrawLimitLinesBehindData
但如果您愿意,您可以轻松删除 3 个 if
语句并硬编码顺序。
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();
}
}