在折线图上显示图像而不是圆圈
Show image instead of circle on LineChart
我使用 MPAndroidChart 库创建了 LineChart
,一切正常。
现在我要做的是为图表上的每个条目显示一个可绘制对象(图像)而不是默认圆圈。
我尝试了 API 中的很多选项,但没有成功。
谁能告诉我该怎么做?
在@David Rawson 的建议和这个 post MPAndroidChart LineChart custom highlight drawable
的帮助下,尝试了很多东西之后终于
我已经成功创建了一个自定义渲染器,它将图表中的默认圆形图像替换为提供的图像。
以下是解决方案的代码片段。
class ImageLineChartRenderer extends LineChartRenderer {
private final LineChart lineChart;
private final Bitmap image;
ImageLineChartRenderer(LineChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap image) {
super(chart, animator, viewPortHandler);
this.lineChart = chart;
this.image = image;
}
private float[] mCirclesBuffer = new float[2];
@Override
protected void drawCircles(Canvas c) {
mRenderPaint.setStyle(Paint.Style.FILL);
float phaseY = mAnimator.getPhaseY();
mCirclesBuffer[0] = 0;
mCirclesBuffer[1] = 0;
List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();
//Draw bitmap image for every data set with size as radius * 10, and store it in scaled bitmaps array
Bitmap[] scaledBitmaps = new Bitmap[dataSets.size()];
float[] scaledBitmapOffsets = new float[dataSets.size()];
for (int i = 0; i < dataSets.size(); i++) {
float imageSize = dataSets.get(i).getCircleRadius() * 10;
scaledBitmapOffsets[i] = imageSize / 2f;
scaledBitmaps[i] = scaleImage((int) imageSize);
}
for (int i = 0; i < dataSets.size(); i++) {
ILineDataSet dataSet = dataSets.get(i);
if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || dataSet.getEntryCount() == 0)
continue;
mCirclePaintInner.setColor(dataSet.getCircleHoleColor());
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mXBounds.set(mChart, dataSet);
int boundsRangeCount = mXBounds.range + mXBounds.min;
for (int j = mXBounds.min; j <= boundsRangeCount; j++) {
Entry e = dataSet.getEntryForIndex(j);
if (e == null) break;
mCirclesBuffer[0] = e.getX();
mCirclesBuffer[1] = e.getY() * phaseY;
trans.pointValuesToPixel(mCirclesBuffer);
if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0]))
break;
if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) || !mViewPortHandler.isInBoundsY(mCirclesBuffer[1]))
continue;
if (scaledBitmaps[i] != null) {
c.drawBitmap(scaledBitmaps[i],
mCirclesBuffer[0] - scaledBitmapOffsets[i],
mCirclesBuffer[1] - scaledBitmapOffsets[i],
mRenderPaint);
}
}
}
}
private Bitmap scaleImage(int radius) {
return Bitmap.createScaledBitmap(image, radius, radius, false);
}
希望这对某人有所帮助。
您可以简单地使用 drawable 创建条目,它将被绘制而不是图形上的圆。
new Entry(i, value, drawable)
我也遇到过这个问题,但有更具体的要求:
- 仅在图表上的特定点绘制圆圈。
- 可以灵活地在不同情况下使用不同的可绘制对象。
最后,我做到了:
其中每个圆圈实际上是一个常规可绘制对象,可以用其他任何东西替换。
用下一种方式解决了:
1.Create 一个以可绘制对象为参数的 Entry 子类。
/**
* Represents an [Entry] which is able to use drawables (including different drawables for different points) instead of the circle.
* For the points where you don't need points use a regular [Entry].
*/
class DrawableCircleEntry @JvmOverloads constructor(
@DrawableRes val circleDrawableRes: Int,
x: Float,
y: Float,
icon: Drawable? = null,
data: Any? = null
) : Entry(x, y, icon, data)
2.Create 自定义渲染,
如果条目是 DrawableCircleEntry 类型,则绘制可绘制对象而不是圆。
如果 try 是常规条目,则不画圆圈。
internal class LineChartCustomCirclesRenderer(private val context: Context, lineChart: LineChart
) : LineChartRenderer(lineChart, lineChart.animator, lineChart.viewPortHandler) {
// Contains (left, top) coordinates of the next circle which has to be drawn
private val circleCoordinates = FloatArray(2)
// Cached drawables
private val drawablesCache = SparseArray<Drawable>()
override fun drawCircles(canvas: Canvas) {
val phaseY = mAnimator.phaseY
circleCoordinates[0] = 0f
circleCoordinates[1] = 0f
val dataSets = mChart.lineData.dataSets
dataSets.forEach { dataSet ->
if (!dataSet.isVisible || !dataSet.isDrawCirclesEnabled || dataSet.entryCount == 0)
return@forEach
val transformer = mChart.getTransformer(dataSet.axisDependency)
mXBounds[mChart] = dataSet
val boundsRangeCount = mXBounds.range + mXBounds.min
for (i in mXBounds.min..boundsRangeCount) {
// don't do anything in case entry is not type of DrawableCircleEntry
val entry = dataSet.getEntryForIndex(i) as? DrawableCircleEntry
?: continue
circleCoordinates[0] = entry.x
circleCoordinates[1] = entry.y * phaseY
transformer.pointValuesToPixel(circleCoordinates)
if (!mViewPortHandler.isInBoundsRight(circleCoordinates[0])) break
if (!mViewPortHandler.isInBoundsLeft(circleCoordinates[0]) || !mViewPortHandler.isInBoundsY(circleCoordinates[1])) continue
// Drawable radius is taken as `dataSet.circleRadius`
val radius = dataSet.circleRadius
// Retrieve the drawable, center it and draw on canvas
getDrawable(entry.circleDrawableRes)?.run {
setBounds((circleCoordinates[0] - radius).roundToInt(), (circleCoordinates[1] - radius).roundToInt(),
(circleCoordinates[0] + radius).roundToInt(), (circleCoordinates[1] + radius).roundToInt())
draw(canvas)
}
}
}
}
private fun getDrawable(@DrawableRes drawableRes: Int): Drawable? {
drawablesCache[drawableRes]?.let {
return it
}
return ContextCompat.getDrawable(context, drawableRes)
.also { drawablesCache.append(drawableRes, it) }
}
}
3.Enable 数据集的圆圈并设置所需的半径。可绘制对象的大小将为 radius*2
dataSet.setDrawCircles(true)
dataSet.circleRadius = 3f
4.When构造Entries,在不需要画圆的地方创建普通Entry,需要画圆的时候创建DrawableCircleEntry。
例如,
...
val entry = when {
someCondition -> DrawableCircleEntry(R.drawable.your_awesome_drawable, floatIndex, floatValue)
anotherCondition -> DrawableCircleEntry(R.drawable.your_another_drawable, floatIndex, floatValue)
else -> Entry(floatIndex, floatValue)
}
...
我案例中的一个可绘制对象如下所示:
<item>
<shape
android:shape="ring"
android:thickness="@dimen/chart_circle_stroke_thickness"
android:useLevel="false">
<solid android:color="#497EFF" />
</shape>
</item>
但它可以是任何其他。
尽情享受吧。
我使用 MPAndroidChart 库创建了 LineChart
,一切正常。
现在我要做的是为图表上的每个条目显示一个可绘制对象(图像)而不是默认圆圈。
我尝试了 API 中的很多选项,但没有成功。
谁能告诉我该怎么做?
在@David Rawson 的建议和这个 post MPAndroidChart LineChart custom highlight drawable
的帮助下,尝试了很多东西之后终于我已经成功创建了一个自定义渲染器,它将图表中的默认圆形图像替换为提供的图像。
以下是解决方案的代码片段。
class ImageLineChartRenderer extends LineChartRenderer {
private final LineChart lineChart;
private final Bitmap image;
ImageLineChartRenderer(LineChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap image) {
super(chart, animator, viewPortHandler);
this.lineChart = chart;
this.image = image;
}
private float[] mCirclesBuffer = new float[2];
@Override
protected void drawCircles(Canvas c) {
mRenderPaint.setStyle(Paint.Style.FILL);
float phaseY = mAnimator.getPhaseY();
mCirclesBuffer[0] = 0;
mCirclesBuffer[1] = 0;
List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();
//Draw bitmap image for every data set with size as radius * 10, and store it in scaled bitmaps array
Bitmap[] scaledBitmaps = new Bitmap[dataSets.size()];
float[] scaledBitmapOffsets = new float[dataSets.size()];
for (int i = 0; i < dataSets.size(); i++) {
float imageSize = dataSets.get(i).getCircleRadius() * 10;
scaledBitmapOffsets[i] = imageSize / 2f;
scaledBitmaps[i] = scaleImage((int) imageSize);
}
for (int i = 0; i < dataSets.size(); i++) {
ILineDataSet dataSet = dataSets.get(i);
if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || dataSet.getEntryCount() == 0)
continue;
mCirclePaintInner.setColor(dataSet.getCircleHoleColor());
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mXBounds.set(mChart, dataSet);
int boundsRangeCount = mXBounds.range + mXBounds.min;
for (int j = mXBounds.min; j <= boundsRangeCount; j++) {
Entry e = dataSet.getEntryForIndex(j);
if (e == null) break;
mCirclesBuffer[0] = e.getX();
mCirclesBuffer[1] = e.getY() * phaseY;
trans.pointValuesToPixel(mCirclesBuffer);
if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0]))
break;
if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) || !mViewPortHandler.isInBoundsY(mCirclesBuffer[1]))
continue;
if (scaledBitmaps[i] != null) {
c.drawBitmap(scaledBitmaps[i],
mCirclesBuffer[0] - scaledBitmapOffsets[i],
mCirclesBuffer[1] - scaledBitmapOffsets[i],
mRenderPaint);
}
}
}
}
private Bitmap scaleImage(int radius) {
return Bitmap.createScaledBitmap(image, radius, radius, false);
}
希望这对某人有所帮助。
您可以简单地使用 drawable 创建条目,它将被绘制而不是图形上的圆。
new Entry(i, value, drawable)
我也遇到过这个问题,但有更具体的要求:
- 仅在图表上的特定点绘制圆圈。
- 可以灵活地在不同情况下使用不同的可绘制对象。
最后,我做到了:
其中每个圆圈实际上是一个常规可绘制对象,可以用其他任何东西替换。
用下一种方式解决了:
1.Create 一个以可绘制对象为参数的 Entry 子类。
/**
* Represents an [Entry] which is able to use drawables (including different drawables for different points) instead of the circle.
* For the points where you don't need points use a regular [Entry].
*/
class DrawableCircleEntry @JvmOverloads constructor(
@DrawableRes val circleDrawableRes: Int,
x: Float,
y: Float,
icon: Drawable? = null,
data: Any? = null
) : Entry(x, y, icon, data)
2.Create 自定义渲染,
如果条目是 DrawableCircleEntry 类型,则绘制可绘制对象而不是圆。
如果 try 是常规条目,则不画圆圈。
internal class LineChartCustomCirclesRenderer(private val context: Context, lineChart: LineChart ) : LineChartRenderer(lineChart, lineChart.animator, lineChart.viewPortHandler) { // Contains (left, top) coordinates of the next circle which has to be drawn private val circleCoordinates = FloatArray(2) // Cached drawables private val drawablesCache = SparseArray<Drawable>() override fun drawCircles(canvas: Canvas) { val phaseY = mAnimator.phaseY circleCoordinates[0] = 0f circleCoordinates[1] = 0f val dataSets = mChart.lineData.dataSets dataSets.forEach { dataSet -> if (!dataSet.isVisible || !dataSet.isDrawCirclesEnabled || dataSet.entryCount == 0) return@forEach val transformer = mChart.getTransformer(dataSet.axisDependency) mXBounds[mChart] = dataSet val boundsRangeCount = mXBounds.range + mXBounds.min for (i in mXBounds.min..boundsRangeCount) { // don't do anything in case entry is not type of DrawableCircleEntry val entry = dataSet.getEntryForIndex(i) as? DrawableCircleEntry ?: continue circleCoordinates[0] = entry.x circleCoordinates[1] = entry.y * phaseY transformer.pointValuesToPixel(circleCoordinates) if (!mViewPortHandler.isInBoundsRight(circleCoordinates[0])) break if (!mViewPortHandler.isInBoundsLeft(circleCoordinates[0]) || !mViewPortHandler.isInBoundsY(circleCoordinates[1])) continue // Drawable radius is taken as `dataSet.circleRadius` val radius = dataSet.circleRadius // Retrieve the drawable, center it and draw on canvas getDrawable(entry.circleDrawableRes)?.run { setBounds((circleCoordinates[0] - radius).roundToInt(), (circleCoordinates[1] - radius).roundToInt(), (circleCoordinates[0] + radius).roundToInt(), (circleCoordinates[1] + radius).roundToInt()) draw(canvas) } } } } private fun getDrawable(@DrawableRes drawableRes: Int): Drawable? { drawablesCache[drawableRes]?.let { return it } return ContextCompat.getDrawable(context, drawableRes) .also { drawablesCache.append(drawableRes, it) } }
}
3.Enable 数据集的圆圈并设置所需的半径。可绘制对象的大小将为 radius*2
dataSet.setDrawCircles(true)
dataSet.circleRadius = 3f
4.When构造Entries,在不需要画圆的地方创建普通Entry,需要画圆的时候创建DrawableCircleEntry。 例如,
...
val entry = when {
someCondition -> DrawableCircleEntry(R.drawable.your_awesome_drawable, floatIndex, floatValue)
anotherCondition -> DrawableCircleEntry(R.drawable.your_another_drawable, floatIndex, floatValue)
else -> Entry(floatIndex, floatValue)
}
...
我案例中的一个可绘制对象如下所示:
<item>
<shape
android:shape="ring"
android:thickness="@dimen/chart_circle_stroke_thickness"
android:useLevel="false">
<solid android:color="#497EFF" />
</shape>
</item>
但它可以是任何其他。
尽情享受吧。