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

时间:2014-05-14 19:36:49

标签: android android-canvas surfaceview doublebuffered

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

事情进展顺利,点数是实时正确绘制的。

我使用Path()在几个点的段中绘制数据 对于每个段,我调用path.computeBounds()来获取一个rect,我将用它来调用holder.lockCanvas(rect)并绘制路径。使用rect可防止闪烁并降低CPU使用率

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

问题是,在每个新“页面”的开头,我从最后一页得到一张鬼影:

ghost image

我认为这是由于在绘图时双重缓冲/使用脏区造成的。

我已经找到了这个问题的解决方案,但似乎没有适合这种类型的应用程序。我们非常欢迎任何帮助。

由于 让 - 皮埃尔

代码如下:

    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;
    }
}

绘制框架/清除图形代码

    private void drawGraphFrame(Canvas canvas) {

    if (!drawGraphFrame) {
        return;
    }

    if (canvas == null) {
        Log.e(TAG, "trying to draw on a null canvas");
        return;
    }

    drawGraphFrame = false;

    // clear the graph
    canvas.drawColor(Color.BLACK, Mode.CLEAR);

    // draw the graph frame
    canvas.drawLine(leftMargin, topMargin, leftMargin, mCanvasHeight - bottomMargin, framePaint);
    canvas.drawLine(leftMargin, mCanvasHeight - bottomMargin, mCanvasWidth - rightMargin, mCanvasHeight
            - bottomMargin, framePaint);

    // more drawing
}

3 个答案:

答案 0 :(得分:0)

您的问题非常简单..您只能锁定新路径所涵盖的画布的新部分。因此,最好的办法是让你的路径和脏矩形的私人成员。然后在draw方法的开始处获取脏rect中路径的当前边界(旧边界)。现在调用path.rewind();并开始修改你的路径。在使用新边界对脏矩形进行联合之后。现在你的脏矩形覆盖旧的和新的矩形。所以你的明确将删除旧的道路。这也减少了开销,因为您不希望每秒为rect和路径分配100多个对象。现在,自绘制示波器以来,您可能希望将旧边界调整为仅视图宽度的一部分。新部分的金额相同。

希望能够解决问题。

答案 1 :(得分:0)

我的简单回答就是在你想要清除画布的地方使用这个函数clear_holder()。我复制并粘贴3行3次,因为它需要3次清除才能使支架留空。

清理持有人后,你应该画出你想要的任何新东西!

这个link给了我这个源代码!

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

答案 2 :(得分:0)

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

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

path.reset();