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

标签 android multithreading user-interface surfaceview

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

问题在于自定义SurfaceView将不会保存UI更新之间的更改。

定制SurfaceView类是在使用该类的 Activity 中定义的,而运行定期检查更新的线程(我们将其称为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()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()调用中的所有更改。我目前正在使用OnTouchListener对此进行测试。

据我了解的代码过程:
  • Touch将用户界面的更改请求添加到ExternalClass中的列表中。
  • DrawingThread看到列表不再为空,安全地抓取了Canvas,并使用postInvalidates()调用自定义SurfaceViewonDraw()
  • onDraw()更新UI并从ExternalClass中删除更改请求。
  • DrawingThread发布Canvas,然后返回等待状态。

  • 我的预期行为是,我将在连续触摸时在屏幕上积聚点。取而代之的是,每次触摸都会清除屏幕,只剩下背景和我最后一次触摸屏幕时的一个点。

    我发现这非常令人困惑,因为lockCanvas(Rect)专门提到它将通过指定“脏”部分来保存锁之间的更改。 Google搜索使我相信大多数用户会发现相反的问题:他们希望重绘View,但必须手动进行每次更新。

    为什么尽管我指定要重绘的“脏”部分,但该程序为什么在每个UI更新时都从头开始绘制干净的SurfaceView

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

    已经在这里和Google搜索了几个小时。希望我没有错过任何显而易见的事情。

    最佳答案

    我设法通过将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);进行绘图,我将在另一个问题中提出这个问题。

    关于android - 如何使用单独的线程部分重绘自定义SurfaceView,而又不会丢失以前的编辑?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15378493/

    相关文章:

    android - 安卓人脸识别?

    java - 在 Java 中创建一个 Atomic RingBuffer

    java - Condition 接口(interface)中的 signalAll 与对象中的 notifyAll

    javascript - 下拉列表的第一项未定义

    Android 底部工具栏像折断

    android - 将图像更改为圆角

    Android Volley Post 请求未发送到服务器且未刷新

    android - 自定义布局 Android 中的图标大小

    c++ - (void (*)(void *))的含义

    user-interface - GUI 输出如何从应用程序到硬件级别工作?