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 81-86) 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)

To see how you could move the image around check the next entry.

LinkedInRedditTumblrDiggTechnorati FavoritesShare

37 Responses - Add Yours+

  1. shipi says:

    God bless you man . super motivation. super code. may god bless you with lot of success in life.

  2. CSEnginerr13 says:

    Hi! I’m having trouble getting this code to run. I have one error, and it is:

    In MainThread during the Game Loop

    this.gamePanel.onDraw(canvas);

    is throwing the error: Suspicious method call; should probably call draw rather than onDraw.

    Now… The way I read the code, this method should be calling the onDraw override used in MainGamePanel.java; I have tried suppressing the error, but either way my program exits when it reaches the call (verified through tracing).

    Any ideas? :/

  3. Gaurav says:

    Just wondering if the canvas scaling the bitmap results in skewed height and width for bitmaps?

  4. nguyenhoai says:

    The article is very good. But i think you better say about the line:
    093 return true; //You changed this, but a lots people did not notice. So they cant catch event MOVE and UP (i took for a while to notice this).

    Anyways i love your site.
    Thanks.

  5. Mete says:

    Great tutorial! Very useful to me, thanks :)

  6. Christian says:

    Where does he indicate which activity it will be created on. I have several activities as a result of a menu before the game, so if i wanna create the game in a specific activity, where do i indicate that?

  7. You have bad logic :D issue in next portion of code:

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

    }

    because bitmap of a droid start from top left corner (0,0) to (width,height). Otherwise great tutorial! :)

  8. Jeth says:

    Hey, excellent tutorial really helped me. How about limiting the movement of the object let’s say just left and right only? Thanks!

  9. Amer says:

    Adding my thanks too. Excellent tutorials.

  10. mang0es says:

    Thanks for sharing the code!

    I am trying to use a grid system as a background for this app. However, when I placed the image file of the grid system (png) in the main.xml, the onDraw in MainGamePanel overrides the background. When I replaced “TRANSPARENT” with “BLACK”, it shows the entire path of the image’s movement across the screen. How do I have the grid system show on the app?

  11. Sam Dickinson says:

    Many thanks for these superb tutorials. As a noob to Android development, these tutorials have been a great starter for a SurfaceView based apps. They give a great sense of satisfaction as each step runs with tangible results.

  12. Ben says:

    You need to change the return statement probably. I had this issue because in a moment of stupidity I forgot to change the return in the method onTouchEvent(MotionEvent e)

  13. Ichiro Ino says:

    Hello, I copied your code for my learning tests.

    I can’t seem to make if (event.getAction() == MotionEvent.ACTION_MOVE) work, I could only so far detect ACTION_DOWN.

    I’ve tried logging the events, but it’s always showing up action=0 which is ACTION_DOWN

    Is this a bug? I’m using API8 and I’ve tested it on a Phone

    • Impaler says:

      There must be something wrong as how you implemented the onTouchEvent as the code works correctly on my device with that API.
      Have you downloaded the project and tried it out?

    • Adil says:

      Same problem for me. Down events only being detected, not up or move events…

  14. Parth Sane says:

    Awesome Dude!!!

  15. Jon says:

    Amazing guide man, really helping me out a lot. Noticed a very small typo in the handleActionDown. When checking if within the y coordinates of the droid you use y instead of eventY, causing touched to be set to true if you touch anywhere directly below the droid.

    Instead of:
    if (eventY >= (y – bitmap.getHeight() / 2) && (y = (y – bitmap.getHeight() / 2) && (eventY <= (y + bitmap.getHeight() / 2)))

    Thanks again for the amazing guide.

  16. Jon says:

    Amazing guide man, really helping me out a lot. Noticed a very small typo in the handleActionDown. When checking if within the y coordinates of the droid you use y instead of eventY, causing touched to be set to true if you touch anywhere directly below the droid.

    Instead of:
    if (eventY >= (y – bitmap.getHeight() / 2) && (y = (y – bitmap.getHeight() / 2) && (eventY <= (y + bitmap.getHeight() / 2)))

  17. Gabriel says:

    Amazing man, you really are a awsome tutor. You’re helping me translate my knowledge in C# XNA Android, maybe make a port? or something very basic for whom is experienced in xna but is new to java and android.

    Anyway i would like to try and contribuite maybe help making a small article about how to make joystick controller for games.

  18. neethu says:

    I want to distroy the image when it reach on the bottom.what can i do?reply please…….

  19. louis says:

    How do you stop the thread with the home key or the back key?

  20. tek911 says:

    Good stuff man. Im just making the jump to android and this made me have like 2 complete “Aha” moments.

    My target is an honeycomb tablet, and when i run the example on it, there is a rectangular draw area that the bitmap of the image the user drags is viewable. I’ve gone through the code tinkering and changing values to see if i can grow (what i can only think from my beginner understanding) is the canvas. It looks like the size of a cell phone so i’m thinking there is a height and width value somewhere i’m not tweaking to take advantage of the larger screen. I’ve gone through everything i can see in the code. Any ideas?

    • tek911 says:

      Nevermind, found it. it was the target and min api levels, i put those in a 4 for minimum and 11 for target and bam. whole screen is usable. thanks again for the excellent start on dragging bitmaps.

      • Impaler says:

        Thanks for writing it up. It might be useful for others too.

        • Droid David says:

          I am no expert but I would like to contribute the following based upon this discussion. I have also had problems obtaining the proper device dimensions in Android and have come up with the following solution:
          1. Create two public static ints for width and height.
          ex: public static int GAMEWIDTH, GAMEHEIGHT;

          2. Assign the device’s dimensions to them.
          ex: GAMEWIDTH = getWindow().getWindowManager().getDefaultDisplay().getWidth();
          GAMEHEIGHT = getWindow().getWindowManager().getDefaultDisplay().getHeight();

          As you can see, I have done this from the primary activity insuring the values get populated prior to loading any other application specific items. Afterward, whenever I need to refer to the device’s width or height, I always have available each of those variables. I hope this helps people.

          Thank you for these wonderful tutorial. Believe it or not, they really make a significant difference.

          Full MainActivity code below:

          public static int GAMEWIDTH, GAMEHEIGHT;

          @Override
          public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);

          setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
          getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
          requestWindowFeature(Window.FEATURE_NO_TITLE);

          GAMEWIDTH = getWindow().getWindowManager().getDefaultDisplay().getWidth();
          GAMEHEIGHT = getWindow().getWindowManager().getDefaultDisplay().getHeight();

          gameView = new Game(this);

          setContentView(gameView);
          Log.d(TAG, “content view loaded”);
          gameView.requestFocus();
          } //onCreate

  21. p4r0dy says:

    Hey man,really dig what you;re doing here.
    If I can offer a suggestion, I’d like to help stop people from making the same mistake i did and spend hours tracking down the bug.

    In your previous tutorial, you end the ontouchevent method with return super.onTouchEvent(event);

    However, in this it was modified to return true to allow for multiple types of move events. If you don’t change it to true, the only one that will ever register is the touch event. And then you will rip your hair out combing through every piece of code comparing to figure out where the difference is. Or at least I did. there’s a good chance someone going through the tuts in order will run into the same mistake (I see afew commenters claiming that the move event doesn’t work “on their phone”

    I highly suggest making a note about this in your tutorial.

    Thanks for these guides…im a C++ developer with limited java background and this guide is right at my pace.

  22. SMASH says:

    I also can’t get the droid to move with the current Motion Event, but I changed it to ACTION_DOWN and it worked, just a little sloppy, and to move it, it had to be on a down press. I guess my Droid X just doesn’t register a MotionEvent.ACTION_MOVE.

  23. mila says:

    Really Great Tutorial, I am new on Android, and struggling on some concepts like, Threads, SurfaceView etc, Now I am suddenly so clear, thank you so much.
    Your explanation is so clear and made it so simple to understand.
    If you write a book on Android, 100% I will buy it.

    Thanks

  24. lokendra says:

    hi , can u provide tutorial for moving background … circular

  25. Kratos says:

    Hi, thank you for this tutorial.

    I want to incorporate vibration whenever you touch the object. How do I do that?

  26. saurabh says:

    the code for the project here is not working for moving the object in android application,when it runs give msg. on emulator that application has stooped unexpectedly.pls try again………..pls send me ans in my mail personaly……….thanks saurabh trivedi
    india

    • Matt says:

      Saurabh, I had the same problem, but it turned out that I was using the wrong settings in my emulator. You might need to create a new virtual device which is simply a little more advanced (like an android 2.2, versus 1.3 or something).

  27. Akshat says:

    Just what I was looking for….. thanks….. i’ll post if this works for me

  28. Shilpa RP says:

    Very nicely explained.It helped me a lot Thank you