如何使用单独的线程部分重绘自定义SurfaceView而不丢失以前的编辑?

时间:2013-03-13 06:31:08

标签: android multithreading user-interface surfaceview

我目前无法从不在主UI上的线程中绘制到自定义SurfaceView。此SurfaceView占据整个屏幕(全屏幕上的Galaxy S3),必须从多个来源更新。

问题是自定义SurfaceView不会保存对UI的更新之间的更改。

自定义SurfaceView类在使用它的活动中定义,并且在自定义DrawingThread类中定义运行定期更新检查的线程(让我们称之为SurfaceView) 。我这样做与this tutorial类似。

另一个外部线程正在更新必须对UI进行的更改列表。 DrawingThread每50ms左右检查一次这个列表,如果列表不为空,则计算一个必须重绘的矩形。

// Thread waits for change request to UI or kill order
// Build Rect at point of Touch
try {
    Canvas = _surfaceHolder.lockCanvas(Rect); 
    synchronized (_surfaceHolder) {
        postInvalidate(); //Calls custom SurfaceView's onDraw() with the correct Canvas
    }
} finally {
    if (Canvas != null) {
        _surfaceHolder.unlockCanvasAndPost(Canvas);
    }
}
// Loop

自定义onDraw()的{​​{1}}方法如下:

SurfaceView

除了忘记之前任何@Override public void onDraw(Canvas canvas) { // Initialize vars for rectangle Paint.setStyle(Style.FILL); for (MapChanges change : ExternalClass.getMapChanges()) { // Build Rect at point of Touch // Select Color for Paint canvas.drawRect(Rect, Paint); ExternalClass.removeMapChange(change); //Thread safe } } 电话的更改这一事实外,这项工作完美无瑕。我目前正在使用onDraw()进行测试。

我理解的代码程序:

  • Touch会将UI的更改请求添加到OnTouchListener
  • 中的列表中
  • ExternalClass看到列表不再为空,安全地抓取画布,DrawingThread来调用自定义postInvalidates()的{​​{1}}。
  • SurfaceView更新了用户界面,并从onDraw()
  • 中删除了更改请求
  • onDraw()发布ExternalClass并返回等待。

我的预期行为是,我将通过连续触摸在屏幕上积累点。相反,每次触摸都会清除屏幕,让我留下背景和我最后一次触摸屏幕的一个点。

我发现这非常令人困惑,因为DrawingThread特别提到它会通过指定“脏”部分来保存锁之间的更改。谷歌搜索让我相信大多数用户都会发现相反的问题:他们希望重新绘制Canvas但必须手动更新每个用户。

为什么程序在每次UI更新时从头开始绘制一个干净的lockCanvas(Rect),尽管我指定了一个“脏”部分来重绘?

毫无疑问,有人会将我引用到this previously answered question,程序员被建议使用位图。我试图避免这种情况,因为我对此有多新,并且我不想使用大量内存(更新UI只是我代码的一小部分)。直接在View上使用简单形状对我来说非常理想。此外,它是一种解决方法。这是Android API中的缺陷吗?

现在已经在这里和谷歌搜索了好几个小时了。希望我没有错过任何明显的明显。

1 个答案:

答案 0 :(得分:0)

我设法找到了一个解决方法,将OnDraw()排除在外。下面的新DrawingPanel

class DrawingPanel extends SurfaceView implements Runnable, SurfaceHolder.Callback {

    Thread thread = null;
    SurfaceHolder surfaceHolder;
    volatile boolean running = false;

    private Paint myPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Random random;

    public DrawingPanel(Context context) {
        super(context);
        getHolder().addCallback(this);

        surfaceHolder = getHolder();
    }

    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) { 


    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {          
        setWillNotDraw(false); //Allows us to use invalidate() to call onDraw()

        if (!running) {
            running = true;
            thread = new Thread(this);
            thread.start();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        try {
            running = false;    //Tells thread to stop
            thread.join();      //Removes thread from mem.
        } catch (InterruptedException e) {}
    }   

    @Override
    public void run() {
        Canvas canvas;
        Rect myRect = new Rect();
        boolean firstRun = true;

        ChangeRequest ChangeReq;

        myPaint.setStyle(Style.FILL);

        while(running) {
            try {
                Thread.sleep(50);
            } catch (Exception e) {}

            canvas = null;

            ChangeReq = getUIChangeRequests();

            if (!ChangeReq.isEmpty() || (firstRun)) {                   
                if(surfaceHolder.getSurface().isValid()) {
                    if (!firstRun) {  

                        // Calculate dirty space for editing
                        x = ChangeReq.getX();
                        y = ChangeReq.getY();

                        try {
                            myRect.set(x*RectSize, y*RectSize, x*RectSize + RectSize, y*RectSize + RectSize);
                            myPaint.setColor(Color.RED);

                            canvas = surfaceHolder.lockCanvas(myRect);

                            synchronized (surfaceHolder) {
                                // Draw
                                canvas.drawRect(myRect, myPaint);

                            }

                            // Remove ChangeRequest
                            ChangeReq.remove();

                        } finally {
                            if (canvas != null) {
                                surfaceHolder.unlockCanvasAndPost(canvas);
                            }
                        }
                    } else {
                        try {
                            // Initialize canvas as all one color
                            myRect.set(0, 0, getWidth(), getHeight());
                            myPaint.setColor(Color.DKGRAY);

                            canvas = surfaceHolder.lockCanvas();

                            synchronized (surfaceHolder) {
                                //Draw
                                canvas.drawRect(myRect, myPaint); 
                            }

                            firstRun = false;
                        } finally {
                            if (canvas != null) {
                                surfaceHolder.unlockCanvasAndPost(canvas);
                            }
                        }
                    }
                }
            }
        }
    }   
}

这解决了我的绘图问题,但提出了一个有趣的问题,为什么我必须使用surfaceHolder.lockCanvas();进行初始化,然后才能使用surfaceHolder.lockCanvas(Rect);进行绘制。我将在另一个问题中提出这个问题。