Android Games

Android Game Development – Displaying Images with Android

Before moving to the actual game loop let’s display some graphics so we can get some measurements done. If you haven’t checked it out please do as it is imperative that you understand how a thread updates the screen. You can check it out here.

Displaying an image using Android is extremely simple.
To scale the problem down we will just display the image in the top left corner. We need an image to display. I prefer .png formats and I have just created one named droid_1.png. The size of the image is 20×20 pixels. You can use whatever tool you like. I use Gimp or Photoshop.

To make it available for your application just copy the image into the /res/drawable-mdpi directory (the easiest way is to drag and drop it there).
I have chosen mdpi which stands for normal screen medium density. To read on screen types check the android documentation.

Modify the MainGamePanel.java class and change the onDraw(Canvas canvas) method to look like this:

protected void onDraw(Canvas canvas) {
 canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.droid_1), 10, 10, null);
}

The drawBitmap method draws the droid_1 image to the coordinates 10,10.
We obtain the bitmap from the application resources by passing the id of our image (resource), which is droid_1 in our case. When we copied the droid_1.png into the resource directory eclipse detected it and the plugin executed the necessary scripts in the background so we have the droid_1 identifier in the R.java file. The R.java holds the resource identifiers.

The thread suffered some changes too. Examine the new run() method.

public void run() {
  Canvas canvas;
  Log.d(TAG, "Starting game loop");
  while (running) {
   canvas = null;
   // try locking the canvas for exclusive pixel editing on the surface
   try {
    canvas = this.surfaceHolder.lockCanvas();
    synchronized (surfaceHolder) {
     // update game state
     // draws the canvas on the panel
     this.gamePanel.onDraw(canvas);
    }
   } finally {
    // in case of an exception the surface is not left in
    // an inconsistent state
    if (canvas != null) {
     surfaceHolder.unlockCanvasAndPost(canvas);
    }
   } // end finally
  }
 }

In line 02 we declare the canvas on which we will draw our image. The canvas is the surface’s bitmap onto which we can draw and we can edit its pixels. In line 08 we try to get hold of it and in line 12 we trigger the panel’s onDraw event to which we pass the obtained canvas. Note that it is a synchronised block which means that we have exclusivity on it and nothing can modify it while we are using it.
The functionality is very simple and basic. On every execution of the game loop we get hold of the canvas and we pass it to the game panel to draw on it. The game panel just displays the image at coordinates 10,10. Now back to the FPS. If the number of times per second the image is displayed drops below 20, it starts to get noticeable by us humans. The challenge is to keep this rate above a set level and we will see how shortly.

Try running the code and you should see our droid displayed close to the top left corner.

Droid in top left corner

Moving the Image

Now that we have displayed it, let’s try moving it. How? We will use our finger. We will implement a simple drag and drop functionality. To pick up an image we will simply touch it and while our finger is on the screen we will update the image’s coordinates accordingly. Once the touch is finished we leave the image there where the last touch was recorded.

We need to create an object that will hold our image and the coordinates.
I have created Droid.java for this. Note that I have put the class into the net.obviam.droidz.model package.

package net.obviam.droidz.model;

import android.graphics.Bitmap;

public class Droid {

 private Bitmap bitmap; // the actual bitmap
 private int x;   // the X coordinate
 private int y;   // the Y coordinate

 public Droid(Bitmap bitmap, int x, int y) {
  this.bitmap = bitmap;
  this.x = x;
  this.y = y;
 }

 public Bitmap getBitmap() {
  return bitmap;
 }
 public void setBitmap(Bitmap bitmap) {
  this.bitmap = bitmap;
 }
 public int getX() {
  return x;
 }
 public void setX(int x) {
  this.x = x;
 }
 public int getY() {
  return y;
 }
 public void setY(int y) {
  this.y = y;
 }
}

It is a plain class with some attributes and a constructor.
The x and y are the coordinates of the droid. The bitmap holds the image that will be displayed as the droid. It is the graphical representation of it.

So far nothing special. But to play around with it we need to add some state. To keep it simple, the droid has only 2 states. Touched and untouched. By touched I mean when our finger touched the droid on the screen. We keep the touched state true while we hold our finger down on the screen at the droid’s position. Otherwise the droid remains untouched (state set to false).

Check out the new droid class.

package net.obviam.droidz.model;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.MotionEvent;

public class Droid {

 private Bitmap bitmap; // the actual bitmap
 private int x;   // the X coordinate
 private int y;   // the Y coordinate
 private boolean touched; // if droid is touched/picked up

 public Droid(Bitmap bitmap, int x, int y) {
  this.bitmap = bitmap;
  this.x = x;
  this.y = y;
 }

