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

Android:无法清除对SurfaceView的初始绘制,android,graphics,surfaceview,Android,Graphics,Surfaceview,我刚刚开始开发Android应用程序,遇到了一些麻烦 这个测试应用程序的目的是在屏幕上画一个正方形,它朝屏幕右下角移动。就这么简单 MainActivityclass(当前入口点)看起来是这样的: Main Activity class (current entry point) looks like so: public class MainActivity extends Activity { Canvas canvas; GameLoopThread ga

我刚刚开始开发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
    }
}
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;
}
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) {
        }
    }
}
GameLoopThread
看起来是这样的:

    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
    }
}
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;
}
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) {
        }
    }
}
}

最后,
MySurfaceView
看起来是这样的:

    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
    }
}
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;
}
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”视图。它仍然覆盖在以后绘制的任何内容之上。见下文:

我无法理解为什么会发生这种情况。再多的清理也不能解决问题。如果我停止绘制“新”方块,“卡住”方块仍然存在。你可以看到我正在改变“新”方块的颜色,以测试它是否改变了“卡住”方块,这表明它正在被重新绘制。显然不是

对于大约4个循环不绘制任何图形,每次绘制之间有30毫秒的停顿,不会导致“卡死”正方形。在最初的4个之后开始绘制,结果是一个正方形在屏幕上移动

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

其他信息

这是在三星Galaxy SIII Mini上运行的


SDK verson 4.0.3

曲面视图包含两部分,曲面和视图。当您使用
lockCanvas()
获得画布时,您将获得曲面零件的画布。曲面是一个独立的层,与用于所有视图元素的层无关

您已经将SurfaceView子类化,并提供了一个
onDraw()
函数,视图层次结构使用该函数渲染到视图部分。我的猜测是视图层次结构得到一个invalidate,并决定在SurfaceView的视图部分绘制。在前几个循环迭代中跳过渲染的实验是有效的,因为您正在跳过在“失效”上发生的渲染。因为表面层是在视图层后面绘制的,所以您可以在正在渲染的其他对象上看到
onDraw()
-渲染的正方形

通常情况下,您不会在视图上绘制;它只是一个透明的占位符,布局代码使用它在视图层中留下一个“洞”,曲面将在其中显示

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

当然,如果您想要有一个遮罩层,也许是为了添加圆角或“污垢”效果,在视图上绘制是一种简单的方法


有关完整信息,请参见。

SurfaceView
需要子类化以提供异步绘图,因此可以将其设置为设置为主要活动的
ContentView
。无论如何,您的解决方案确实有效。我觉得很奇怪,这种改变是必要的;我看过的每个教程都使用了
onDraw()
。这些教程的结构不正确。他们中的大多数人也不必要地锁上了路面。参见Grafika(),了解SurfaceView的几种用法,这些用法不涉及创建子类;是一个非常简单的例子。实际上,“多表面测试”(multi-surface test)可能是一个更好的例子。它是从一个专用线程渲染的软件,具有多个重叠的SurfaceView。@faddem啊,是的,我明白了。您完全正确,我不需要子类
SurfaceView
。顺便说一句,这是一个很好的例子。