彩色 HorizontalBarChart 的 MPAndroidchart 圆形边缘
MPAndroidchart rounded edges for colorized HorizontalBarChart
我想使用 MPAndroidChart 制作一个带有圆边的彩色 HorizontalBarChart。我正在尝试为我的图表更改 HorizontalBarChartRenderer。这是我的代码:
HorizontalBarChart
初始化:
List<Float> values = new ArrayList<>();
List<Integer> colors = new ArrayList<>();
// initializing
float[] valuesArray = new float[values.size()];
for (int i = 0; i < values.size(); i++) {
valuesArray[i] = values.get(i);
}
List<BarEntry> yValues = valuesArray.length <= 0
? Collections.<BarEntry>emptyList()
: Collections.singletonList(new BarEntry(valuesArray, 0));
BarDataSet barDataSet = new BarDataSet(yValues, "");
barDataSet.setColors(colors);
barDataSet.setValueFormatter(new ValueFormatter() {
@Override
public String getFormattedValue(float value, Entry entry,
int dataSetIndex, ViewPortHandler viewPortHandler) {
return "";
}
});
chartView.setData(new BarData(new String[]{"sleep"}, barDataSet));
并扩展HorizontalBarChartRenderer
:
@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
// ...
c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j + 2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
}
结果是:
如何只为外侧制作圆边?
像这样:
我建议您在 "normal" 模式下绘制除最后一个以外的所有条形图,并首先绘制顶部(圆角)条形图并让它横跨所有条形图的整个长度。
这样一来,其他未圆化的条形将掩盖第一个绘制(圆化)条形底部不需要的圆角。
我找到了解决办法。我的想法是像往常一样绘制数据,然后清除一些区域以使边缘变圆。
我的代码:
public class CircleHorizontalBarChartRenderer extends HorizontalBarChartRenderer {
public CircleHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mShadowPaint.setColor(dataSet.getBarShadowColor());
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
// initialize the buffer
BarBuffer buffer = mBarBuffers[index];
buffer.setPhases(phaseX, phaseY);
buffer.setBarSpace(dataSet.getBarSpace());
buffer.setDataSet(index);
buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));
buffer.feed(dataSet);
trans.pointValuesToPixel(buffer.buffer);
int length = buffer.buffer.length;
float left = 0;
float right = 0;
float top = buffer.buffer[length - 3];
float bot = buffer.buffer[length - 1];
boolean leftSaved = false;
for (int j = 0; j < buffer.size(); j += 4) {
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
break;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
continue;
// Set the color for the currently drawn value.
// If the index is
// out of bounds, reuse colors.
int color = dataSet.getColor(j / 4);
mRenderPaint.setColor(color);
if (color != 0 && !leftSaved) {
leftSaved = true;
left = buffer.buffer[j];
}
if (j > 4) { // it works but its ugly
right = buffer.buffer[j - 2];
}
c.drawRect(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j + 2],
buffer.buffer[j + 3] - 10, mRenderPaint);
}
Paint erasePaint = new Paint();
erasePaint.setAntiAlias(true);
erasePaint.setStyle(Paint.Style.STROKE);
int paintWidth = 20;
erasePaint.setStrokeWidth(paintWidth);
erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
c.drawRoundRect(new RectF(left - paintWidth / 2, top, right + paintWidth / 2, bot), 30, 30, erasePaint);
}
}
PorterDuff.Mode.CLEAR 仅当您将图层类型设置为软件时才有效。所以你需要调用
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
用于图表视图或其父视图。如果您不需要透明背景,并且已知背景颜色,您可以为 erasePaint 设置此颜色,并且不使用软件层类型,这会降低性能。 erasePaint.setXfermode 这种情况也不需要。
最终结果:
我编辑了 Nick Kober 的解决方案。这是代码;
public class CircleHorizontalBarChartRenderer extends HorizontalBarChartRenderer {
public CircleHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mShadowPaint.setColor(dataSet.getBarShadowColor());
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
// initialize the buffer
BarBuffer buffer = mBarBuffers[index];
buffer.setPhases(phaseX, phaseY);
buffer.setBarWidth(dataSet.getBarBorderWidth());
buffer.setDataSet(index);
buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));
buffer.feed(dataSet);
trans.pointValuesToPixel(buffer.buffer);
int timeToChange = buffer.size() / 4;
for (int j = 0; j < buffer.size(); j += 4) {
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
break;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
continue;
// Set the color for the currently drawn value.
// If the index is
// out of bounds, reuse colors.
int color = dataSet.getColor(j / 4);
mRenderPaint.setColor(color);
if (j/4 == 0) {
c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
c.drawRect(new RectF(buffer.buffer[j] + 10 , buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), mRenderPaint);
}
else if (j/4 < timeToChange - 1) {
c.drawRect(new RectF(buffer.buffer[j] , buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), mRenderPaint);
}
else if (j/4 == timeToChange - 1) {
c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
c.drawRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2] - 10, buffer.buffer[j + 3] - 10), mRenderPaint);
}
}
}
同样,您应该像 Nick 那样编写自己的自定义渲染器 class。我添加了最后一部分(if-else if 块)。
我是这样想的,为第一条画圆角矩形。然后在其上方通过将左侧移动到右侧来绘制一个矩形。通过移动,左侧保持圆形。对于内部的,将它们全部绘制为矩形。最后一个绘制圆角矩形。然后在上面通过将右侧移动到左侧来绘制一个矩形。通过移动,右侧保持圆润。
我使用变量“timeToChange”来了解我当前正在绘制哪个柱。
And the output is like this
我想使用 MPAndroidChart 制作一个带有圆边的彩色 HorizontalBarChart。我正在尝试为我的图表更改 HorizontalBarChartRenderer。这是我的代码:
HorizontalBarChart
初始化:
List<Float> values = new ArrayList<>();
List<Integer> colors = new ArrayList<>();
// initializing
float[] valuesArray = new float[values.size()];
for (int i = 0; i < values.size(); i++) {
valuesArray[i] = values.get(i);
}
List<BarEntry> yValues = valuesArray.length <= 0
? Collections.<BarEntry>emptyList()
: Collections.singletonList(new BarEntry(valuesArray, 0));
BarDataSet barDataSet = new BarDataSet(yValues, "");
barDataSet.setColors(colors);
barDataSet.setValueFormatter(new ValueFormatter() {
@Override
public String getFormattedValue(float value, Entry entry,
int dataSetIndex, ViewPortHandler viewPortHandler) {
return "";
}
});
chartView.setData(new BarData(new String[]{"sleep"}, barDataSet));
并扩展HorizontalBarChartRenderer
:
@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
// ...
c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j + 2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
}
结果是:
如何只为外侧制作圆边?
像这样:
我建议您在 "normal" 模式下绘制除最后一个以外的所有条形图,并首先绘制顶部(圆角)条形图并让它横跨所有条形图的整个长度。
这样一来,其他未圆化的条形将掩盖第一个绘制(圆化)条形底部不需要的圆角。
我找到了解决办法。我的想法是像往常一样绘制数据,然后清除一些区域以使边缘变圆。 我的代码:
public class CircleHorizontalBarChartRenderer extends HorizontalBarChartRenderer {
public CircleHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mShadowPaint.setColor(dataSet.getBarShadowColor());
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
// initialize the buffer
BarBuffer buffer = mBarBuffers[index];
buffer.setPhases(phaseX, phaseY);
buffer.setBarSpace(dataSet.getBarSpace());
buffer.setDataSet(index);
buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));
buffer.feed(dataSet);
trans.pointValuesToPixel(buffer.buffer);
int length = buffer.buffer.length;
float left = 0;
float right = 0;
float top = buffer.buffer[length - 3];
float bot = buffer.buffer[length - 1];
boolean leftSaved = false;
for (int j = 0; j < buffer.size(); j += 4) {
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
break;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
continue;
// Set the color for the currently drawn value.
// If the index is
// out of bounds, reuse colors.
int color = dataSet.getColor(j / 4);
mRenderPaint.setColor(color);
if (color != 0 && !leftSaved) {
leftSaved = true;
left = buffer.buffer[j];
}
if (j > 4) { // it works but its ugly
right = buffer.buffer[j - 2];
}
c.drawRect(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j + 2],
buffer.buffer[j + 3] - 10, mRenderPaint);
}
Paint erasePaint = new Paint();
erasePaint.setAntiAlias(true);
erasePaint.setStyle(Paint.Style.STROKE);
int paintWidth = 20;
erasePaint.setStrokeWidth(paintWidth);
erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
c.drawRoundRect(new RectF(left - paintWidth / 2, top, right + paintWidth / 2, bot), 30, 30, erasePaint);
}
}
PorterDuff.Mode.CLEAR 仅当您将图层类型设置为软件时才有效。所以你需要调用
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
用于图表视图或其父视图。如果您不需要透明背景,并且已知背景颜色,您可以为 erasePaint 设置此颜色,并且不使用软件层类型,这会降低性能。 erasePaint.setXfermode 这种情况也不需要。
最终结果:
我编辑了 Nick Kober 的解决方案。这是代码;
public class CircleHorizontalBarChartRenderer extends HorizontalBarChartRenderer {
public CircleHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mShadowPaint.setColor(dataSet.getBarShadowColor());
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
// initialize the buffer
BarBuffer buffer = mBarBuffers[index];
buffer.setPhases(phaseX, phaseY);
buffer.setBarWidth(dataSet.getBarBorderWidth());
buffer.setDataSet(index);
buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));
buffer.feed(dataSet);
trans.pointValuesToPixel(buffer.buffer);
int timeToChange = buffer.size() / 4;
for (int j = 0; j < buffer.size(); j += 4) {
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
break;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
continue;
// Set the color for the currently drawn value.
// If the index is
// out of bounds, reuse colors.
int color = dataSet.getColor(j / 4);
mRenderPaint.setColor(color);
if (j/4 == 0) {
c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
c.drawRect(new RectF(buffer.buffer[j] + 10 , buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), mRenderPaint);
}
else if (j/4 < timeToChange - 1) {
c.drawRect(new RectF(buffer.buffer[j] , buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), mRenderPaint);
}
else if (j/4 == timeToChange - 1) {
c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
c.drawRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2] - 10, buffer.buffer[j + 3] - 10), mRenderPaint);
}
}
}
同样,您应该像 Nick 那样编写自己的自定义渲染器 class。我添加了最后一部分(if-else if 块)。
我是这样想的,为第一条画圆角矩形。然后在其上方通过将左侧移动到右侧来绘制一个矩形。通过移动,左侧保持圆形。对于内部的,将它们全部绘制为矩形。最后一个绘制圆角矩形。然后在上面通过将右侧移动到左侧来绘制一个矩形。通过移动,右侧保持圆润。
我使用变量“timeToChange”来了解我当前正在绘制哪个柱。
And the output is like this