I'm working on a Android game using SurfaceView, the game works fine, so I want to pause it when the user hits HOME key but when getting back the SurfaceView disappears(black background Edit: sometimes the drawing appears) and buttons are inactive in both cases then I get ANR.
public class MainActivity extends Activity {
private Game game;
MainPanel mp;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
game = new Game();
LoadLevel();
Init();
setContentView(R.layout.main);//contains surfaceview and buttons
mp = (MainPanel) findViewById(R.id.SurfaceView01);
mp.Init(game,this);
Button btnTop = (Button) findViewById(R.id.buttonTop);
Button btnBottom = (Button) findViewById(R.id.buttonBottom);
Button btnLeft = (Button) findViewById(R.id.buttonLeft);
Button btnRight = (Button) findViewById(R.id.buttonRight);
btnTop.setOnClickListener...
}
private void Init() {
...
}
public void LoadLevel() {
...
}
#Override
protected void onResume() {
super.onResume();
Log.d("Tag","Resume");
}
#Override
protected void onPause() {
super.onPause();
Log.d("Tag","Pause");
}
}
public class MainPanel extends SurfaceView implements SurfaceHolder.Callback {
private MainThread thread;
private Game game;
private MainActivity act;
public MainPanel(Context context, AttributeSet attrs) {
super(context,attrs);
getHolder().addCallback(this);
}
public MainThread getThread() {
return thread;
}
public void Init(Game game, MainActivity act){
this.game = game;
this.act = act;
}
public void Move(int dir) {
...
}
public void update() {
...
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (thread == null) {
thread = new MainThread(getHolder(), this);
thread.Start();
thread.start();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
thread.Stop();
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {}
}
thread = null;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
//drawing...
}
}
public class MainThread extends Thread {
private final static int MAX_FPS = 30;
private final static int MAX_FRAME_SKIPS = 3;
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
private SurfaceHolder surfaceHolder;
private MainPanel gamePanel;
private boolean run = false;
private boolean start = false;
public MainThread(SurfaceHolder surfaceHolder, MainPanel gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
public void Start() {
this.run = true;
this.start = true;
}
public void Pause() {
this.start = false;
}
public void Resume() {
this.start = true;
}
public void Stop() {
this.run = false;
}
#Override
public void run() {
Canvas c = null;
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
while(run) {
while(start) {
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
gamePanel.update();
// render state to the screen draws the canvas on the panel
if(c!=null) gamePanel.onDraw(c);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up update without rendering
gamePanel.update();
// add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
} finally {
if (c != null) surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
Thx to this post why is my runnable giving ANR? I could solve the problem, I isolated the MainThread from the UI thread for further details check the best answer.
Related
I am running into an issue in my app. When my game ends (when life == 0) I am attempting to switch to a game over screen by using a different activity. When the game ends, the app simply crashes. I have included the XML for the activity I am trying to switch from as well as indicating where the app crashes. If anyone could help out, that would be great! Thanks.
activity_game.XML:
SurfaceView I am trying to switch from once game ends:
public class SVGameView extends SurfaceView implements Runnable {
private SurfaceHolder holder;
Thread thread = null;
volatile boolean running = false;
static final long FPS = 30;
private Sprite sprite;
private long lastClick;
private Bitmap ball, gameOver;
//private int x = 200, y = 200;
private int scorePosX = 100;
private int scorePosY = 100;
private int countScore = 0;
private int life = 1;
public SVGameView(Context context) {
super(context);
thread = new Thread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
running = false;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
running = true;
thread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball2);
gameOver = BitmapFactory.decodeResource(getResources(),R.drawable.endscreen);
sprite = new Sprite(this, ball);
}
#Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (running) {
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = getHolder().lockCanvas();
synchronized (getHolder()) {
update();
draw(c);
}
} finally {
if (c != null) {
getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
thread.sleep(sleepTime);
else
thread.sleep(10);
} catch (Exception e) {}
}
}
private void update(){
sprite.update();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
canvas.drawPaint(paint);
paint.setColor(Color.WHITE);
paint.setTextSize(48);
canvas.drawText("Score: " + countScore, scorePosX, scorePosY, paint);
canvas.drawText("Lives: " + life, 500, 100, paint);
sprite.onDraw(canvas);
//Crashes here
if(life == 0) {
getContext().startActivity(new Intent(getContext(), SVGameOver.class));
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(System.currentTimeMillis()-lastClick > 300){
lastClick = System.currentTimeMillis();
}
synchronized (getHolder()){
if(sprite.isHit(event.getX(), event.getY())){
countScore += 1;
sprite.increase();
}else{
life --;
}
}
return super.onTouchEvent(event);
}
}
Activity I am trying to reach once the game ends:
public class SVGameOver extends Activity {
private Bitmap gameOverScreen;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
gameOverScreen = BitmapFactory.decodeResource(getResources(), R.drawable.endscreen);
}
protected void onDraw(Canvas canvas){
canvas.drawBitmap(gameOverScreen, 0,0,null);
}
}
I think logcat is asking you the right question: "have you declared this activity in your AndroidManifest.xml" ?
If you think you did it, It's highly probable you did it in a wrong way, most of the times that you think you added an Activity to the manifest but you are receiving this kind of crash, 99,9% of the time you declared it with a wrong namespace
Declare SVGameOver activity in your AndroidManifest.xml:
<activity
android:name="com.example.welcome.assignment2.SVGameOver">
...
</activity>
I have an activity like below. I want the threads inside the activity terminate when the activity is paused or stopped. I searched and found volatile Boolean solution which didn't work for me. When i put the activity in pause or stop state, download continues, which i don't want it.
public class MyActivity extends Activity {
//some code here
private void foo(){
new Thread (new Runnable (){
#Override
public void run() {
//download something from internet
}
}).start();
}
}
i used this pattern which didn't work:
public class MyActivity extends Activity {
volatile Boolean state = true;
//some code here
private void foo(){
new Thread (new Runnable (){
#Override
public void run() {
while (state) {
//download something from internet
}
}
}).start();
}
#Override
public void onPause(){
super.onPause();
state = false;
}
#Override
public void onStop(){
super.onStop();
state = false;
}
}
Here's an example of the structure of a thread which stops when back is hit, which I made for my game (and works). One key difference is you're using a thread in your Activity, while in mine I call a View in my Activity and run the thread in the View. If I hit back, I return to my Activity, and can call the thread again by hitting 'start.' BECAUSE my thread behaves the way you want yours too, even if it's happening in View, I thought you may find it helpful.
Where my of my synchronization happens is with the touchscreen values, and making sure those are updated in the thread and calling function.
The thread is trashed totally unless you have a way of saving the state (if you need to).
Your thread will need to synchronize with other functions you want controlling/sharing values with the thread, like onPause, etc..
**you'll need to have some synchronized test value inside your while loop which can then change state to false, otherwise the thread will continue on its own, which is the point of threads.
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
private final GameActivity gameActivity;
private GameThread _thread;
private boolean previouslyRunning = false;
private int width; //Screen width
private int height; //Screen height
private boolean newGame = true;
public GameView(Context context) {
super(context);
getHolder().addCallback(this);
this.gameActivity = (GameActivity) context;
_thread = new GameThread(getHolder(), this);
setFocusable(true);
setFocusableInTouchMode(true);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
width = w;
height = h;
super.onSizeChanged(w, h, oldw, oldh);
//_thread.initialize();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!previouslyRunning) {
_thread = new GameThread(getHolder(), this);
_thread.initialize();
}
_thread.setRunning(true);
_thread.start();
previouslyRunning = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//TODO - this was an Auto-generated method stub...
//TODO - research what this might be useful for
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
//Put stuff that needs destructed here.
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// will will try again and again
//TODO: figure it out....
}
}
}
public boolean onTouchEvent(MotionEvent event) {
int numPointers = event.getPointerCount();
int ptrIdx = 0;
int touch = event.getActionMasked();
if (touch == MotionEvent.ACTION_DOWN) {
while (ptrIdx < numPointers) {
int id = event.getPointerId(ptrIdx);
float xp = event.getX(ptrIdx) / width;
if (xp > 0.6) {
_thread.shieldFront = false;
}
if (xp > 0.6 && !attacks) {
attacks = true;
_thread.attackandDefendToggle(true);
} else if (xp > 0.6 && attacks) {
attacks = false;
_thread.attackandDefendToggle(false);
} else if ((xp < 0.4 && xp > 0.2) && !movedRight) {
movedRight = true;
_thread.moveRight(true);
} else if ((xp < 0.4 && xp > 0.2) && movedRight) {
movedRight = false;
_thread.moveRight(false);
} else if (xp < 0.2 && !movedLeft) {
movedLeft = true;
_thread.moveLeft(true);
} else if (xp < 0.2 && movedLeft) {
movedLeft = false;
_thread.moveLeft(false);
}
ptrIdx++;
}
}
if (touch == MotionEvent.ACTION_UP) {
_thread.moveLeft(false);
_thread.moveRight(false);
_thread.attackandDefendToggle(false);
attacks = false;
_thread.shieldFront = true;
}
return true;
}
class GameThread extends Thread {
/****************************
* Public functions *
****************************/
public GameThread(SurfaceHolder surfaceHolder, GameView panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
// put sounds here.
soundPool = new SoundPool(20, AudioManager.STREAM_MUSIC, 0);
//more sounds later
//TODO: create sounds
}
/************************************************
* update() function updates all variables, *
* such as physics, Canvas draw points, score *
* life, etc.. It is called before draw. *
************************************************/
private void update() {
// all the values I want updated with each callback
}
/************************************************
* draw() function creates images on screen, *
* but it performs no logic. *
************************************************/
private void draw(Canvas canvas) {
if (canvas == null) {
return;
}
//Draw stuff on screen
}
public void initialize() {
// Values I want the program to start with;
}
public void setRunning(boolean run) {
_run = run;
}
//Code below actually runs the thread.
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
// Update the game state
update();
// Draw image
draw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
I have a background animation drawn onto a SurfaceView by another thread in my app. The animation seems to work well except when the screen is rotated. Then the main thread will sometimes hang for a couple of seconds. Using DDMS I see that the main thread is calling Object.wait(), I don't understand where or why it's doing that though.
Below is some abbreviated code, if needed the full source can be found on github at https://github.com/GavinDBrown/Amazing.
Main Activity:
public class StartMenu extends Activity {
/** A handle to the thread that's running the Game Of Life animation. */
private GOLThread mGOLThread;
/** A handle to the View in which the background is running. */
private GOLView mGOLView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_of_life_background);
startGOLBackground();
}
private void startGOLBackground() {
// get handles to the GOLView and it's GOLThread
mGOLView = (GOLView) findViewById(R.id.game_of_life_background);
mGOLThread = new GOLThread(mGOLView.getHolder());
mGOLView.setThread(mGOLThread);
mGOLThread.start();
}
private void stopGOLBackground() {
if (mGOLThread != null) {
mGOLThread.halt(); // stop the animation if it's valid
boolean retry = true;
while (retry) {
try {
mGOLThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
mGOLThread = null;
mGOLView = null;
}
}
#Override
public void onResume() {
super.onResume();
mGOLThread = mGOLView.getThread();
mGOLThread.unpause();
}
#Override
public void onPause() {
super.onPause();
mGOLThread.pause();
}
#Override
protected void onDestroy() {
super.onDestroy();
stopGOLBackground();
}
}
The SurfaceView:
public class GOLView extends SurfaceView implements SurfaceHolder.Callback {
/** The thread that actually draws the animation */
private GOLThread thread;
SurfaceHolder surfaceHolder;
public GOLView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
}
#Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (hasWindowFocus){
thread.unpause();
} else {
thread.pause();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
thread.setSurfaceSize(width, height);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
thread.pause();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.unpause();
}
}
And finally the Thread:
public class GOLThread extends Thread {
private GameOfLife gameOfLife;
private final Object GOLLock = new Object();
private int mCanvasHeight;
private int mCanvasWidth;
private SurfaceHolder mSurfaceHolder;
public GOLThread(SurfaceHolder surfaceHolder) {
mSurfaceHolder = surfaceHolder;
}
#Override
public void start() {
synchronized (mSurfaceHolder) {
stopped = false;
mSurfaceHolder.notify();
}
super.start();
}
public void halt() {
synchronized (mSurfaceHolder) {
paused = true;
stopped = true;
mSurfaceHolder.notify();
}
}
public void pause() {
synchronized (mSurfaceHolder) {
paused = true;
}
}
public void unpause() {
synchronized (mSurfaceHolder) {
paused = false;
mSurfaceHolder.notify();
}
}
public Bundle saveState(Bundle outState) {
synchronized (GOLLock) {
if (outState != null) {
outState.putParcelable(GAME_OF_LIFE_ID, gameOfLife);
}
}
return outState;
}
public synchronized void restoreState(Bundle savedState) {
synchronized (GOLLock) {
gameOfLife = (GameOfLife) savedState.getParcelable(GAME_OF_LIFE_ID);
}
}
#Override
public void run() {
while (!stopped) {
while (paused && !stopped) {
try {
synchronized (mSurfaceHolder) {
mSurfaceHolder.wait(100L);
}
} catch (InterruptedException ignore) {
}
}
// Check if thread was stopped while it was paused.
if (stopped)
break;
beforeTime = System.nanoTime();
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas();
synchronized (GOLLock) {
if (gameOfLife != null) {
gameOfLife.drawAndUpdate(c);
} else {
pause();
}
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
sleepTime = FRAME_DELAY
- ((System.nanoTime() - beforeTime) / 1000000L);
try {
// actual sleep code
if (sleepTime > 0 && !stopped && !paused) {
synchronized (mSurfaceHolder) {
mSurfaceHolder.wait(sleepTime);
}
}
} catch (InterruptedException ex) {
}
}
}
public void setSurfaceSize(int width, int height) {
synchronized (GOLLock) {
if (mCanvasWidth != width || mCanvasHeight != height) {
mCanvasWidth = width;
mCanvasHeight = height;
// reset the GOL
if (mCanvasWidth > 0 && mCanvasHeight > 0) {
gameOfLife = new GameOfLife();
gameOfLife.init(mCanvasWidth, mCanvasHeight);
}
}
}
}
}
The problem is GOLThread is calling SurfaceHolder.lockCanvas() and getting null as a result too often.
From the docs on SurfaceHolder.lockCanvas()
If you call this repeatedly when the Surface is not ready (before
Callback.surfaceCreated or after Callback.surfaceDestroyed), your
calls will be throttled to a slow rate in order to avoid consuming
CPU.
So the OS was throttling calls by putting my threads to sleep.
I fixed it by updating the code in GOLThread.run() to have
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas();
if (c == null) {
// Pause here so that our calls do not get throttled by the
// OS for calling lockCanvas too often.
pause();
} else {
synchronized (GOLLock) {
if (gameOfLife != null) {
gameOfLife.drawAndUpdate(c);
} else {
pause();
}
}
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
I'm new to programming in android and I can't understand why the OnDraw() method in my renderer doesn't get called as invalidate() is called. The final set background resource does get called however. There are 2 classes, Game.java and Renderer.java. It seems that the invalidate call does not have time to get processed during the loop in game.
Game.Java
public class Game extends Activity
{
Level currentLevel;
private static List<Entity> _renderList = new ArrayList<Entity>();
public void StartLevel(View view)
{
System.out.println("I HAVE STARTED THE LEVEL!");
// Hide the play button
Button playButton = (Button) findViewById(R.id.start_game);
playButton.setVisibility(View.GONE);
// Load the level
currentLevel = new Level1(this);
// Create the initial entities
// Add the entities to the screen
List<Bitmap> bitmapList;
bitmapList = new ArrayList<Bitmap>();
Bitmap enemy = BitmapFactory.decodeResource(getResources(), R.drawable.enemy);
bitmapList.add(enemy);
System.out.println("CREATING RENDERER!");
Renderer renderer = new Renderer(this, _renderList, bitmapList);
renderer.setBackgroundResource(R.drawable.beachbackground);
setContentView(renderer);
System.out.println("STARTING TRAVEL!");
while (currentLevel.GetDistTravelled() < currentLevel.GetDist())
{
Retrieve();
currentLevel.AddDistance(1);
System.out.println("DISTANCE +1!");
renderer.RenderFrame(_renderList);
System.out.println("RENDER LEVEL FRAME!");
try
{
Thread.currentThread().sleep(50);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("SET BACKGROUND!");
renderer.setBackgroundResource(R.drawable.menubackground);
}
public List<Entity> GetRenderList()
{
return _renderList;
}
private void Retrieve()
{
// get the array
Entity[] enemies;
enemies = currentLevel.CheckSpawn();
if(enemies != null)
{
// parse the array into entities
int length = enemies.length;
System.out.println(length);
// remember to Get enemy strings
for(int i = 0; i < length; i++)
{
// add them to our array
_renderList.add(enemies[i]);
}
}
}
}
Renderer.java
class Renderer extends SurfaceView implements SurfaceHolder.Callback
{
PanelThread _thread;
List<Entity> _renderList = new ArrayList<Entity>();
List<Bitmap> _bitmapList = new ArrayList<Bitmap>();
public Renderer(Context context, List<Entity> renderList, List<Bitmap> bitmapList)
{
super(context);
getHolder().addCallback(this);
_bitmapList = bitmapList;
}
public void RenderFrame(List<Entity> renderList)
{
for (int i = 0; i < renderList.size(); i++)
{
_renderList.add(renderList.get(i));
}
invalidate();
}
#Override
public void onDraw(Canvas canvas)
{
Paint paint = null;
// Draw enemies
if(_renderList.size() != 0 && _renderList != null)
{
int size = _renderList.size();
for(int i = 0; i < size; i++)
{
canvas.drawBitmap(_bitmapList.get(_renderList.get(i).GetBitmapID()), _renderList.get(i).GetX(), _renderList.get(i).GetY(), paint);
}
}
}
// position the train on the screen in the appropriate place
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
#Override
public void surfaceCreated(SurfaceHolder holder)
{
setWillNotDraw(false); //Allows us to use invalidate() to call onDraw()
_thread = new PanelThread(getHolder(), this); //Start the thread that
_thread.setRunning(true); //will make calls to
_thread.start(); //onDraw()
}
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
try
{
_thread.setRunning(false); //Tells thread to stop
_thread.join(); //Removes thread from mem.
}
catch (InterruptedException e) {}
}
class PanelThread extends Thread
{
private SurfaceHolder _surfaceHolder;
private Renderer _renderer;
private boolean _run = false;
public PanelThread(SurfaceHolder surfaceHolder, Renderer renderer)
{
_surfaceHolder = surfaceHolder;
_renderer = renderer;
}
public void setRunning(boolean run)
{
_run = run;
}
#Override
public void run()
{
Canvas c;
while (_run)
{
c = null;
try
{
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder)
{
//Insert methods to modify positions of items in onDraw()
postInvalidate();
}
}
finally {
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
Any help on solving this problem would be very grateful. If anymore info is needed just ask.
Hi i have the same error like you. But i have found that. But i don't known clearly about that.
but you can try
add the attribute android:background to your layout.
I try to display one from list of bitmaps during onDraw.
When i'm passing list to the canvas all are display and stay in their places.
When I pass one random bitmaps it's redrawing canvas all the time.
All works when i'm using public void drawEnemy(Canvas canvas) but not exactly like I want when using public void drawEn(Canvas canvas).
I want to display one random bitmap, then after a few seconds, delete it and display other bitmap. I think the problem is how I implemented onDrow() method. It's redrawing canvas all the time.
Activity:
public class NewGameActivity extends Activity{
NewGame newgame;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// Landscape mode
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// no title
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
// content Newgame.java
newgame = new NewGame(this);
setContentView(newgame);
}
Thread:
public class MainThread extends Thread{
private SurfaceHolder surfaceHolder;
private NewGame screen;
public MainThread(SurfaceHolder surfaceHolder, NewGame ekran) {
super();
this.surfaceHolder = surfaceHolder;
this.screen= screen;
}
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
#Override
public void run() {
Canvas canvas;
while (running) {
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
this.screen.onDraw(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
SurfaceView:
public class NewGame extends SurfaceView implements SurfaceHolder.Callback{
private MainThread thread;
private EnemyManager manager;
public NewGame(Context context) {
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
manager = new EnemyManager();
// TODO Auto-generated constructor stub
//adding enemy
Enemy e1 = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.card), 1);
Enemy e2 = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.horse), 2);
EnemyLocation l1 = new EnemyLocation(60, 180);
EnemyLocation l2 = new EnemyLocation(60, 50);
manager.AddEnemy(e1, l1);
manager.AddEnemy(e2, l2);
setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.saloon), 0, 0, null);
manager.drawEn(canvas);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
thread.setRunning(false);
thread.stop();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
manager.handleActionDown((int)event.getX(), (int)event.getY());
}
return true;
}
}
EnemyManager:
public class EnemyManager {
private ArrayList<Enemy> enemyList;
private ArrayList<Enemy> suspects;
private Enemy cow;
private String message;
private int suspectID;
private Random rnd;
public String getMessage() {
return message;
}
public EnemyManager(){
enemyList = new ArrayList<Enemy>();
suspects = new ArrayList<Enemy>();
}
public void AddEnemy(Enemy enemy, EnemyLocation loc){
// set x,y enemy localization
enemy.setX(loc.getX());
enemy.setY(loc.getY());
enemyList.add(enemy);
}
public void clearEnemy() {
enemyList.clear();
}
// message if enemy touched
public void handleActionDown(int x, int y) {
for (Enemy enemy: enemyList) {
if (enemy.wasTouched(x, y)) {
message = enemy.getId();
return;
}
}
}
public void PrepareEnemy(){
suspectID = enemyList.get(rnd.nextInt(enemyList.size()+1)).getId();
suspects = new ArrayList<Enemy>();
suspects.add(getSuspectByID(suspectID));
}
private Enemy SingleEnemy(){
Double i = 1 + Math.random() * ((enemyList.size()-1)+1);
cow = getSuspectByID(i.intValue());
return cow;
}
private Enemy getSuspectByID(int suspectID) {
for (Enemy s: enemyList) {
if (s.getId() == suspectID) {
return s;
}
}
return null;
}
public void drawEn(Canvas canvas){
try {
Enemy k = SingleEnemy();
canvas.drawBitmap(cow.picture, cow.x, cow.y, null);
} catch (Exception e) {
// TODO: handle exception
}
}
// draw enemy
public void drawEnemy(Canvas canvas) {
try {
for (Enemy enemy: enemyList) {
canvas.drawBitmap(enemy.picture, enemy.x, enemy.y, null);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
das
As for as understand you are trying to do something like this (if it's not, please correct me):
This is rendering the canvas with all components:
Draw background
Draw enemy
To "refresh" the canvas you simply do something like this:
Draw background
Update
To pause the rendering you could do something like this:
int lastUpdateTime;
int delayTime = 2000; 2 seconds
if(System.currenttimeMillis() > lastUpdateTime + delayTime) {
// Finished waiting
}
You should only define lastUpdateTime when you want to wait and not in every iteration.
NB: Don't call Thread.sleep() in a rendering thread!