我刚刚创建了一个Activity,它在“setContentView”中使用了一个扩展SurfaceView的类的视图。问题是: 它运行正常,但当我退出它(BACK键)时它会崩溃。代码:
package ro.etrandafir.mate.appCreator;
import android.app.Activity;
import android.os.Bundle;
import android.content.Context;
import android.view.View;
import android.view.SurfaceView;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.graphics.Color;
import android.graphics.Paint;
public class Sample2 extends Activity implements View.OnTouchListener {
float x = 0, y = 0;
SampleTwoView theView;
public boolean onTouch(View v, MotionEvent event) {
// TODO: Implement this method
x = event.getX();
y = event.getY();
return true;
}
@Override
protected void onPause() {
super.onPause();
finish();
}
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
theView = new SampleTwoView(this);
theView.setOnTouchListener(this);
setContentView(theView);
}
public class SampleTwoView extends SurfaceView implements Runnable {
Paint p = new Paint();
public SampleTwoView(Context context) {
super(context);
p.setColor(Color.RED);
Thread theThread = new Thread(this);
theThread.start();
}
public void run() {
while (true) {
if (!getHolder().getSurface().isValid()) continue;
Canvas canvas;
canvas = getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
if ((x != 0) && (y != 0)) canvas.drawCircle(x, y, 40, p);
getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
我该怎么办?我应该添加onDestroy还是什么?
提前致谢, 马太
答案 0 :(得分:2)
您遇到的问题与此代码有关:
Canvas canvas;
canvas = getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
当您的活动结束时,您的线程仍在运行,但您的自定义SurfaceView
不再可用,因此您将获得null ptr异常。只要调用onPause
fn,就可以通过添加一个设置为false的布尔值来轻松修补现有代码:
public void run() {
while (booleanThatGetsSetToFalseWhenActivityPauses) {
if (!getHolder().getSurface().isValid()) continue;
Canvas canvas;
canvas = getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
if ((x != 0) && (y != 0)) canvas.drawCircle(x, y, 40, p);
getHolder().unlockCanvasAndPost(canvas);
}
}
但是,我建议改变整个应用程序的结构。这可能只是为了练习,但我认为实现目标的更有效和无错误的方法是简单地使用标准SurfaceView
并完全将绘图逻辑与任何自定义视图分离。
我重新设计的活动如下,但它使用了一个Ball
类,用于维护球的逻辑,在当前代码中,它与动作(坐标)和视图分别耦合( Paint
)。在这个新的球类中,球具有位置(由PointF
指定),Paint
和直径。除了设置一些变量外,它还有获取大多数变量的方法。
public class Ball {
private Paint mPaint;
private PointF mCoordinates;
private int mDiameter;
public Ball (int color, int diameter) {
mPaint = new Paint();
mPaint.setColor(color);
mCoordinates = new PointF();
mCoordinates.x = 0;
mCoordinates.y = 0;
mDiameter = diameter;
}
public void setCoordinates (float x, float y) {
mCoordinates.x = x;
mCoordinates.y = y;
}
public PointF getCoordinates() {
return mCoordinates;
}
public Paint getPaint() {
return mPaint;
}
public int getDiameter() {
return mDiameter;
}
/* You did not want to draw the uninitialized ball, so this method checks that */
public boolean hasNonZeroLocation () {
return (mCoordinates.x != 0 && mCoordinates.y != 0);
}
}
我在活动中使用Ball
类,如下所示。请注意,重绘到画布现在只在用户触摸画布而不是无限循环时才会发生。这是由于使用Handler
类来发布要运行到UI线程的操作。另外,现在我们不需要自定义视图,并且我们的球的逻辑已经与活动和视图分离。
public class RedBallActivity extends Activity {
Handler mDrawingHandler;
SurfaceView mDrawingSurfaceView;
Ball mBall;
private final Runnable drawRedBallOnBlueSurface = new Runnable() {
@Override
public void run() {
if (!mDrawingSurfaceView.getHolder().getSurface().isValid()) return;
Canvas canvas = mDrawingSurfaceView.getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
if (mBall.hasNonZeroLocation())
canvas.drawCircle(mBall.getCoordinates().x, mBall.getCoordinates().y, mBall.getDiameter(), mBall.getPaint());
mDrawingSurfaceView.getHolder().unlockCanvasAndPost(canvas);
}
};
private final OnTouchListener mCanvasTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mBall.setCoordinates(event.getX(), event.getY());
mDrawingHandler.post(drawRedBallOnBlueSurface);
return true;
}
};
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
mDrawingSurfaceView = new SurfaceView(this);
mDrawingSurfaceView.setOnTouchListener(mCanvasTouchListener);
setContentView(mDrawingSurfaceView);
mBall = new Ball(Color.RED, 40);
mDrawingHandler = new Handler();
}
}
现在,如果您实际运行此代码,您会注意到最初屏幕未使用蓝色背景绘制。您可能只想在mDrawingHandler.post(drawRedBallOnBlueSurface);
方法的末尾调用onCreate
,但不能保证SurfaceView已准备就绪(see the documentation on this lockCanvas method)。如果您希望曲面最初为蓝色,则需要实现[SurfaceHolder.Callback][2]
,需要连接到SurfaceView的SurfaceHolder,并且在被调用的surfaceCreated
方法上,我们知道曲面已准备就绪,所以我们可以调用mDrawingHandler.post(drawRedBallOnBlueSurface);
现在,添加了这个,我更改了Activity以实现[SurfaceHolder.Callback][2]
,如下所示:
public class FriendManagerActivity extends Activity implements SurfaceHolder.Callback {
并将此行添加到构造函数中:
mDrawingSurfaceView.getHolder().addCallback(this);
并实现界面:
@Override
public void surfaceCreated(SurfaceHolder holder) {
mDrawingHandler.post(drawRedBallOnBlueSurface);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
随意对我的小重新设计提出任何问题!虽然您的问题可以轻松修补,但我觉得您将逻辑与视图耦合的方式有点有缺陷,并且认为有关SurfaceView编码的更多信息会有所帮助。
答案 1 :(得分:2)
正如有人在上面提到的,当你的活动结束时,你的线程仍然在运行,但你的自定义SurfaceView不再可用,所以你会得到一个Null Point异常。一旦onPause fn被调用,就可以通过添加一个设置为false的布尔值来轻松修补现有代码:我遇到了同样的问题。为了解决这个问题,我在您的SampleTwoView类中添加了以下onPause():
// pause method will destroy the Thread
public void pause() {
isRunning = false;
while (true) {
try {
myThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
myThread = null;
}
然后在Sample2类的onPause()方法中调用此onPause()方法,如下所示:
@Override
protected void onPause() {
super.onPause();
SampleTwoView.onPause();
finish();
}
因此,每次调用主Activity类的onPause()方法时,Thread都将被销毁。 我希望这能帮到您。 干杯!