Juraj Carnogursky
Juraj Carnogursky

Reputation: 355

Android Canvas SurfaceView black screen

I have a following class:

public class GameView extends SurfaceView implements SurfaceHolder.Callback {

public static final int WIDTH = 860;
public static final int HEIGHT = 480;

int x = 0;

GameLoop gameLoop;

public GameView(Context context) {
    super(context);
    getHolder().addCallback(this);
    setFocusable(true);
}

public GameView(Context context, AttributeSet attrs) {
    super(context, attrs);
    getHolder().addCallback(this);
    setFocusable(true);
}

public GameView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    getHolder().addCallback(this);
    setFocusable(true);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    //setWillNotDraw(false);

    gameLoop = new GameLoop(this);

    gameLoop.start();
}

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

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if(canvas != null) {
        Paint paint = new Paint();
        paint.setColor(Color.WHITE);
        canvas.drawRect(x, 0, 200, 200, paint);
        canvas.drawRect(200, 200, 400, 400, paint);
        Log.v("TEST", "Drawing into canvas");
        Log.v("TEST", "x := " + x);
    }
}

public void update() {
    x += 5;
}

and a game loop which looks like this:

public class GameLoop extends Thread {
public static final int REFRESH_TIME = 2;
private static final int LOG_FPS_AFTER_FRAMES = 30;

public static Canvas canvas;

private double averageFPS;

private GameView gameView;

private boolean running;

public GameLoop(GameView gamePanel) {
    this.gameView = gamePanel;
}

@Override
public void run() {
    super.run();

    running = true;

    long startTime;
    long timeMillis;
    long waitTime;
    long totalTime = 0;
    int frameCount = 0;
    long targetTime = 1000*REFRESH_TIME;

    while(running) {
        startTime = System.nanoTime();
        canvas = null;

        try {
            canvas = gameView.getHolder().lockCanvas();

            synchronized (gameView.getHolder()) {
                gameView.update();
                gameView.draw(canvas);
            }


        } catch(Exception e) {}
        finally {
            if(canvas != null) {
                try {
                    gameView.getHolder().unlockCanvasAndPost(canvas);
                } catch(Exception e) {}
            }
        }


        timeMillis = (System.nanoTime() - startTime) / 1000000;
        waitTime = targetTime - timeMillis;

        try {
            this.sleep(waitTime);
        } catch(Exception e) {}

        totalTime += System.nanoTime() - startTime;
        frameCount++;

        if(frameCount == LOG_FPS_AFTER_FRAMES) {
            averageFPS = 1000/((totalTime/frameCount)/1000000);
            frameCount = 0;
            totalTime = 0;
            Log.v("FPS", Double.toString(averageFPS));
        }

    }

}

@Override
public void interrupt() {
    super.interrupt();

    running = false;
}

public boolean isRunning() {
    return running;
}

public void setRunning(boolean value) {
    this.running = value;
}

}

OnDraw method is being called every two seconds but the screen remains black. Any ideas what could be the problem? (by the way if I uncomment line setWillNotDraw(false) the rectangles are drawn once but not moving)...

Upvotes: 0

Views: 3665

Answers (2)

Juraj Carnogursky
Juraj Carnogursky

Reputation: 355

I just changed overriden function onDraw(Canvas canvas) to draw(Canvas canvas) and it works!

Upvotes: 3

fadden
fadden

Reputation: 52303

You have the usual problem: you're overriding onDraw(), which draws on the View part. So you have a very expensive custom View that doesn't work right. If you let the View draw, it'll draw once, but you're not calling invalidate() so it never happens again. My guess is you set a background color in your layout, making the View opaque, which is why you can't see what's being drawn on the Surface.

For Canvas rendering, it's often better to use a custom View, rather than a SurfaceView. I would recommend that approach.

If you really want to use a SurfaceView, change onDraw() to something else (like doDraw()), and call it from your render thread. Since you'll be drawing on the Surface part, rather than the View part, you don't need to subclass SurfaceView (and shouldn't, as making it an independent class will avoid common pitfalls, such as one you fell in). Make sure you understand how SurfaceView and Activity interact, as there are various ways to get into trouble.

You should also read up on the recommended way to structure a game loop.

Upvotes: 7

Related Questions