Can I switch activities from a SurfaceView? - android

I am trying to adapt a snake game in Android Studio so that game over triggers a GameOverActivity. The problem is that the game over text is currently written to the screen in the redrawCanvas() function:
public void redrawCanvas(Canvas canvas) {
if (this.snake.isEnabled()) {
this.snake.draw(canvas);
this.apple.draw(canvas);
this.score.draw(canvas);
} else {
Paint p = getPaint();
p.setTextSize(50);
p.setColor(Color.BLACK);
canvas.drawText("Game over!", 100, 100, p);
// LAUNCH PLAY AGAIN ACTIVITY HERE?
}
}
and this redrawCanvas code is contained in a class extending SurfaceView. I would ordinarily call an intent, but I can't call startActivity() from within a SurfaceView class. My SnakeGameActivity just sets the content view, and all the code occurs in this surface view. Here is the code in SnakeGameActivity:
public class SnakeGameActivity extends com.codepath.simplegame.GameActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//before activity is created : cold start
//switch back to original Theme (App Theme)
setTheme(R.style.AppTheme);
switchFullscreen();
setContentView(new SnakeGamePanel(this));
}
}
And here is the full code in SnakeGamePanel:
public class SnakeGamePanel extends AbstractGamePanel {
public SnakeGamePanel(Context context) {
super(context);
}
private SnakeActor snake;
private AppleActor apple ;
private ScoreBoard score;
private boolean isPaused = false;
#Override
public void onStart() {
this.snake = new SnakeActor(100, 100);
this.apple = new AppleActor(200, 50);
this.score = new ScoreBoard(this);
}
#Override
public void onTimer() {
if (!isPaused) {
if (this.snake.checkBoundsCollision(this)) {
this.snake.setEnabled(false);
}
this.snake.move();
if (this.apple.intersect(this.snake)) {
this.snake.grow();
this.score.earnPoints(50);
this.apple.reposition(this);
}
}
}
#Override
public void redrawCanvas(Canvas canvas) {
if (this.snake.isEnabled()) {
this.snake.draw(canvas);
this.apple.draw(canvas);
this.score.draw(canvas);
} else {
Paint p = getPaint();
p.setTextSize(50);
p.setColor(Color.BLACK);
canvas.drawText("Game over!", 100, 100, p);
// LAUNCH PLAY AGAIN ACTIVITY?
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
this.snake.handleKeyInput(keyCode);
if (keyCode == KeyEvent.KEYCODE_G) {
this.onStart();
}
if (keyCode == KeyEvent.KEYCODE_P) {
isPaused = !isPaused;
}
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
this.snake.handleTouchInput(event);
return true;
}
return false;
}
}
Is there any way to start an activity when the game ends?

One alternative to your inner class solution is to pass a Context object to the constructor of your SurfaceView subclass. Then you can call startActivity() on that context.

I found what seems to be a solution so far. I just made the SurfaceView extension class be an inner class in my activity. That way, it has an activity as a containing class.

Related

libgdx firing event from actor to call another actor

I'm using Scene2D and have 2 actors. When MyActor1 is touched, it should fire an event which should be handled by MyActor2. I've tried several different variants on how to fire and even from MyActor1, but it is still not being handled by MyActor2. The full code is below.
Game class which just forwards to a Screen
public class MyGdxGame extends Game {
#Override
public void create() {
setScreen(new MyGdxScreen());
}
}
Screen class (empty methods ommited)
public class MyGdxScreen implements Screen {
private Stage stage;
#Override
public void show() {
stage = new Stage(new ScreenViewport());
stage.addActor(new MyActor1());
stage.addActor(new MyActor2());
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
#Override
public void dispose() {
stage.dispose();
}
}
First Actor (fires custom event when touched)
public class MyActor1 extends Actor {
private Texture image;
public MyActor1() {
this.image = new Texture("badlogic.jpg");
setPosition(0, 0);
setBounds(0, 0, image.getWidth(), image.getHeight());
setTouchable(Touchable.enabled);
addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log("touched", "First actor touched!");
MyActor1.this.fire(new ActorTouchedListener.ActorTouchedEvent());
MyActor1.this.getParent().fire(new ActorTouchedListener.ActorTouchedEvent());
return true;
}
});
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(image, 0, 0, image.getWidth(), image.getHeight());
}
}
Second Actor (should handle an Event fired by Actor1)
public class MyActor2 extends Actor {
public MyActor2() {
addListener(new ActorTouchedListener());
}
}
Event listener
public class ActorTouchedListener implements EventListener {
#Override
public boolean handle(Event event) {
if (event instanceof ActorTouchedEvent) {
Gdx.app.log("fired", "Second actor got event!");
return true;
}
return false;
}
public static class ActorTouchedEvent extends Event {
public ActorTouchedEvent() {
}
}
}
These two lines do nothing.
MyActor1.this.fire(new ActorTouchedListener.ActorTouchedEvent());
MyActor1.this.getParent().fire(new ActorTouchedListener.ActorTouchedEvent());
To call Actor from another Actor you should create instance of it.
public class MyActor1 extends Actor {
private Texture image;
private Actor actor2; // instance of MyActor2
public MyActor1(actor2: Actor) {
this.image = new Texture("badlogic.jpg");
this.actor2 = actor2;
setPosition(0, 0);
setBounds(0, 0, image.getWidth(), image.getHeight());
setTouchable(Touchable.enabled);
addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.log("touched", "First actor touched!");
actor2.fire(event); // Here we fire event on MyActor2
return true;
}
});
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(image, 0, 0, image.getWidth(), image.getHeight());
}
}
Intercomponent events
LibGDX does not provide embeded intercomponent event system, but you can use additional frameworks. For example LibGDX Autumn. You can use two type events:
text event - to notify components about changing of some state
object event - this mechanism deliver your event object to observers
This framework have documentaion and examples, it will help to do what you want
Delayed actor update
For actor's update purposes LibGDX have actions mechanism. In your case I suggest to attach DelayAction to MyActor2 with wrapped MyRedrawAction
DelayAction delayAction = new DelayAction();
// set wrapped action dellay (in sec.)
delayAction.setDuration(1F);
// set wrapped action. It will be invoked after 1 sec. of stage drawing
delayAction.setAction(new MyRedrawAction());
actor2.addAction(delayAction);
You can do this in touchDown() method as #icarumbas suggest