 public Bitmap getBitmap() {
  return bitmap;
 }
 public void setBitmap(Bitmap bitmap) {
  this.bitmap = bitmap;
 }
 public int getX() {
  return x;
 }
 public void setX(int x) {
  this.x = x;
 }
 public int getY() {
  return y;
 }
 public void setY(int y) {
  this.y = y;
 }

 public boolean isTouched() {
  return touched;
 }

 public void setTouched(boolean touched) {
  this.touched = touched;
 }

 public void draw(Canvas canvas) {
  canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2), y - (bitmap.getHeight() / 2), null);
 }

 public void handleActionDown(int eventX, int eventY) {
  if (eventX >= (x - bitmap.getWidth() / 2) && (eventX <= (x + bitmap.getWidth()/2))) {
   if (eventY >= (y - bitmap.getHeight() / 2) && (y <= (y + bitmap.getHeight() / 2))) {
    // droid touched
    setTouched(true);
   } else {
    setTouched(false);
   }
  } else {
   setTouched(false);
  }

 }
}

We added the touched field to keep track of the state of our droid. You will notice two more methods: public void draw(Canvas canvas) and public void handleActionDown(int eventX, int eventY).
These methods are discussed later.
Now let’s have a look at the MainGamePanel.java. It changed quite a lot.

package net.obviam.droidz;

import net.obviam.droidz.model.Droid;
import android.app.Activity;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainGamePanel extends SurfaceView implements
  SurfaceHolder.Callback {

 private static final String TAG = MainGamePanel.class.getSimpleName();

 private MainThread thread;
 private Droid droid;

 public MainGamePanel(Context context) {
  super(context);
  // adding the callback (this) to the surface holder to intercept events
  getHolder().addCallback(this);

  // create droid and load bitmap
  droid = new Droid(BitmapFactory.decodeResource(getResources(), R.drawable.droid_1), 50, 50);

  // 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) {
   // delegating event handling to the droid
   droid.handleActionDown((int)event.getX(), (int)event.getY());

   // check if in the lower part of the screen we exit
   if (event.getY() > getHeight() - 50) {
    thread.setRunning(false);
    ((Activity)getContext()).finish();
   } else {
    Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY());
   }
  } if (event.getAction() == MotionEvent.ACTION_MOVE) {
   // the gestures
   if (droid.isTouched()) {
    // the droid was picked up and is being dragged
    droid.setX((int)event.getX());
    droid.setY((int)event.getY());
   }
  } if (event.getAction() == MotionEvent.ACTION_UP) {
   // touch was released
   if (droid.isTouched()) {
    droid.setTouched(false);
   }
  }
  return true;
 }

 @Override
 protected void onDraw(Canvas canvas) {
  // fills the canvas with black
  canvas.drawColor(Color.BLACK);
  droid.draw(canvas);
 }
}

Line 28 creates the droid object at the the coordinates 50,50.
It is declared as an attribute in line 20.

In the onTouchEvent (method line 71) if the action is the touch of the screen (MotionEvent.ACTION_DOWN) we want to know if our finger landed on the droid. To do this is easy. We need to check if the event’s coordinates are inside the droid’s bitmap. In order not to clutter the onTouch event we just delegate this to the droid object. Now you can go back to the Droid.java class and check the handleActionDown method.

public void handleActionDown(int eventX, int eventY) {
  if (eventX >= (x - bitmap.getWidth() / 2) && (eventX <= (x + bitmap.getWidth()/2))) {
   if (eventY >= (y - bitmap.getHeight() / 2) && (y <= (y + bitmap.getHeight() / 2))) {
    // droid touched
    setTouched(true);
   } else {
    setTouched(false);
   }
  } else {
   setTouched(false);
  }

 }

It is very simple. If the action happened inside the area of our droid’s bitmap we’ll set its touched status to true

Going back to the onTouched method, notice that there is a code block (lines 8186) that is executed when the event is of type MotionEvent.ACTION_MOVE. This event happens when our finger started to move on the screen. It is simple. We check if the droid is touched and if so we just update its coordinates accordingly.
That is it. This was the update game state. We have updated the state of our sole object in the game. The touched state and the coordinates. Now it is time to display it.

Check the onDraw method. This is triggered at every execution of the main loop inside the thread remember?
It contains 2 lines. Line 99 simply fills the canvas with black and line 100 tells the droid to draw itself on the canvas provided. By doing this is like me giving you a paper to draw yourself onto it, and give the paper back to me so I can continue my drawing.

