Android 清除SurfaceView画布后如何避免重影绘制

Android 清除SurfaceView画布后如何避免重影绘制,android,android-canvas,surfaceview,doublebuffered,Android,Android Canvas,Surfaceview,Doublebuffered,嗨,我正在使用SurfaceView绘制输入信号的实时图形。 采样率为128Hz,目标图形刷新率为50Zh 事情进展得很顺利,点是实时绘制的 我使用Path将数据绘制为几个点的分段 对于每个段,我调用path.computeBounds以获得一个rect,我将使用它调用holder.lockCanvasrect并绘制路径。使用rect可防止闪烁并减少cpu使用 当图形到达终点时,我锁定整个画布并清除背景,绘制图形框架,然后继续绘图 问题在于,在每个新页面的开头,我都会从最后一页得到一个重影图像:

嗨,我正在使用SurfaceView绘制输入信号的实时图形。 采样率为128Hz,目标图形刷新率为50Zh

事情进展得很顺利,点是实时绘制的

我使用Path将数据绘制为几个点的分段 对于每个段,我调用path.computeBounds以获得一个rect,我将使用它调用holder.lockCanvasrect并绘制路径。使用rect可防止闪烁并减少cpu使用

当图形到达终点时,我锁定整个画布并清除背景,绘制图形框架,然后继续绘图

问题在于,在每个新页面的开头,我都会从最后一页得到一个重影图像:

我相信这是由于打印时双缓冲/使用脏区域造成的

我一直在寻找这个问题的解决方案,但似乎没有一个适合这种类型的应用。欢迎任何帮助

谢谢 让·皮埃尔

代码如下:

    private void draw() {
    Point point = null;
    Canvas canvas = null;
    Path path = new Path();
    ArrayList<Point> pointArray;
    float oldX = -1;
    boolean setToClear = false;
    boolean isNewSegment = false;

    if (samplesInQueue == 0) {
        return;
    }

    pointArray = new ArrayList<Point>((int) samplesInQueue);

    for (int i = 0; i < samplesInQueue; i++) {
        // take a peek at the point without retrieving it from the point
        // queue
        point = Points.peek();
        // check if first point of segment is the start of a page
        if (i == 0) {
            if (lastSegmentEndPoint != null) {
                if (point.x < lastSegmentEndPoint.x) {
                    // yes then we will need to clear the screen now
                    isNewSegment = true;
                }
            } else {
                // yes then we will need to clear the screen now
                isNewSegment = true;
            }
        }

        if (point != null) {
            if (point.x > oldX) {
                // put consecutive points in the path point array
                point = Points.poll();
                samplesInQueue--;
                pointArray.add(point);
                oldX = point.x;
            } else {
                // we have a wrap around, stop and indicate we need to clear
                // the screen on the next pass
                if (!isNewSegment) {
                    setToClear = true;
                }
                break;
            }
        }
    }

    // no points, return
    if (pointArray.size() == 0) {
        return;
    }

    // fill the path
    for (int i = 0; i < pointArray.size(); i++) {
        Point p = pointArray.get(i);

        if (i == 0) {
            if (lastSegmentEndPoint != null) {
                if (p.x >= lastSegmentEndPoint.x) {
                    // if we have the end of the last segment, move to it
                    // and line to the new point
                    path.moveTo(lastSegmentEndPoint.x, lastSegmentEndPoint.y);
                    path.lineTo(p.x, p.y);
                } else {
                    // otherwise just line to the new point
                    path.moveTo(p.x, p.y);
                }
            } else {
                path.moveTo(p.x, p.y);
            }
        } else {
            path.lineTo(p.x, p.y);
        }
    }

    if (clear || isNewSegment) {
        if (clear) {
            clear = false;
        }
        // we need to clear, lock the whole canvas
        canvas = holder.lockCanvas();
        // draw the graph frame / scales
        drawGraphFrame = true;
        drawGraphFrame(canvas);
    } else {
        // just draw the path
        RectF bounds = new RectF();
        Rect dirty = new Rect();
        // calculate path bounds
        path.computeBounds(bounds, true);

        int extra = 0;
        dirty.left = (int) java.lang.Math.floor(bounds.left - extra);
        dirty.top = (int) java.lang.Math.floor(bounds.top - extra);
        dirty.right = (int) java.lang.Math.round(bounds.right + 0.5);
        dirty.bottom = (int) java.lang.Math.round(bounds.bottom + 0.5);

        // just lock what is needed to plot the path
        canvas = holder.lockCanvas(dirty);
    }

    // draw the path
    canvas.drawPath(path, linePaint);

    // unlock the canvas
    holder.unlockCanvasAndPost(canvas);

    // remember last segment end point
    lastSegmentEndPoint = pointArray.get(pointArray.size() - 1);

    // set clear flag for next pass
    if (setToClear) {
        clear = true;
    }
}

你的问题很直接。。您只能锁定新路径覆盖的画布的新部分。所以最好的办法是让你的路径和脏rect的私有成员成为你的类。然后在draw方法开始时,获取路径的当前边界,即脏rect中的旧边界。现在调用path.rewind;并开始修改您的路径。之后,使用新边界在脏矩形上进行并集。现在你的脏直肠覆盖了新旧直肠。因此,您的清除将删除旧路径。这也减少了开销,因为您不希望为rect和path每秒分配100多个对象。现在,由于您绘制的是示波器,因此您可能希望将旧边界调整为仅为视图宽度的一部分。与你的新部分所涵盖的金额相同


希望这些都能解决问题。

我的简单回答是,只要你想在任何地方清理画布,就可以使用这个函数clear\u holder。我复制并粘贴了3行3次,因为它需要3次清除,以保留空白。 在结算持有人后,你应该画任何你想要的新东西! 这给我这个源代码

private void clear_holder(SurfaceHolder holder){
        Canvas c = holder.lockCanvas();
        c.drawColor( 0, PorterDuff.Mode.CLEAR );
        holder.unlockCanvasAndPost(c);
        c = holder.lockCanvas();
        c.drawColor( 0, PorterDuff.Mode.CLEAR );
        holder.unlockCanvasAndPost(c);
        c = holder.lockCanvas();
        c.drawColor( 0, PorterDuff.Mode.CLEAR );
        holder.unlockCanvasAndPost(c);
    }

看起来您正在清理画布,因此,这不是双缓冲问题。我认为这与你的路径被重复使用有关

尝试在开始新页面时添加下一行

path.reset();

谢谢你,西蒙。在这种情况下,这实际上不起作用。由于保留了旧点,因此回放会导致路径增长。这会导致性能在几百点后快速下降。这就是为什么我一次只画几个点。另外,在您最后的声明示波器中,我相信这就是我的代码中要完成的,rect只覆盖了所需的区域。也许我不太明白你的意思。
path.reset();