Monday, May 31, 2010

Basic graphics with Android

I have been working on a small game, and come across a real lack of useful information on how to develop a non-real time game. (i.e. A game that is entirely event driven.)

I started with the usual route, attempting to use existing UI objects to create the look that I wanted. However, I was unable to get the look and behavior that I wanted. So, I looked closer at the Lunar Lander sample in the Android SDK. The problem is that the Lunar Lander sample was perfect if you wanted to develop a real-time game. But, I didn't.

So, what to do? After much Googling, I found suggestions that what I really wanted to do was create a specialized view that would contain my game board. Doing this turned out to be easier than I thought it would. Using the Eclipse wizards, I created a new class that would be my game board view. The class was derived from the View class. The simplistic class would look something like this :


package com.example.simplegame;

public class BoardView extends View {
public BoardView(Context context) {
super(context);

}

public BoardView(Context context, AttributeSet attrib)
{
super(context, attrib);

}
}


Now, let me save you some time and pain. When I started this effort, I used "super(context)" inside the second constructor. Doing that will prevent you from using findViewById() to get a pointer to the view in your form.

Okay, so now we have a basic class set up. But, it doesn't actually do anything useful. What we really wanted to do is have a small blue box drawn on our view. So how do we do that?

Simple. We add an onDraw() method to our class. onDraw() takes a single parameter, with is of type Canvas. The canvas is what we will draw on, and what will ultimately be shown on the screen. As previously stated, we want to draw a simple blue square on the screen, so our onDraw method should look something like this :


@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

Paint p = new Paint();
p.setAntiAlias(true);
p.setARGB(255, 0, 0, 255);

canvas.drawRect(0, 0, 10, 10, p);
}


Before we move on to talk about how to use our new View in a form, I want to point out a little bit more information on how onDraw() works. According to the Android documentation, you should assume that the canvas passed in to onDraw() is blank. (That is, you should never rely on anything you have previously drawn to still exist on the canvas.) Because of this, you will need to redraw your entire view each time onDraw() is called. In our sample, we draw the blue square each time onDraw() is called.

Now that we have a new view class set up, we need to figure out how to put it in a form, and use it. To make things easy, I first created a simple form with a generic View that fills the entire screen. It looks something like this :


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<View android:id="@+id/View01" android:layout_width="fill_parent" android:layout_height="fill_parent"></View>
</LinearLayout>


Now that we have our layout, we need to modify it to include our custom view. To do this, we change the <View> tags to be <com.example.simplegame.BoardView>. If you then take this class, and this form and wrap it in some boiler plate code that would display the form, you will get a form with a small blue square in the top left corner of the screen.

If you do some experimenting, you will find that you get the blue square on the screen even if you don't establish a pointer to the BoardView object in the form. While it may seem strange at first, if you think about it you will realize that all of the objects on a form exist as soon as the form is inflated. The findViewById() call is simply giving you a pointer to the object that already exists in memory. Therefore, the onDraw() method is called, even if your main program doesn't have a pointer to the view.

No comments:

Post a Comment