Check Droid.java for the draw implementation. It is dead simple. It takes the bitmap it was instantiated with and draws it to the canvas at the coordinates the droid is at in that moment. Note that the coordinates of the droid are exactly in the center of the bitmap so we need to move the pointer to the top left corner of the image and draw it there. I’m sure you won’t find it hard to understand.

That’s it. Run it and have a play with it.

Download he code for the project here (droidz.android.02.3-graphics.tar.gz)

Reference: Displaying Images with Android from our JCG partner Tamas Jano from “Against The Grain” blog.

Do not forget to check out our new Android Game ArkDroid (screenshots below). You feedback will be more than helpful!
Related Articles:
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

28 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Ryan Durel
12 years ago

So I have everything almost identical to what you’ve given, but I can’t actually get the droid to move. It recognizes it’s being pressed, but ACTION_MOVE never seems to happen or something, the if statement always returns false.

The problem seems to be that once the event is ACTION_DOWN, it doesn’t change.

Carlos Palmerox
12 years ago
Reply to  Ryan Durel

yea I have the same problem and I don’t know how to figure it out. at android documents said they recomend to use .getActionMasked but it’s the same outcome it just don’t work and I don’t really know why you stop the thread at that part I quit it with out problems.

Carlos Palmerox
12 years ago
Reply to  Ryan Durel

fixed just don’t use the super function as return just return true :D

Sebastián Moncho
Sebastián Moncho
11 years ago
Reply to  Ryan Durel

if you look the imports from the droid class, there’s a warning on “import android.view.MotionEvent”, because there’s no action listener to handler motion_down on this class. So it’s impossible to have getTouched()=true. It will be always false… handleActionDown will be NEVER called.

I’ve solved it by writting “droid.handleActionDown((int)event.getX(), (int)event.getY());” the MainGamePanel class, at the method onTouchEvent when analysing the ACTION_DOWN event. Just like that:

if (event.getAction() == MotionEvent.ACTION_DOWN)
{
if (event.getY() > getHeight() – 50)
{
thread.setRunning(false);
((MainActivity)getContext()).finish();
}
else
{
droid.handleActionDown((int)event.getX(), (int)event.getY());
Log.d(TAG, “Coords: x=” + event.getX() + “,y=” + event.getY());
}
}

Cheers, Sebas.

stri8ed
stri8ed
12 years ago

Why dont you do the drawing directly in the thread? What is the purpose of delegating it to the game panel?

Kees Koenen
Kees Koenen
12 years ago

Great tutorial! Actually building a new game, based on what I learned. Got one question, though:

How would I create multiple instances of the droid? I’m stuck at something like :

