我有一个可以绘制动画的Runnable GameView
,它在NewGame
活动中创建并使用。 NewGame
启动另一个名为PetInfo
的活动,点击视图上的按钮。但是,每当我启动PetInfo
时,我都会收到以下错误:
04-06 19:13:30.025 23272-23272 /?我/艺术:延迟启用-Xcheck:jni
04-06 19:13:30.211 23272-23312 / com.example.xuan.tictactoe D / OpenGLRenderer:使用EGL_SWAP_BEHAVIOR_PRESERVED:true
04-06 19:13:30.219 23272-23272 / com.example.xuan.tictactoe D / Atlas:验证地图......
04-06 19:13:30.245 23272-23312 / com.example.xuan.tictactoe I / Adreno-EGL :: QUALCOMM Build:01/14/15,ab0075f,Id3510ff6dc
04-06 19:13:30.246 23272-23312 / com.example.xuan.tictactoe I / OpenGLRenderer:初始化的EGL,版本1.4
04-06 19:13:30.267 23272-23312 / com.example.xuan.tictactoe D / OpenGLRenderer:启用调试模式0
04-06 19:14:40.950 23272-23272 / com.example.xuan.tictactoe D / test:onCreate
04-06 19:14:40.956 23272-23272 / com.example.xuan.tictactoe D / test:设置视图后的onCreate
04-06 19:14:41.161 23272-24500 / com.example.xuan.tictactoe E / AndroidRuntime:FATAL EXCEPTION:Thread-8231
处理:com.example.xuan.tictactoe,PID:23272
java.lang.NullPointerException:尝试在空对象引用上调用虚方法'void android.graphics.Canvas.drawColor(int)'
at com.example.xuan.tictactoe.GameView.draw(GameView.java:135)
at com.example.xuan.tictactoe.GameView.run(GameView.java:100)
在java.lang.Thread.run(Thread.java:818)
NewGame
活动
public class NewGame extends Activity {
GameView game_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
game_view = new GameView(this);
setContentView(R.layout.activity_new_game);
}
// This method executes when the player starts the game
@Override
protected void onResume() {
super.onResume();
// Tell the gameView resume method to execute
game_view.resume();
}
// This method executes when the player quits the game
@Override
protected void onPause() {
super.onPause();
// Tell the gameView pause method to execute
game_view.pause();
}
// Start new activity
public void createInfo(View view) {
Intent intent = new Intent(this, PetInfo.class);
startActivity(intent);
}
}
GameView.java
public class GameView extends SurfaceView implements Runnable {
Thread game_thread = null;
// SurfaceHolder for Paint and Canvas in a thread
SurfaceHolder the_holder;
// Canvas and Paint objects
Canvas canvas;
Paint paint;
Bitmap dragon;
volatile boolean is_running;
long fps; // tracks the game frame rate
private long time_this_frame; // calculate the fps
float x_position = 0; // start position
float y_position = 0;
long frame_ticker = 0l;
// New variables for spritesheet
private int frame_count = 10; // How many frames are there on the sprite sheet?
private int sprite_width = 600;
private int sprite_height = 450;
private int current_frame = 0; // Start at the first frame - where else?
// A rectangle to define an area of the sprite sheet that represents 1 frame
private Rect frame_to_draw = new Rect(0, 0, sprite_width, sprite_height);
// A rect that defines an area of the screen on which to draw
RectF where_to_draw = new RectF(x_position, 0, x_position + sprite_width, sprite_height);
// Constructor methods
public GameView(Context context) {
super(context);
init(context);
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
// Initialize ourHolder and paint objects
the_holder = getHolder();
the_holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
pause();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
resume();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
paint = new Paint();
// Load dragon from .png file
dragon = BitmapFactory.decodeResource(this.getResources(), R.drawable.gd10_spritesheet);
}
@Override
public void run() {
while (is_running) {
// Capture the current time in milliseconds in startFrameTime
long startFrameTime = System.currentTimeMillis();
update(); // Update the frame
draw(); // Draw the frame
// Calculate the fps this frame to time animations.
time_this_frame = System.currentTimeMillis() - startFrameTime;
if (time_this_frame >= 1) {
fps = 5000 / time_this_frame;
}
}
}
// Everything that needs to be updated goes in here
// In later projects we will have dozens (arrays) of objects.
// We will also do other things like collision detection.
public void update() {
long game_time = System.currentTimeMillis();
if (game_time > (frame_ticker + fps)) {
frame_ticker = game_time;
current_frame++;
if (current_frame >= frame_count) {
current_frame = 0;
}
}
frame_to_draw.left = current_frame * sprite_width;
frame_to_draw.right = frame_to_draw.left + sprite_width;
}
// Draw the newly updated scene
public void draw() {
// Make sure our drawing surface is valid or we crash
if (the_holder.getSurface().isValid()) {
canvas = the_holder.lockCanvas(); // Lock the canvas ready to draw
canvas.drawColor(Color.argb(255, 144, 195, 212)); // Draw the background color
paint.setColor(Color.argb(255, 249, 129, 0)); // Choose the brush color for drawing
x_position = (float) (this.getWidth()/2.0 - frame_to_draw.width()/2.0);
y_position = (float) (this.getHeight()/4.0 - frame_to_draw.height()/2.0);
where_to_draw.set((int) x_position,
(int) y_position,
(int) x_position + sprite_width,
(int) y_position + sprite_height);
canvas.drawBitmap(dragon,
frame_to_draw,
where_to_draw, paint);
the_holder.unlockCanvasAndPost(canvas); // Draw everything to the screen
}
}
// If activity is paused/stopped shutdown our thread.
public void pause() {
is_running = false;
try {
game_thread.join();
} catch (InterruptedException e) {
Log.e("Error:", "joining thread");
}
game_thread = null;
}
// If activity is started then start our thread.
public void resume() {
is_running = true;
game_thread = new Thread(this);
game_thread.start();
}
}
activity_new_game.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.xuan.tictactoe.NewGame">
<com.example.xuan.tictactoe.GameView
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:text="OK"
android:onClick="createInfo"/>
</RelativeLayout>
有趣的是,我在PetInfo
创建了一些登录onCreate并且日志运行,这意味着创建了PetInfo
活动,但由于某些原因,它会在上一个活动的线程上抛出错误(是NewGame
的{{1}})。
有什么想法吗?谢谢!
答案 0 :(得分:-1)
Runnable
=不同/后台线程,不是主线程。这使得在Runnable
内部完成的代码能够在不锁定主/ UI线程的情况下花费很长时间。
但是,这也意味着您无法从其他/后台线程更新主/ UI线程。您只能在Runnable
操作完成后更新UI线程,并且您已离开Runnable
块。