вопрос вы получаете связано с этим кодом:
Canvas canvas;
canvas = getHolder().lockCanvas();
canvas.drawColor(Color.BLUE);
Когда заканчивается ваша деятельность, ваш поток по-прежнему работает, но пользовательский SurfaceView
больше не доступно, поэтому вы получите исключение null ptr. Ваш существующий код может быть легко исправлена путем добавления булево, который получает значение ЛОЖЬ, как только п вызывается:
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
класс, который используется для поддержания логики шара, который, в текущем коде отдельно в сочетании как с actvity (координат) и представление (или 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
класс в деятельности, как показано ниже. Обратите внимание, что перерисовка на холст теперь возникает только тогда, когда пользователь касается холста, а не бесконечного цикла while. Это связано с использованием класса Handler
, который отправляет действия для запуска в поток пользовательского интерфейса. Кроме того, теперь нам не нужен пользовательский вид, и логика нашего шара была отделена от активности и представления.
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]
, который необходимо подключить к SurfaceHolder SurfaceView, и по вызываемому методу surfaceCreated
мы знаем, что поверхность готова, поэтому мы можем позвонить mDrawingHandler.post(drawRedBallOnBlueSurface);
Теперь с этим добавил, изменить активность реализовать [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.
отправьте свою трассировку стека logcat. –
Какая ошибка вы получаете? –
Так что я должен установить while (boolean var) и изменить var на false в onPause? – Chaoz