Как сделать линию с закругленными (гладкими) углами с 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);

но это не сработало, как ожидалось.

Enter image description here

Примечание:Анализатор Wifi приложение имеет аналогичный график, и его график имеет закругленные углы, которые я хочу. Выглядит это так:

Enter image description here

5 ответов


можно использовать путь.cubicTo() метод. Он рисует линию, используя алгоритм кубического сплайна, который приводит к сглаживающему эффекту, который вы хотите.

проверка ответа на подобный вопрос здесь, где парень говорит о кубических сплайнах. Существует короткий алгоритм, показывающий, как вычислить входные параметры для Path.cubicTo() метод. Вы можете играть со значениями делителя для достижения требуемой гладкости. Например, на рисунке ниже я разделен на 5 вместо 3. Надеюсь, это поможет.

Example of a polylyne drawn using Path.cubicTo() method

Я потратил некоторое время и реализовал SplineLineAndPointFormatter класс, который делает то, что нужно в библиотеке androidplot. Он использует такую же технику. Вот как выглядит пример приложений androidplot. Вам просто нужно использовать его вместо LineAndPointFormatter.

Androidplot example with SplineLineAndPointFormatter

вот пример кода и класс я написал.

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. Затем попробуйте нарисовать эту функцию:

Enter image description here

так это выглядит так:

Enter image description here

тогда переведите его на (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);
    }
}

результат должен выглядеть как это:

Enter image description here

вы можете добавить больше методов в класс и изменить его, чтобы увеличить производительность!


в 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);

большая часть информации нашел здесь