Как сделать линию с закругленными (гладкими) углами с AndroidPlot
у меня небольшая проблема с построением моего графика. На картинке ниже то, что я уже сделал.
график должен представлять фактическую силу сигнала доступных сетей Wi-Fi. Это просто XYPlot
здесь данные представлены с (значения).
вот небольшой фрагмент кода (только для примера):
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);
примером на рисунке является динамическое моделирование изменений БД. Все работает, я угадайте, правильно, но то, что я хочу достичь, - это иметь линию с "закругленными" углами (см. рисунок, чтобы увидеть, что я имею в виду).
Я уже пытался настроить LineFormatter:
f1.getFillPaint().setStrokeJoin(Join.ROUND);
f1.getFillPaint().setStrokeWidth(8);
но это не сработало, как ожидалось.
Примечание:Анализатор Wifi приложение имеет аналогичный график, и его график имеет закругленные углы, которые я хочу. Выглядит это так:
5 ответов
можно использовать путь.cubicTo() метод. Он рисует линию, используя алгоритм кубического сплайна, который приводит к сглаживающему эффекту, который вы хотите.
проверка ответа на подобный вопрос здесь, где парень говорит о кубических сплайнах. Существует короткий алгоритм, показывающий, как вычислить входные параметры для Path.cubicTo()
метод. Вы можете играть со значениями делителя для достижения требуемой гладкости. Например, на рисунке ниже я разделен на 5 вместо 3. Надеюсь, это поможет.
Я потратил некоторое время и реализовал SplineLineAndPointFormatter
класс, который делает то, что нужно в библиотеке androidplot. Он использует такую же технику. Вот как выглядит пример приложений androidplot. Вам просто нужно использовать его вместо LineAndPointFormatter
.
вот пример кода и класс я написал.
f1 = new SplineLineAndPointFormatter(color.getColor(), null,
Color.argb(60, color.getRed(), color.getGreen(), color.getBlue()), null);
plot.addSeries(series1, f1);
вот класс делает магию. Он основан на версии 0.6.1 на androidplot библиотека.
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- я думаю, что вы используете только несколько точек для рисования графиков сигналов. Все графические / графические приложения пытаются соединить точки с прямыми линиями, а затем ваша диаграмма будет показана. Поэтому, если вы используете только три точки, ваш график будет выглядеть как треугольник! Если вы хотите, чтобы ваш график был изогнут, вам нужно добавить больше точек. Затем он выходит, как кривая.
2- или вы можете найти любую библиотеку, которая может рисовать sin
графика, например GraphView Library
. Затем попробуйте нарисовать эту функцию:
так это выглядит так:
тогда переведите его на (a,0)
, поэтому результат кажется тем, что вы хотите.
3- и другой способ, вы можете использовать построенный в Math.sin
в Java:
выбрал например 1000 точек в диапазоне a
to b
и вычислить значение вышеуказанной функции для каждой точки и, наконец, создать path
и показать их на холсте.
можно использовать quadTo (поплавок x1, поплавок y1, поплавок x2, поплавок y2) это упрощает рисование quad curves
для вас. В документации говорится:
добавить квадратичный Безье из последней точки, приближаясь к контрольной точке (x1, y1) и заканчивается на (x2,y2). Если вызов moveTo () не был сделан для такого контуру, первая точка автоматически устанавливается в положение (0,0).
параметры
Х1 X-координата контрольной точки На квадратичной кривой
y1 y-координата контрольной точки На квадратичной кривой
x2 координата X конечной точки На квадратичной кривой
y2 y-координата конечной точки На квадратичной кривой
наконец, я добавляю простой класс, который расширяет 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);
}
}
результат должен выглядеть как это:
вы можете добавить больше методов в класс и изменить его, чтобы увеличить производительность!
в Androidplot всегда был рендерер гладкой линии: BezierLineAndPointRenderer, который, как и реализации выше, использует встроенные в Android процедуры рисования Безье cubicTo(...) & quadTo(...). Проблема в том, что использование Безье для рисования гладких линий таким образом создает ложную линию, которая перекрывает фактические контрольные точки на разные суммы, что вы можете увидеть, если внимательно посмотрите на изображение выше.
решение используйте интерполяцию сплайнов Catmull-Rom, которая теперь, наконец, поддерживается Androidplot. Подробности здесь: http://androidplot.com/smooth-curves-and-androidplot/
просто используйте ChartFactory.getCubeLineChartView вместо ChartFactory.getLineChartView с помощью achart engine
попробуйте это:
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);
большая часть информации нашел здесь