Android Canvas SurfaceView黑屏
Android Canvas SurfaceView black screen
我有一个关注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;
}
和一个看起来像这样的游戏循环:
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 方法每两秒调用一次,但屏幕仍然黑屏。任何想法可能是什么问题?
(顺便说一句,如果我取消注释 setWillNotDraw(false) 行,矩形只绘制一次但不移动)...
您遇到了常见问题:您重写了 onDraw()
,它利用了视图部分。所以你有一个非常昂贵的自定义视图,它不能正常工作。如果您让 View 绘制,它将绘制一次,但您没有调用 invalidate()
,因此它永远不会再发生。我的猜测是您在布局中设置了背景颜色,使 View 不透明,这就是为什么您看不到 Surface 上绘制的内容的原因。
对于 Canvas 渲染,通常使用 custom View 而不是 SurfaceView 更好。我会推荐这种方法。
如果您真的想使用 SurfaceView,请将 onDraw()
更改为其他内容(例如 doDraw()
),然后从渲染线程调用它。由于您将在 Surface 部分而不是 View 部分上绘图,因此您不需要 subclass SurfaceView(也不应该,因为将其设为独立的 class 将避免常见的陷阱,比如你掉进去的那个)。确保您了解如何 SurfaceView and Activity interact,因为惹麻烦的方式多种多样。
您还应该阅读构建 game loop 的推荐方法。
我刚刚将覆盖函数 onDraw(Canvas canvas) 更改为 draw(Canvas canvas) 并且它有效!
我有一个关注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;
}
和一个看起来像这样的游戏循环:
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 方法每两秒调用一次,但屏幕仍然黑屏。任何想法可能是什么问题? (顺便说一句,如果我取消注释 setWillNotDraw(false) 行,矩形只绘制一次但不移动)...
您遇到了常见问题:您重写了 onDraw()
,它利用了视图部分。所以你有一个非常昂贵的自定义视图,它不能正常工作。如果您让 View 绘制,它将绘制一次,但您没有调用 invalidate()
,因此它永远不会再发生。我的猜测是您在布局中设置了背景颜色,使 View 不透明,这就是为什么您看不到 Surface 上绘制的内容的原因。
对于 Canvas 渲染,通常使用 custom View 而不是 SurfaceView 更好。我会推荐这种方法。
如果您真的想使用 SurfaceView,请将 onDraw()
更改为其他内容(例如 doDraw()
),然后从渲染线程调用它。由于您将在 Surface 部分而不是 View 部分上绘图,因此您不需要 subclass SurfaceView(也不应该,因为将其设为独立的 class 将避免常见的陷阱,比如你掉进去的那个)。确保您了解如何 SurfaceView and Activity interact,因为惹麻烦的方式多种多样。
您还应该阅读构建 game loop 的推荐方法。
我刚刚将覆盖函数 onDraw(Canvas canvas) 更改为 draw(Canvas canvas) 并且它有效!