ArrayList droidList = new ArrayList();int nDroids = 5;for (int i=0; i<5; i++)
      droidList.add(new droid();

… how would you build the code for drawing and checking movement on droids 0 through 5 ?

I'm thinking the answer is somewhere in your other post (http://www.javacodegeeks.com/2011/08/android-game-development-design-in-game.html).. but I can't find it.

scott Pilgrim
scott Pilgrim
11 years ago

Thanks for the tutorial, I’m just starting with java and android. Two days ago it looked rather complicated changes of C / C + + to Java, but rather the code you have made it very easy to understand.

Brian Sexton
Brian Sexton
11 years ago

Thank you for the tutorials, Tamas Jano and Java Code Geeks.

I have a small, but important correction: those @Override directives should not be there for the surfaceChanged, surfaceCreated, and surfaceDestroyed methods because those methods are not overriding superclass methods. Rather, they are implementations for SurfaceHolder.Callback.

Luke Voelk
11 years ago

There is an error in Droid.java handleActionDown(int eventX, int eventY)

The first two lines read

if(eventX >= (x-bitmap.getWidth()/2) && (eventX = (y-bitmap.getHeight()/2) && (y= (x-bitmap.getWidth()/2) && (eventX = (y-bitmap.getHeight()/2) && (eventY<= (y+bitmap.getHeight()/2))){

The first one allows you to click directly below the image and drag it
around the second actually forces drag and drop.

Harold Swets
Harold Swets
11 years ago

I don’t know if everyone is still having issues on this stage with the ACTION_MOVE. I had the same issues many others did where I could get ACTION_DOWN and ACTION_UP to work fine. I also tested to ensure that droid.isTouched() returned true. I reviewed the code many times ensured I was returning true and not using super function I even tried ACTION_MASK. These all gave me the same results. I then decided to try changing the if statements in the onTouchEvent function to a switch. After doing this Everything works perfectly.

Paul Mallon
Paul Mallon
11 years ago
Reply to  Harold Swets

Thanks for this! changed the if statement to a switch statement and everything is working fine!

Ricky
9 years ago
Reply to  Harold Swets

For me the problem was my auto-generated method ended with ‘return super.onTouchEvent(event)’.
I changed it to this structure

@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);

// other code

return true;
}

Pete Nixon
Pete Nixon
11 years ago

You’ve copied and pasted the line numbers as well. Try typing it all out fresh and run it then.

Shai Kerer
11 years ago

How does MainThread access the gamePanel.onDraw(…) method ?!?
Its declared as protected!

Nick Behrens
11 years ago

I don’t understand how I can follow these things step-by-step and still have the goddamn thing fail to load and fill with errors every time. Hell, I can’t even make the Android 2.2 version on my Eclipse. Lowest I can go i 4.0, and this seems to fill in a bunch of other crap that these tutorials don’t cover. This is a disaster. I have no errors, should have my image display, and it simply does not work.

Zee
Zee
11 years ago

Hello Everyone :) Great tutorial so far :D. I found the same problem with ACTION_DOWN etc. NOT causing the droid to move. After examining the code carefully, I found that the other if statements were contained with in the if (event.getAction() == MotionEvent.ACTION_DOWN) block. Just move the if (ACTION_MOVE) and If(ACTION_UP) statements to be outside of it.

hassan
hassan
11 years ago

when i add canvas to draw my FPS goes down, very down (10) but when i delete the part below it works fine what should i do? tnx canvas = null; 06 // try locking the canvas for exclusive pixel editing on the surface 07 try { 08 canvas = this.surfaceHolder.lockCanvas(); 09 synchronized (surfaceHolder) { 10 // update game state 11 // draws the canvas on the panel 12 this.gamePanel.onDraw(canvas); 13 } 14 } finally { 15 // in case of an exception the surface is not left in 16 // an inconsistent state 17 if (canvas != null) {… Read more »

Mou Justice Beaver
11 years ago

can anyone tell me please what are the getY() and getX() methods in the droid class for? my understanding of constructors is not so good I see.

aksesuar
11 years ago

Thanks for the explanation

Radoslav
Radoslav
10 years ago

I added the code and in the MainThread i get an error at this row this.gamePanel.onDraw(canvas); in the run() method
saying “Suspicious method call;”
if I add @SuppressLint(“WrongCall”) at the top of the run() method it the code works BUT when I press back button to exit the application I get following error:

AndroidRuntime(30712): java.lang.NullPointerException
AndroidRuntime(30712): at com.example.droidz.MainGamePanel.onDraw(MainGamePanel.java:66)
AndroidRuntime(30712): at com.example.galahan.MainThread.run(MainThread.java:40)

Stefano
Stefano
10 years ago
Reply to  Radoslav

Hello,
have you solved the “onDraw” problem?
Could you explain to me of to use “@SuppressLint(“WrongCall”)”.
I put it just before the @Override command of the function run, but I can’t run the program.

Multiple markers at this line
– SuppressLint cannot be resolved to a type
– The attribute value is undefined for the annotation type

Thanks

ATiminey
ATiminey
10 years ago
Reply to  Stefano

Try adding this import to your file.

import android.annotation.SuppressLint;

Ricky
9 years ago
Reply to  Radoslav

I changed the method to draw instead of onDraw.

@Override
public void draw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
droid.draw(canvas);
}

palas
palas
10 years ago

when draw bitmap to new position previous image remains. how to solve it?

Carl
Carl
10 years ago
Reply to  palas

@palas I think you need to add ‘this.invalidate()’ after you set the new coordinates of the droid, like this:

droid.setX((int)event.getX());
droid.setY((int)event.getY());
this.invalidate();

snjus
snjus
9 years ago

Hi I m getting null pointer exception my doubt is— in your code you are creating mainThread without passing anything in your constructor but in MainThread.java no such constructor is there so we must pass surfacePanel and MainGamePanel object from MainGamePanel.java
at line
thread = new MainThread();

how to pass those two objects from there ?? kindly reply ASAP

Bob
Bob
9 years ago

At first, I got an error saying that calling onDraw directly was a “suspicious call” (which I agree with — it’s clearly an event handler, so it should only be called by the underlying framework when the event it’s handling occurs). I changed MainThread to call gamePanel.draw() instead of gamePanel.onDraw() which made the error go away but then even though the event was triggering and calling onDraw() (which I verified via logging) I wasn’t actually seeing anything on the screen — it was just black. After going through another SurfaceView tutorial elsewhere I found that changing this to not override… Read more »

Bobba
Bobba
8 years ago

Hey,
Could somebody tell me how to do this with say a circle drawn in onDraw method?
Thanks!

Back to top button