我目前无法从不在主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()
进行测试。
我理解的代码程序:
OnTouchListener
。ExternalClass
看到列表不再为空,安全地抓取画布,DrawingThread
来调用自定义postInvalidates()
的{{1}}。SurfaceView
更新了用户界面,并从onDraw()
。onDraw()
发布ExternalClass
并返回等待。我的预期行为是,我将通过连续触摸在屏幕上积累点。相反,每次触摸都会清除屏幕,让我留下背景和我最后一次触摸屏幕的一个点。
我发现这非常令人困惑,因为DrawingThread
特别提到它会通过指定“脏”部分来保存锁之间的更改。谷歌搜索让我相信大多数用户都会发现相反的问题:他们希望重新绘制Canvas
但必须手动更新每个用户。
为什么程序在每次UI更新时从头开始绘制一个干净的lockCanvas(Rect)
,尽管我指定了一个“脏”部分来重绘?
毫无疑问,有人会将我引用到this previously answered question,程序员被建议使用位图。我试图避免这种情况,因为我对此有多新,并且我不想使用大量内存(更新UI只是我代码的一小部分)。直接在View
上使用简单形状对我来说非常理想。此外,它是一种解决方法。这是Android API中的缺陷吗?
现在已经在这里和谷歌搜索了好几个小时了。希望我没有错过任何明显的明显。
答案 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);
进行绘制。我将在另一个问题中提出这个问题。