Android: 无法清除 SurfaceView 的初始绘制

Android: Unable to clear initial draw to SurfaceView

我刚开始尝试开发 Android 应用程序,运行 遇到了一些麻烦。

此测试应用的目的是在屏幕上绘制一个向屏幕右下角移动的正方形。就这么简单。

MainActivity class(当前入口点)看起来像这样:

    Main Activity class (current entry point) looks like so:

    public class MainActivity extends Activity {
    Canvas canvas;
    GameLoopThread gameThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); //Super constructor
        gameThread=new GameLoopThread(); //Create GameLoop instance
        //Create mySurfaceView instance and pass it the new gameloop
        MySurfaceView sView=new MySurfaceView(this,gameThread);
        //Without this only the bit I cant remove is drawn
        sView.setBackgroundColor(Color.TRANSPARENT);
        setContentView(sView); //Set the current ContentView to the one we created
        //Pass the GameThread the MySurfaceView to repeatedly update
        gameThread.setSurfaceView(sView); 
        gameThread.start(); //Start the thread
    }
}

GameLoopThread 看起来像这样:

public class GameLoopThread extends Thread {
protected volatile boolean running;
private MySurfaceView view;

public GameLoopThread(){
}

public void setSurfaceView(MySurfaceView view){
    this.view=view;
}

@Override
public void run() {
    running=true;
    while(running){
        Canvas c = null;
        c = view.getHolder().lockCanvas(); //Get the canvas
        if (c!=null) {
            synchronized (view) {
                view.draw(c); //Run the doDraw method in our MySurfaceView
            }
        }
        try {
            sleep(30, 0); //Throttle
        }
        catch(InterruptedException e){
            e.printStackTrace();
        }
        if (c != null) {
            view.getHolder().unlockCanvasAndPost(c); //Lock and post canvas
        }
    }
}

public void terminate(){
    running=false;
}

}

最后 MySurfaceView 看起来像这样:

public class MySurfaceView extends SurfaceView {
    private Bitmap bmp;
    private SurfaceHolder holder;
    private final GameLoopThread gameLoop;
    Paint paint;
    float x=0;
    float y=0;

    public MySurfaceView(Context c, GameLoopThread gThread){
        super(c);
        holder=getHolder();
        gameLoop=gThread;
        paint=new Paint();
        paint.setColor(Color.CYAN);
        paint.setStrokeWidth(10);
        holder.addCallback(new CallBack());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        paint.setColor(paint.getColor() - 1);
        canvas.drawRect(x, y, x + 50, y + 50, paint);
        x++;
        y++;
    }

    private class CallBack implements SurfaceHolder.Callback{
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

            gameLoop.terminate();
            while (true) {
                try {
                    gameLoop.join();
                    break;
                } catch (InterruptedException e) {
                }
            }
        }
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        }

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

问题 这一切都有效,除了最初的几个视图之一 "sticks"。它仍然在以后绘制的任何内容之上。见下文:

我不明白为什么会这样。再多的清理也无法解决问题。如果我停止绘制 'new' 方块,'stuck' 方块仍然存在。你可以看到我正在改变 'new' 方块的颜色来测试它是否改变了 'stuck' 方块,这表明它正在重绘。显然不是。

在每次绘制之间有 30 毫秒的停顿,在大约 4 个循环中不绘制任何内容导致没有 'stuck' 方块。在最初的 4 个之后开始绘图会导致一个正方形在屏幕上按预期移动。

改变暂停时间会改变必须等待的循环次数,但这种关系似乎并不成比例。

其他信息

这是 运行 三星 Galaxy SIII Mini

SDK 版本 4.0.3

一个 SurfaceView 有两个部分,一个 Surface 和一个 View。当您使用 lockCanvas() 获得 Canvas 时,您将获得 Surface 部分的 Canvas。 Surface 是一个单独的层,独立于用于所有 View 元素的层。

您已将 SurfaceView 子类化并提供了一个 onDraw() 函数,View 层次结构使用该函数呈现到 View 部分。我的猜测是 View 层次结构失效并决定在 SurfaceView 的 View 部分上绘制。您在前几次循环迭代中跳过渲染的实验是有效的,因为您跳过了在无效时发生的渲染。因为 Surface 层绘制在 View 层后面,所以您会看到 onDraw() 渲染的正方形位于您正在渲染的其他内容之上。

通常你不会在视图上绘制;它只是一个透明的占位符,布局代码使用它在 Surface 显示的视图层中留下一个 "hole"。

onDraw() 重命名为 doDraw(),并删除 @override。我认为根本没有理由将 SurfaceView 子类化。这应该会阻止 SurfaceView 在其视图上绘制。

当然,如果你想要有一个遮罩层,也许是为了添加圆角或"dirt"效果,在视图上绘制是一个简单的方法完成它。

完整故事请参阅 Graphics Architecture doc