Having trouble implementing CountDownTimer in Android game - java

This is a simple Android game built on one Activity that is extended in my AndroidGame class. Most of my other classes extend my 'Screen' class to make things like the Menu, In-Game screen, Game Over screen, etc. I would like to create an instance of CountDownTimer, call it once the user presses play and the game is confirmed to be running (in my GameScreen class), and then return the user to the main menu after 30 seconds are up.
My main Activity looks like this. I've included the whole class but surrounded the important parts with comments:
package com.game.framework.implementation;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.view.Window;
import android.view.WindowManager;
import com.game.framework.Audio;
import com.game.framework.FileIO;
import com.game.framework.Game;
import com.game.framework.Graphics;
import com.game.framework.Input;
import com.game.framework.Screen;
public abstract class AndroidGame extends Activity implements Game {
AndroidFastRenderView renderView;
Graphics graphics;
Audio audio;
Input input;
FileIO fileIO;
Screen screen;
WakeLock wakeLock;
//////// Timer variables ////////
public static boolean timerFinished = false;
public static MyCount counter;
////////////////////////////////////////
#SuppressWarnings("deprecation")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
int frameBufferWidth = isPortrait ? 480: 800;
int frameBufferHeight = isPortrait ? 800: 480;
Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth, frameBufferHeight, Config.RGB_565);
float scaleX = (float) frameBufferWidth / getWindowManager().getDefaultDisplay().getWidth();
float scaleY = (float) frameBufferHeight / getWindowManager().getDefaultDisplay().getHeight();
renderView = new AndroidFastRenderView(this, frameBuffer);
graphics = new AndroidGraphics(getAssets(), frameBuffer);
fileIO = new AndroidFileIO(this);
audio = new AndroidAudio(this);
input = new AndroidInput(this, renderView, scaleX, scaleY);
screen = getInitScreen();
setContentView(renderView);
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MyGame");
//////// Instantiate counter ////////
counter = new MyCount(30000,1000);
//////////////////////////////////
}
#Override
public void onResume() {
super.onResume();
wakeLock.acquire();
screen.resume();
renderView.resume();
}
#Override
public void onPause() {
super.onPause();
wakeLock.release();
renderView.pause();
screen.pause();
if (isFinishing())
screen.dispose();
}
#Override
public Input getInput() {
return input;
}
#Override
public FileIO getFileIO() {
return fileIO;
}
#Override
public Graphics getGraphics() {
return graphics;
}
#Override
public Audio getAudio() {
return audio;
}
#Override
public void setScreen(Screen screen) {
if (screen == null)
throw new IllegalArgumentException("Screen must not be null");
this.screen.pause();
this.screen.dispose();
screen.resume();
screen.update(0);
this.screen = screen;
}
#Override
public void onBackPressed() {
getCurrentScreen().backButton();
}
public Screen getCurrentScreen() {
return screen;
}
//////// The Timer Class ////////////////////////////////
public class MyCount extends CountDownTimer{
public MyCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onFinish() {
timerFinished = true;
}
#Override
public void onTick(long millisUntilFinished) {
}
}
////////////////////////////////////////////////////////
}
Now once the timer runs out, as you see, I change the boolean timerFinished to true. In my GameScreen class I have a method called updateRunning that deals with user interaction once the game is live and running (play has been pressed).
private void updateRunning(List<TouchEvent> touchEvents, float deltaTime) {
///// Start the timer and go to menu if time is up /////
AndroidGame.counter.start();
if (AndroidGame.timerFinished == true) {
goToMenu();
}
///////////////////////////////////////////////////////
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = (TouchEvent) touchEvents.get(i);
if (event.type == TouchEvent.TOUCH_DOWN) {
// Do lots of stuff based on which buttons the user presses
I am not getting an errors and the game is running fine, but it is not returning to the main menu after the 30 seconds are up. I have no idea why and would really appreciate any thoughts on the matter. Thanks!
EDIT 1: This is my goToMenu() method:
private void goToMenu() {
game.setScreen(new MainMenuScreen(game));
}
I cannot call this within AndroidGame because it references game, and making that static will cause a lot of issues.
EDIT 2:
Here's a TLDR version of what I'm trying to do - for clarity.
I have class1, where I extend Activity, and class2 where I deal with user input and paint items to the screen.
In my Android game, I want a function that will count down from 30 seconds. I want to pass the time remaining into a global variable so I can paint it in class2. Once the Timer reaches 0, I would like to run goToMenu(), which also has to exist in class2.
As of now, I have the CountDownTimer subclass in class1 and I instantiate it there. I have a global boolean that I set to True within the timer's onFinish() method. If the boolean is True, then I run goToMenu() in class2 but for some reason it does not work.

Related

Android use grey overlay on screen as dimming, fadeing

I bought myself a android car unit and came at the conclusion that the minimum brightness is still to bright when driving in the night. There is a clever app called screen filter that (i think it works like this) uses a overlay over the screen to fake a screen dimming. I wanted to make this by myself because i want to enable it when it's sunset and disable it after sunrise.
After some searching in the web i found a usable code.
Create a Main activity with a service called Overlayservice. The service runs when i start the app and the overlay also works fine.
The next thing i want to make is a code so i can change the overlay transparency. I want that the program starts fading when sunset starts and for example after an half hour the transparency is 50%.
The problem i am facing at is i am not able to set a new colour and transparency while the overlay is active in a service.
What i tired was creating a timer in the service that sets the colour after 5 seconds but i got a crash with the error:
Only the original thread that created a view hierarchy can touch its views.
The code i now have is as following:
package erik.autostart;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.LinearLayout;
import java.util.Timer;
import java.util.TimerTask;
public class OverlayService extends Service {
String color;
LinearLayout oView;
WindowManager.LayoutParams localLayoutParams;
private static Timer timer = new Timer();
#Override
public IBinder onBind(Intent i) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.color = intent.getExtras().getString("color");
Log.v("Message","Intent: " + Color.parseColor(this.color));
oView = new LinearLayout(getBaseContext());
localLayoutParams = new WindowManager.LayoutParams();
oView.setBackgroundColor(Color.parseColor(this.color)); //De eerste 2 waardes zijn de density ff is volledig en 00 is weg de laatste 6 waardes zijn hex color waardes, kan dus zwart zijn.
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
WindowManager manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.format = PixelFormat.TRANSLUCENT;
manager.addView(oView, localLayoutParams);
timer.scheduleAtFixedRate(new Task(), 0, 500);
return START_STICKY;
}
private class Task extends TimerTask
{
public void run()
{
Log.v("Message", "Tasker");
runtask();
}
}
void runtask()
{
if(oView!=null){
oView.setBackgroundColor(Color.parseColor("#ff000000"));
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.updateViewLayout(oView, oView.getLayoutParams());
}
}
#Override
public void onCreate() {
super.onCreate();
Log.v("Message", "OverlayService started");
}
#Override
public void onDestroy() {
if(oView!=null){
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.removeView(oView);
}
Log.v("Message", "Destroying OverlayService");
super.onDestroy();
}
}
I want to run a tasker somewhere so i can set the new colour and update the overlay.
To fix this i thought perhaps stopping the service and starting it again with a new colour would help so i made a taker in the Mainactivity and stopped the service and start a new one with a different colour. The problem there is that the screen flickers.
I was hoping someone can help me with this problem. Is it even possible to do what i want?
The complete answer I promised earlier. I'm sorry for the untested code, I haven't done anything with Android development in about half a year and don't have the setup ready to test code.
That said, probably the best way is to use Handler. The Handler is initialized to be run on the main (UI) thread, so that it can modify UI. The Dimmer executes on the UI thread, thus preventing the problem you had.
...
public class OverlayService extends Service {
Handler handler;
...
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
...
manager.addView(oView, localLayoutParams);
handler.postDelayed(new Dimmer("#ff000000"), 500);
return START_STICKY;
}
private class Dimmer implements Runnable {
private String color;
public Dimmer (String color) {
this.color = color;
}
public void run() {
if(oView!=null){
oView.setBackgroundColor(Color.parseColor(color));
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.updateViewLayout(oView, oView.getLayoutParams());
}
}
}
#Override
public void onCreate() {
super.onCreate();
Log.v("Message", "OverlayService started");
handler = new Handler(Looper.getMainLooper());
}
...
}