livewallpaper Double Tap to open an activity

I am new in android development.
I want to implement a double tap event in my wallpaper so that when I double tap the wallpaper it opens the application.
here is the code where I have drawn the wallpaper image:
public class AbcService extends WallpaperService
{
public void onCreate()
{
super.onCreate();
}
public void onDestroy()
{
super.onDestroy();
}
public Engine onCreateEngine()
{
return new wallpaperEngine();
}
class wallpaperEngine extends Engine
{
public Bitmap image1;
wallpaperEngine()
{
image1 = BitmapFactory.decodeResource(getResources(), R.drawable.img_3);
}
public void onCreate(SurfaceHolder surfaceHolder)
{
super.onCreate(surfaceHolder);
}
public void onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels)
{
drawFrame();
}
void drawFrame()
{
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try
{
c = holder.lockCanvas();
if (c != null)
{
c.drawBitmap(image1, 0, 0, null);
}
} finally
{
if (c != null) holder.unlockCanvasAndPost(c);
}
}
}
what to do now??
am i on right track?
Thank You in Advance..
For a double Tap Action in a Live Wallpaper you can subclass a GestureDetector.SimpleOnGestureListener like this :
class wallpaperEngine extends Engine {
...
...
private GestureDetector gestureListener;
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onDoubleTap(MotionEvent e) {
//start intent/application here
return super.onDoubleTap(e);
}
}
Then create an Instance of that class, f.e. in your onCreate(SurfaceHolder surfaceHolder) Method and enable touch events in your Live Wallpaper :
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
gestureListener = new GestureDetector(getApplicationContext(), new GestureListener());
setTouchEventsEnabled(true);
}
Then #Override onTouchEvent :
#Override
public void onTouchEvent(MotionEvent event) {
gestureListener.onTouchEvent(event);
}
Thats the easiest way I can think of.

Android app does not get into onTouch method

