Closed vasyl91 closed 5 years ago
I would need something like this:
Hmm, tried many combinations with code from the link I posted. Even created modified LineAndPointRenderer
and LineAndPointFormatter
(changes pointed with arrows <===):
class MyLineAndPointFormatter extends LineAndPointFormatter{
public MyLineAndPointFormatter() {
super(Color.rgb(154, 223, 130), null, null, null);
}
@Override
public Class<? extends SeriesRenderer> getRendererClass() {
return MyLineAndPointRenderer.class;
}
@Override
public SeriesRenderer getRendererInstance(XYPlot plot) {
return new MyLineAndPointRenderer(plot);
}
}
class MyLineAndPointRenderer extends LineAndPointRenderer<MyLineAndPointFormatter> {
private final Path path = new Path();
public MyLineAndPointRenderer(XYPlot plot) {
super(plot);
}
@Override
public void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, LineAndPointFormatter formatter) {
PointF thisPoint;
PointF lastPoint = null;
PointF firstPoint = null;
path.reset();
final List<PointF> points = getPointsCache(series);
int iStart = 0;
int iEnd = series.size();
if(SeriesUtils.getXYOrder(series) == OrderedXYSeries.XOrder.ASCENDING) {
final Region iBounds = SeriesUtils.iBounds(series, getPlot().getBounds());
iStart = iBounds.getMin().intValue();
if(iStart > 0) {
iStart--;
}
iEnd = iBounds.getMax().intValue() + 1;
if(iEnd < series.size() - 1) {
iEnd++;
}
}
for (int i = iStart; i < iEnd; i++) {
final Number y = series.getY(i);
final Number x = series.getX(i);
PointF iPoint = points.get(i);
if (y != null && x != null) {
if(iPoint == null) {
iPoint = new PointF();
points.set(i, iPoint);
}
thisPoint = iPoint;
getPlot().getBounds().transformScreen(thisPoint, x, y, plotArea);
} else {
thisPoint = null;
iPoint = null;
points.set(i, iPoint);
}
// don't need to do any of this if the line isnt going to be drawn:
if(formatter.hasLinePaint() && formatter.getInterpolationParams() == null) {
if (thisPoint != null) {
// record the first point of the new Path
if (firstPoint == null) {
path.reset();
firstPoint = thisPoint;
// create our first point at the bottom/x position so filling will look good:
path.moveTo(firstPoint.x, firstPoint.y);
}
if (lastPoint != null) {
appendToPath(path, thisPoint, lastPoint);
}
lastPoint = thisPoint;
} else {
if (lastPoint != null) {
renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
}
firstPoint = null;
lastPoint = null;
}
}
}
if(formatter.hasLinePaint()) {
if(formatter.getInterpolationParams() != null) {
List<XYCoords> interpolatedPoints = getInterpolator(
formatter.getInterpolationParams()).interpolate(series,
formatter.getInterpolationParams());
firstPoint = convertPoint(interpolatedPoints.get(ZERO), plotArea);
lastPoint = convertPoint(interpolatedPoints.get(interpolatedPoints.size()-ONE), plotArea);
path.reset();
path.moveTo(firstPoint.x, firstPoint.y);
for(int i = 1; i < interpolatedPoints.size(); i++) {
thisPoint = convertPoint(interpolatedPoints.get(i), plotArea);
path.lineTo(thisPoint.x, thisPoint.y);
}
}
if(firstPoint != null) {
renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter, series); // <=== added series
}
}
renderPoints(canvas, plotArea, series, iStart, iEnd, points, formatter);
}
public void renderPath(Canvas canvas, RectF plotArea, Path path, PointF firstPoint, PointF lastPoint, LineAndPointFormatter formatter, XYSeries series) { // <=== added series
Path outlinePath = new Path(path);
final RectRegion bounds = getPlot().getBounds();
final RectRegion plotRegion = new RectRegion(plotArea);
// determine how to close the path for filling purposes:
// We always need to calculate this path because it is also used for
// masking off for region highlighting.
switch (formatter.getFillDirection()) {
case BOTTOM:
path.lineTo(lastPoint.x, plotArea.bottom);
path.lineTo(firstPoint.x, plotArea.bottom);
path.close();
break;
case TOP:
path.lineTo(lastPoint.x, plotArea.top);
path.lineTo(firstPoint.x, plotArea.top);
path.close();
break;
case RANGE_ORIGIN:
float originPix = (float) getPlot().getBounds().getxRegion()
.transform(getPlot().getRangeOrigin()
.doubleValue(), plotArea.top, plotArea.bottom, true);
path.lineTo(lastPoint.x, originPix);
path.lineTo(firstPoint.x, originPix);
path.close();
break;
default:
throw new UnsupportedOperationException(
"Fill direction not yet implemented: " + formatter.getFillDirection());
}
if (formatter.getFillPaint() != null) {
canvas.drawPath(path, formatter.getFillPaint());
} else
// draw each region:
for (RectRegion thisRegion : bounds.intersects(formatter.getRegions().elements())) {
XYRegionFormatter regionFormatter = formatter.getRegionFormatter(thisRegion);
RectRegion thisRegionTransformed = bounds
.transform(thisRegion, plotRegion, false, true);
thisRegionTransformed.intersect(plotRegion);
if(thisRegion.isFullyDefined()) {
RectF thisRegionRectF = thisRegionTransformed.asRectF();
if (thisRegionRectF != null) {
try {
canvas.save();
canvas.clipPath(path);
canvas.drawRect(thisRegionRectF, regionFormatter.getPaint());
} finally {
canvas.restore();
}
}
}
}
// finally we draw the outline path on top of everything else:
if(formatter.hasLinePaint()) { // <===
for (int i = 0; i < series.size(); i++) {
int y = (series.getY(i)).intValue();
if (y <= 30) {
Paint lowPaint = new Paint();
lowPaint.setColor(Color.RED);
canvas.drawPath(outlinePath, lowPaint);
} else {
Paint regularPaint = new Paint();
regularPaint.setColor(Color.rgb(154, 223, 130));
canvas.drawPath(outlinePath, regularPaint);
}
}
}
path.rewind();
}
}
Called with:
MyLineAndPointFormatter lineFormatter = new MyLineAndPointFormatter();
Mostly I received such result (sometimes line was drawn properly, but color was set depending on first Y value I guess):
I'm out of ideas and definitely need your help. I've also no clue why threshold visible on chart was set to ~21(?). Wolud apreciate any answer ;)
Hi vasyl91 - As the github issue submission guidelines state, this isn't the place to ask how-to questions; feel free to post your question(s) under the Androidplot tag on Stack Overflow. Having said that, the short answer is yes it's possible by using a Shader on your formatter's line paint.
Shader works, but the "threshold" doesn't change accordingly to whats currently shown on Y axis. I rather mean sth like RectRegion applied to color of the line.
That should just be a matter of adjusting the Shader. You can either fix your boundaries to a static range and then define a gradient or background image that properly encompasses that static range, or you can dynamically generate the gradient based on the current visible range (obtainable via Plot.getBounds()
). The later is a bit harder to do but still not too terribly difficult. If you plan to use something besides linear scaling, say logarithmic, its even trickier, but still possible.
For illustration purposes, here's a screen from a quick tweak to XYPlotWithBgImgActivity (available in the demo app source):
Is there any way to render line color depending on Y value? I found this but sadly it doesn't work.