How to navigate back and resume my app without hanging it?

When I run my code below everything works fine. When I want to go back to the previous activity (using the emulator) I get a black screen and then the app shuts down. I also get a black screen when I exit the application and try to resume it.
The code:
package com.example.bono.as3;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
import java.io.InputStream;
public class Main extends Activity {
DrawView drawView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
drawView = new DrawView(this);
setContentView(drawView);
}
#Override public void onResume(){
super.onResume();
drawView.resume();
}
#Override public void onPause(){
super.onPause();
drawView.pause();
}
public class DrawView extends SurfaceView implements Runnable{
Thread gameloop = new Thread();
SurfaceHolder surface;
volatile boolean running = false;
AssetManager assets = null;
BitmapFactory.Options options = null;
Bitmap incect[];
int frame = 0;
public DrawView(Context context){
super(context);
surface = getHolder();
assets = context.getAssets();
options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
incect = new Bitmap[2];
try {
for (int n = 0; n < incect.length; n++){
String filename = "incect"+Integer.toString(n+1)+".png";
InputStream istream = assets.open(filename);
incect[n] = BitmapFactory.decodeStream(istream,null,options);
istream.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
public void resume(){
running = true;
gameloop = new Thread(this);
gameloop.start();
}
public void pause(){
running = false;
while (true){
try {
gameloop.join();
}
catch (InterruptedException e){}
}
}
#Override public void run (){
while (running){
if (!surface.getSurface().isValid())
continue;
Canvas canvas = surface.lockCanvas();
canvas.drawColor(Color.rgb(85, 107, 47));
canvas.drawBitmap(incect[frame], 0, 0, null);
surface.unlockCanvasAndPost(canvas);
frame++;
if (frame > 1) frame = 0;
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
I dont get any error message in the log, what I do get is about 13 messages saying "suspending all threads took: X ms" so it has something to do with my gameloop Thread I think. Unfortunately I don't see what the problem is in my code...
If you need a context, use getApplicationContext() or Activity.this. (seems less buggy with my code)
While going back to a previously used activity, onCreate() is not called, only onResume(), so it might help if you move the following lines to there.
drawView = new DrawView(this);
setContentView(drawView);
Why use inner class at all? Activity is a class already. If you want to use the code multiple times, make it a class on its own.
This is what i use to navigate back. Maybe this is usefull for you 2!
Button backpressed = (Button) findViewById(R.id.btBack);
backpressed.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
onBackPressed();
}
});
The onBackPressed() is just a method that you can call if you want to return to the last page.
What it looks like for me:

Why Does My Surface View Class Stops Rendering When Main Activity is Busy?

I am implementing a simple render view class for Android extending SurfaceView. My main activity class includes an infinite while loop for other operations.
However, the app shows a black screen when main activity is in the infinite loop. As far as I know, main activity and surface view classes have their own separate threads, so surface view class should keep rendering even when main activity class is busy. The app works just fine when I prevent infinite loop by setting 'running' boolean variable false in the code.
What might be the reason of that surface view class stops rendering when my main activity is in an infinite loop?
My main activity and surface view classes are as follows:
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity{
RenderSurfaceView renderView;
public boolean running = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate ( savedInstanceState );
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
renderView = new RenderSurfaceView(this);
setContentView(renderView);
loop();
}
public void loop(){
running = true;
while(running){
}
}
protected void onResume() {
super.onResume();
renderView.resume();
}
protected void onPause() {
super.onPause();
renderView.pause();
}
}
This my render class that extends SurfaceView:
import java.util.Random;
import android.content.Context;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class RenderSurfaceView extends SurfaceView implements Runnable{
Thread renderThread = null;
SurfaceHolder holder;
volatile boolean running = false;
Random r = new Random();
public RenderSurfaceView(Context context) {
super(context);
holder = getHolder();
}
public void resume() {
running = true;
renderThread = new Thread(this);
renderThread.start();
}
public void run() {
while(running) {
if(!holder.getSurface().isValid())
continue;
render();
}
}
private void render(){
Canvas canvas = holder.lockCanvas();
canvas.drawRGB(r.nextInt(255), r.nextInt(255), r.nextInt(255));
holder.unlockCanvasAndPost(canvas);
}
public void pause() {
running = false;
while(true) {
try {
renderThread.join();
} catch (InterruptedException e) {
// retry
}
}
}
}
This
renderView = new RenderSurfaceView(this);
setContentView(renderView);
does not show the view immediately. Instead it has to go through a layout traversal first, which doesn't happen immediately but asynchronously to the Activity lifecycle methods, and it happens on the main thread. So running an infinite loop at the end of onCreate() prevents the layout traversal of the view hierarchy, which means your SurfaceView will never be shown.
In theory you could experiment with starting your infinite loop delayed using a Handler, for experimental purposes maybe. In general infinite loops on the main thread are a super bad idea and I can't see a reason why you would want to do this, apart from experiments maybe.
My self-answer to this question Modifying views in AsyncTask doInBackground() does not (always) throw exception has a bit more detail on what's going on behind the scenes. The question is not directly related to what you're asking here, but the background is the same I believe.

Android Threaded Canvas problems with Sensors

I'm having some problems with creating a SensorManager and outputting the values of the accelerometer on the screen with a green circle that moves when you tilt the phone. It's an example from Sam's teach yourself android game programming in 24 hours by Jonathan Harbour. I have checked and double checked and my code is identical to his. Basically, it's a Custom SurfaceHolder canvas that uses a Thread to repeatedly update. Here is the main activity, Game (yes, I know, the imports are messy):
package com.example.sensordemo;
import java.util.*;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.renderscript.Float2;
import android.renderscript.Float3;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
public class Game extends Activity implements SensorEventListener
{
DrawView drawView;
SensorManager sensors;
Sensor accel;
Float3 accelMotion = new Float3();
List<Sensor> sensorList;
#Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
drawView = new DrawView(this);
setContentView(drawView);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
sensors = (SensorManager) getSystemService(SENSOR_SERVICE);
accel = sensors.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorList = sensors.getSensorList(Sensor.TYPE_ALL);
Log.i("Game", "onCreate()");
}
#Override public void onSensorChanged(SensorEvent event)
{
switch (event.sensor.getType())
{
case Sensor.TYPE_ACCELEROMETER:
accelMotion.x = event.values[0];
accelMotion.y = event.values[1];
accelMotion.z = event.values[2];
break;
}
}
#Override public void onAccuracyChanged(Sensor arg0, int arg1)
{
}
#Override public void onResume()
{
super.onResume();
Log.i("Game", "resume");
drawView.resume();
sensors.registerListener(this, accel, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override public void onPause()
{
super.onPause();
drawView.pause();
sensors.unregisterListener(this);
Log.i("Game", "pause");
}
public class DrawView extends SurfaceView implements Runnable
{
Thread gameloop = null;
SurfaceHolder surface = null;
volatile boolean running = false;
AssetManager assets = null;
Paint paint = new Paint();
Float2 center;
public DrawView(Context context)
{
super(context);
surface = getHolder();
assets = context.getAssets();
Log.i("Game", "draw view made");
}
public void resume()
{
running = true;
gameloop = new Thread(this);
gameloop.start();
Log.i("Game", "drawview resumed");
}
public void pause()
{
running = false;
Log.i("DrawView", "pausing");
while(true)
{
try
{
gameloop.join();
}
catch(InterruptedException e)
{
}
}
}
#Override public void run()
{
while(running)
{
if(!surface.getSurface().isValid()) continue;
Canvas canvas = surface.lockCanvas();
canvas.drawColor(Color.BLACK);
Log.i("Game", "running");
float width = canvas.getWidth();
float height = canvas.getHeight();
center = new Float2(width / 2.0f, height / 2.0f);
Float2 ratio = new Float2(width / 10.0f, height / 10.0f);
float x = center.x - accelMotion.x * ratio.x;
float y = center.y + accelMotion.y * ratio.y;
paint.setColor(Color.GREEN);
canvas.drawCircle(x, y, 100, paint);
paint.setColor(Color.WHITE);
paint.setTextSize(24);
canvas.drawText("Accelerometer Demo", 10, 20, paint);
String s = "X=" + Math.round(accelMotion.x) + ", Y=" + accelMotion.y + ", Z=" + accelMotion.z;
canvas.drawText(s, 10, 50, paint);
canvas.drawText("Model: " + accel.getVendor(), 10, 80, paint);
surface.unlockCanvasAndPost(canvas);
try
{
Thread.sleep(20);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
I don't know what the problem is. The Screen goes all white and nothing happens (The thingy at the top with notifications is still visible). Also, it crashes the phone sometimes. It's a htc 1 M7 running android 5.0.1. This is the output from Logcat:
06-17 21:47:40.760: W/asset(14951): Copying FileAsset 0xb85d1e68 (zip:/data/app/com.example.sensordemo-1/base.apk:/resources.arsc) to buffer size 157568 to make it aligned.
06-17 21:47:40.770: E/Typeface(14951): SANS_LOC file not found.
06-17 21:47:40.790: I/Game(14951): draw view made
06-17 21:47:41.090: E/SensorManager(14951): uid = 10196
06-17 21:47:41.100: E/SensorManager(14951): uid = 10196
06-17 21:47:41.100: I/Game(14951): onCreate()
06-17 21:47:41.110: I/Game(14951): resume
06-17 21:47:41.110: I/Game(14951): drawview resumed
06-17 21:47:41.110: I/SensorManager(14951): registerListenerImpl: listener = com.example.sensordemo.Game#110bc638, sensor = {Sensor name="BOSCH BMA250 3-axis Accelerometer", vendor="BOSCH", version=1, type=1, maxRange=39.2266, resolution=0.038307227, power=0.1, minDelay=10000}, delay = 200000, handler = null
06-17 21:47:41.130: D/Atlas(14951): Validating map...
06-17 21:47:41.150: E/Typeface(14951): SANS_LOC file not found.
06-17 21:47:41.150: I/DrawView(14951): pausing
I would just like to note that pause is called straight away, not when I tab out of the app. Exiting and entering the app does not make the programme continue, it just stops. Nothing happens on the screen apart from it turning white, going into landscape mode and nothing else. Sorry for the long post, but I have no idea what is going on, and so I have tried to include all relevant sources. Any suggestions, ideas and, of course, any solutions you have please comment or reply below. All help is appreciated. If you require any other files I will add them, just tell me.
I'm working with the same book and had the same problem... ran across your question looking for an answer, so I thought I'd backtrack and pass along what I found (after MUCH pain and suffering lol)
The problem seems to be in the pause procedure (not the onPause, the pause in DrawView). I think it was that the thread was not properly stopped, and they tried to re-start the same thread again in the resume procedure.
After changing it to the following code, everything seems to work fine.
public void pause() {
gameloop.interrupt();
running = false;
try{
gameloop.join();
} catch (InterruptedException e) {}
}

Android App Not Working After Pressing Back Button

I created an Android app which draws a small image to the screen and exits if you tap in the bottom 50px of the screen. When I tap there, it exits fine, however, when I press the back (or home) button to exit the application, it exits, but then says, "Unfortunately, Game Development has stopped." I also checked LogCat, and it showed that a Java NullPointerException occurred each time it stopped working. The output from LogCat was:
10-30 01:13:28.280: E/AndroidRuntime(15294): FATAL EXCEPTION: Thread-1718
10-30 01:13:28.280: E/AndroidRuntime(15294): java.lang.NullPointerException
10-30 01:13:28.280: E/AndroidRuntime(15294): at tricrose.gamedev.GameView.onDraw(GameView.java:56)
10-30 01:13:28.280: E/AndroidRuntime(15294): at tricrose.gamedev.GameThread.run(GameThread.java:30)
I have three Java classes: Game.java (the Activity), GameView.java (the SurfaceView), and GameThread.java (the main Thread).
The code is as follows:
Game.java:
package tricrose.gamedev;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.Window;
import android.view.WindowManager;
public class Game extends Activity {
public static final String TAG = Game.class.getSimpleName();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new GameView(this));
Log.d(TAG, "View added");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_game, menu);
return true;
}
}
GameView.java:
package tricrose.gamedev;
import android.app.Activity;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
public static final String TAG = GameView.class.getSimpleName();
private GameThread thread;
public GameView(Context context) {
super(context);
getHolder().addCallback(this);
thread = new GameThread(getHolder(), this);
setFocusable(true);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (Exception e) {}
}
}
public boolean onTouchEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
if (e.getY() > getHeight() - 60) {
thread.setRunning(false);
((Activity)getContext()).finish();
}
}
return super.onTouchEvent(e);
}
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.droid_1), 10, 10, null);
}
}
GameThread.java:
package tricrose.gamedev;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
public class GameThread extends Thread {
public static final String TAG = GameThread.class.getSimpleName();
private boolean running;
private SurfaceHolder surfaceHolder;
private GameView gameView;
public GameThread(SurfaceHolder surfaceHolder, GameView gameView) {
super();
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
public void setRunning(boolean running) {
this.running = running;
}
public void run() {
Canvas canvas;
while (running) {
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
this.gameView.onDraw(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
I've searched all over the internet for a solution, and I cannot find one which has actually worked, so any help is hugely appreciated.
I finally managed to find the solution! It turned out that I should have checked if the canvas was null before calling onDraw, and if it was null, then quit the application by finishing the activity. I found the solution on JavaCodeGeeks.com, where I was looking at an Android tutorial. Thanks everyone for all your answers!
why you have doing drawing from two places from onDraw and GameThread run method. I think you need to remove onDraw do every drawing from GameThread rlun method only.
This is just a guess, but your app might still be drawing while the app is trying to exit. In you onDraw method you call this line of code (which is probably your cause of problem):
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.droid_1), 10, 10, null);
In that case you might not have resuorces available anymore ;)
You can try to:
1. Put a class private bitmap variable into your GameView Class:
private Bitmap mBitmap;
2. Load the bitmap in your GameView constructor:
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.droid_1);
3. Draw using the (already loaded) bitmap:
canvas.drawBitmap(mBitmap, 10, 10, null);
Hope that helps!

Categories