I'm having troubles with the method onTouch(). The code I'm using to implement it is the following.
public class EjemploView extends View implements OnTouchListener {
...
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getActionMasked()==0) {
slider2.setCenX(slider2.getCenX() - 1);
}
return false;
}
}
The point is that I set a debugger stop inside onTouch method and even if I touch the screen it will never go inside it. What am I doing wrong?
Adding as an answer, simply so that it makes more sense.
You are implementing onTouchListener but you dont set it as your onTouchListener. Thus you are left with a fully functioning onTouchListener in your view that does not ever get called.
public EjemploView(Context context) {
super(context);
/** \/ Add this line right here \/ */
setOnTouchListener(this);
Resources res = context.getResources();
drawableFader = res.getDrawable(R.drawable.fader);
drawableSlider=res.getDrawable(R.drawable.slider);
fader1 = new Grafico(this, drawableFader);
fader2 = new Grafico(this, drawableFader);
slider1 = new Grafico(this, drawableSlider);
slider2 = new Grafico(this, drawableSlider);
fader1.setAlto(350);
fader1.setAncho(64);
fader2.setAlto(350);
fader2.setAncho(64);
fader2.setAngulo(90);
slider1.setAlto(90);
slider1.setAncho(55);
slider2.setAlto(90);
slider2.setAncho(55);
slider2.setAngulo(90);
}
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
setContentView(new EjemploView(this));
That's the place where I instantiate the view
public class EjemploView extends View implements OnTouchListener {
private Drawable drawableFader, drawableSlider;
private Grafico fader1, fader2, slider1, slider2;
public EjemploView(Context context) {
super(context);
Resources res = context.getResources();
drawableFader = res.getDrawable(R.drawable.fader);
drawableSlider=res.getDrawable(R.drawable.slider);
fader1 = new Grafico(this, drawableFader);
fader2 = new Grafico(this, drawableFader);
slider1 = new Grafico(this, drawableSlider);
slider2 = new Grafico(this, drawableSlider);
fader1.setAlto(350);
fader1.setAncho(64);
fader2.setAlto(350);
fader2.setAncho(64);
fader2.setAngulo(90);
slider1.setAlto(90);
slider1.setAncho(55);
slider2.setAlto(90);
slider2.setAncho(55);
slider2.setAngulo(90);
}
#Override
protected void onSizeChanged(int ancho, int alto, int ancho_anter, int alto_anter) {
super.onSizeChanged(ancho, alto, ancho_anter, alto_anter);
// Una vez que conocemos nuestro ancho y alto.
fader1.setCenX(ancho / 4 - 31);
fader1.setCenY(alto / 4 - 50);
slider1.setCenX(ancho / 4 - 28);
slider1.setCenY(alto / 4 - 50);
fader2.setCenX(ancho - 271);
fader2.setCenY(alto / 4 - 50);
slider2.setCenX(ancho - 277);
slider2.setCenY(alto / 4 - 50);
}
#Override
synchronized protected void onDraw(Canvas canvas) {
fader1.dibujaGrafico(canvas);
fader2.dibujaGrafico(canvas);
slider1.dibujaGrafico(canvas);
slider2.dibujaGrafico(canvas);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("TAG", "I've reached this point");
if(event.getActionMasked()==0) {
slider2.setCenX(slider2.getCenX() - 2);
}
return false;
}
}
And that's my whole code for the view. It is displaying it because I can see the drawables.
You are implementing a new interface, but you should use the already defined onTouchEvent(MotionEvent event). Just override it.
class CustomImageView extends ImageView {
...
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
Log.d(TAG, String.valueOf(action));
return false;
}
}

Refreshing canvas?

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!

Can I make a Button in SurfaceView?

I have created a class like this:
public class nir extends Activity implements OnClickListener{
/** Called when the activity is first created. */
ImageButton btnNew, btnPlay, btnSignUp;
TextView txtTitle;
Typeface font;
//sprite
SurfaceView pet;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
font = Typeface.createFromAsset(getAssets(), "fonts/aaaiight.ttf");
btnNew = (ImageButton)findViewById(R.id.btnNew);
btnNew.setOnClickListener(this);
btnPlay = (ImageButton)findViewById(R.id.btnPlay);
btnPlay.setOnClickListener(this);
txtTitle = (TextView)findViewById(R.id.lblTama);
txtTitle.setTypeface(font);
//sprite
//setContentView(new MainGamePanel(this));
}
}
Now I want to make that SurfaceView show any sprite. I've tried
<view class="Tamagotchi.nir.MainGamePanel...">
in my XML, but it gives me a "Force Close" dialog.
I also have this class:
public class MainGamePanel extends SurfaceView implements
SurfaceHolder.Callback {
private static final String TAG = MainGamePanel.class.getSimpleName();
private MainThread thread;
private Sprite elaine;
// the fps to be displayed
private String avgFps;
public void setAvgFps(String avgFps) {
this.avgFps = avgFps;
}
public MainGamePanel(Context context) {
super(context);
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// create Elaine and load bitmap
elaine = new Sprite(
BitmapFactory.decodeResource(getResources(),
R.drawable.walk_elaine),
10, 50, // initial position
30, 47, // width and height of sprite
5, 5); // FPS and number of frames in the animation
// create the game loop thread
thread = new MainThread(getHolder(), this);
// make the GamePanel focusable so it can handle events
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and
// we can safely start the game loop
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// handle touch
}
return true;
}
public void render(Canvas canvas) {
canvas.drawColor(Color.BLACK);
elaine.draw(canvas);
// display fps
displayFps(canvas, avgFps);
}
/**
* This is the game update method. It iterates through all the objects
* and calls their update method if they have one or calls specific
* engine's update method.
*/
public void update() {
elaine.update(System.currentTimeMillis());
}
private void displayFps(Canvas canvas, String fps) {
if (canvas != null && fps != null) {
Paint paint = new Paint();
paint.setARGB(255, 255, 255, 255);
canvas.drawText(fps, this.getWidth() - 50, 20, paint);
}
}
}
What is the reason for the crash?
Take out view class =
in front of Your package name all you need is the package name.YourSurfaceViewClass
This is how your code should look
<Tamagotchi.nir.MainGamePanel.YourSurfaceViewClassName
android:id="#+id/myView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>

Categories

Resources