我目前无法从不在主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
对此进行测试。据我了解的代码过程:
ExternalClass
中的列表中。 DrawingThread
看到列表不再为空,安全地抓取了Canvas,并使用postInvalidates()
调用自定义SurfaceView
的onDraw()
。 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/