[英]How to make line with rounded (smooth) corners with AndroidPlot
I have a small problem with ploting my graph.我在绘制图表时遇到了一个小问题。 On a picture below is what I have already done.
下面的图片是我已经完成的。
The graph should represent the actual signal strength of available Wi-Fi.network(s).该图应表示可用 Wi-Fi.network(s) 的实际信号强度。 It's a simple
XYPlot
here data are represented with SimpleXYSeries
(values are dynamically created).这是一个简单的
XYPlot
,这里的数据用SimpleXYSeries
表示(值是动态创建的)。
Here is a little snippet of code (only for example):这是一小段代码(仅作为示例):
plot = (XYPlot) findViewById(R.id.simplexyPlot);
series1 = new SimpleXYSeries(Arrays.asList(series1Numbers),
SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Link 1");
f1 = new LineAndPointFormatter(color.getColor(), null,
Color.argb(60, color.getRed(), color.getGreen(), color.getBlue()), null);
plot.addSeries(series1, f1);
The example in the picture is a dynamic simulation of dB changes.图中的例子是dB变化的动态模拟。 Everything works, I guess, correctly, but what I want to achieve is to have line with "rounded" corners (see the picture to see what I mean).
我猜一切正常,但我想要实现的是与“圆角”对齐(请参阅图片了解我的意思)。
I already tried to customize LineFormatter:我已经尝试自定义 LineFormatter:
f1.getFillPaint().setStrokeJoin(Join.ROUND);
f1.getFillPaint().setStrokeWidth(8);
But this didn't work as expected.但这没有按预期工作。
Note: The Wifi Analyzer application has a similar graph and its graph has the rounded corners I want.注意: Wifi Analyzer应用程序有一个类似的图形,它的图形有我想要的圆角。 It looks like this:
它看起来像这样:
You can use Path.cubicTo() method. 您可以使用Path.cubicTo()方法。 It draws a line using cubic spline algorithm which results in the smoothing effect you want.
它使用三次样条算法绘制一条线,从而产生您想要的平滑效果。
Checkout the answer to a similar question here , where a guy is talking about cubic splines. 在这里查看类似问题的答案,其中一个人正在谈论三次样条。 There is a short algorithm showing how to calculate input parameters for
Path.cubicTo()
method. 有一个简短的算法显示如何计算
Path.cubicTo()
方法的输入参数。 You can play with divider values to achieve required smoothness. 您可以使用分隔值来实现所需的平滑度。 For example, in the picture below I divided by 5 instead of 3. Hope this helps.
例如,在下面的图片中,我除以5而不是3.希望这会有所帮助。
I have spent some time and implemented a SplineLineAndPointFormatter
class, which does the stuff you need in androidplot library. 我花了一些时间并实现了一个
SplineLineAndPointFormatter
类,它可以在androidplot库中完成你需要的东西。 It uses same technics. 它使用相同的技术。 Here is how androidplot example applications looks like.
以下是androidplot示例应用程序的外观。 You just need to use it instead of
LineAndPointFormatter
. 您只需要使用它而不是
LineAndPointFormatter
。
Here is code example and the class I wrote. 这是代码示例和我写的类。
f1 = new SplineLineAndPointFormatter(color.getColor(), null,
Color.argb(60, color.getRed(), color.getGreen(), color.getBlue()), null);
plot.addSeries(series1, f1);
Here is the class doing the magic. 这是上课的魔法。 It is based on version 0.6.1 of androidplot library.
它基于androidplot库的0.6.1版本。
package com.androidplot.xy;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import com.androidplot.ui.SeriesRenderer;
import com.androidplot.util.ValPixConverter;
public class SplineLineAndPointFormatter extends LineAndPointFormatter {
public SplineLineAndPointFormatter() { }
public SplineLineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor) {
super(lineColor, vertexColor, fillColor, null);
}
public SplineLineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor, FillDirection fillDir) {
super(lineColor, vertexColor, fillColor, null, fillDir);
}
@Override
public Class<? extends SeriesRenderer> getRendererClass() {
return SplineLineAndPointRenderer.class;
}
@Override
public SeriesRenderer getRendererInstance(XYPlot plot) {
return new SplineLineAndPointRenderer(plot);
}
public static class SplineLineAndPointRenderer extends LineAndPointRenderer<BezierLineAndPointFormatter> {
static class Point {
public float x, y, dx, dy;
public Point(PointF pf) { x = pf.x; y = pf.y; }
}
private Point prev, point, next;
private int pointsCounter;
public SplineLineAndPointRenderer(XYPlot plot) {
super(plot);
}
@Override
protected void appendToPath(Path path, final PointF thisPoint, PointF lastPoint) {
pointsCounter--;
if (point == null) {
point = new Point(thisPoint);
point.dx = ((point.x - prev.x) / 5);
point.dy = ((point.y - prev.y) / 5);
return;
} else if (next == null) {
next = new Point(thisPoint);
} else {
prev = point;
point = next;
next = new Point(thisPoint);
}
point.dx = ((next.x - prev.x) / 5);
point.dy = ((next.y - prev.y) / 5);
path.cubicTo(prev.x + prev.dx, prev.y + prev.dy, point.x - point.dx, point.y - point.dy, point.x, point.y);
if (pointsCounter == 1) { // last point
next.dx = ((next.x - point.x) / 5);
next.dy = ((next.y - point.y) / 5);
path.cubicTo(point.x + point.dx, point.y + point.dy, next.x - next.dx, next.y - next.dy, next.x, next.y);
}
}
@Override
protected void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, LineAndPointFormatter formatter) {
Number y = series.getY(0);
Number x = series.getX(0);
if (x == null || y == null) throw new IllegalArgumentException("no null values in xyseries permitted");
XYPlot p = getPlot();
PointF thisPoint = ValPixConverter.valToPix(x, y, plotArea,
p.getCalculatedMinX(), p.getCalculatedMaxX(), p.getCalculatedMinY(), p.getCalculatedMaxY());
prev = new Point(thisPoint);
point = next = null;
pointsCounter = series.size();
super.drawSeries(canvas, plotArea, series, formatter);
}
}
}
1- I guess that you only use a few points to draw graphs of signals. 1-我猜你只用几个点来绘制信号图。 All graph/chart applications try to connect points with direct lines and then your chart will be shown.
所有图形/图表应用程序都尝试使用直线连接点,然后会显示您的图表。 So if you only use three points, your graph will looks like a triangle!
因此,如果您只使用三个点,您的图形将看起来像一个三角形! If you want your graph to be curved, you have to add more points.
如果您希望曲线图是弯曲的,则必须添加更多点。 Then it comes out like a curve.
然后它就像一条曲线。
2- Or you can find any library that can draw sin
graph, for example GraphView Library
. 2-或者你可以找到任何可以绘制
sin
图的库,例如GraphView Library
。 Then try to draw this function: 然后尝试绘制此函数:
So it looks like to this: 所以它看起来像这样:
Then translate it to (a,0)
, so result seems like what you want. 然后将其翻译为
(a,0)
,因此结果看起来像你想要的。
3- And another way, you can use built in Math.sin
in Java: 3-另一种方式,你可以在Java中使用内置的
Math.sin
:
Chose for example 1000 point in range a
to b
and compute value of above function for each point and finally create a path
and show them in a canvas. 选择例如范围
a
到b
1000点并计算每个点的上述函数的值,最后创建path
并在画布中显示它们。
You can use quadTo (float x1, float y1, float x2, float y2) that simplify drawing quad curves
for you. 您可以使用quadTo(float x1,float y1,float x2,float y2)来简化绘制
quad curves
。 The documentation says: 文件说:
Add a quadratic bezier from the last point, approaching control point (x1,y1), and ending at (x2,y2).
从最后一个点添加二次贝塞尔曲线,接近控制点(x1,y1),并以(x2,y2)结束。 If no moveTo() call has been made for this contour, the first point is automatically set to (0,0).
如果没有对此轮廓进行moveTo()调用,则第一个点自动设置为(0,0)。
Parameters
参数
x1 The x-coordinate of the control point on a quadratic curvex1二次曲线上控制点的x坐标
y1 The y-coordinate of the control point on a quadratic curvey1二次曲线上控制点的y坐标
x2 The x-coordinate of the end point on a quadratic curvex2二次曲线上终点的x坐标
y2 The y-coordinate of the end point on a quadratic curvey2二次曲线上终点的y坐标
Finally, I add a simple class that extends View
and can draw a curve that looks like what you want: 最后,我添加了一个扩展
View
的简单类,可以绘制一个看起来像你想要的曲线:
public class SinWave extends View {
private float first_X = 50;
private float first_Y = 230;
private float end_X = 100;
private float end_Y = 230;
private float Max = 50;
public SinWave(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint() {
{
setStyle(Paint.Style.STROKE);
setStrokeCap(Paint.Cap.ROUND);
setStrokeWidth(0.7f);
setAntiAlias(true);
setColor(0xFFFF00FF);
}
};
final Path path = new Path();
path.moveTo(first_X, first_Y);
path.quadTo((first_X + end_X)/2, Max, end_X, end_Y);
canvas.drawPath(path, paint);
}
}
The result must look like this: 结果必须如下所示:
You can add more methods to the class and change it to increase performance! 您可以向该类添加更多方法并进行更改以提高性能!
There's always been a smooth line renderer in Androidplot: BezierLineAndPointRenderer, which like the implementations above uses Android's built in Bezier drawing routines cubicTo(...) & quadTo(...) . 在Androidplot中总是有一个平滑的线条渲染器:BezierLineAndPointRenderer,它与上面的实现类似,使用Android内置的Bezier绘图例程cubicTo(...)和quadTo(...) 。 The problem is that using Beziers to draw smooth lines in this way creates a false line that overshoots the actual control points by varying amounts, which you can see happening if you look closely at the image above.
问题是使用贝塞尔曲线以这种方式绘制平滑线会产生一个假线,通过改变量来超过实际控制点,如果仔细观察上面的图像,就会发现这种情况。
The solution is to use the Catmull-Rom spline interpolation, which is now finally supported by Androidplot. 解决方案是使用Catmull-Rom样条插值,现在最终由Androidplot支持。 Details here: http://androidplot.com/smooth-curves-and-androidplot/
详情请访问: http : //androidplot.com/smooth-curves-and-androidplot/
使用ChartFactory.getCubeLineChartView代替ChartFactory.getLineChartView使用achart引擎
In some simple cases, this could help:在一些简单的情况下,这可能会有所帮助:
mPaint.pathEffect = CornerPathEffect(radius)
even in combination with即使结合
path.lineTo(x,y)
try this: 尝试这个:
symbol = new Path(); paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(2); paint.setColor(-7829368); paint.setStrokeJoin(Paint.Join.ROUND); // set the join to round you want paint.setStrokeCap(Paint.Cap.ROUND); // set the paint cap to round too paint.setPathEffect(new CornerPathEffect(10) ); paint.setStyle(Paint.Style.STROKE); symbol.moveTo(50.0F, 230.0F); symbol.lineTo(75.0F, 100.0F); symbol.lineTo(100.0F, 230.0F);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.