Capture the flag: A tutorial on Android's Canvas class and Touch event handler

Android developer William Francis takes a break from writing business apps and shows how to code a simple electronic game of capture the flag.

Step seven: Besides a method to hide the flag, we will also want to provide a way for the user to give up. In this case, we just toggle the global isHiddenFlag, and again force the screen to re-draw calling the invalidate() method.

publicvoid giveUp(){
isFlagHidden = false;

invalidate();

}

Step eight: Now we come to the onDraw method. On the Android platform, overriding the onDraw is a common technique used to manually paint the contents of a view. You must draw everything within this function each time the method is called. For us that means maybe the image of our flag (depending if it is "hidden" or not) and a simple gray rectangle that acts as a visual indicator to the user where the game board is. There is also an initialization clause, where we check to see if our flag x and y location have been set, and if not, we initialize both the size of our canvas and prepare to draw the flag in the center of it.

Gotcha! A common beginner's mistake is to want to do the initialization of the canvas width and height in the constructor. However, the width and height of a View is not actually set until the first pass of the layout happens. getWidth() and getHeight() will always return 0 in the constructor. Since we need the width and height to determine the initial x and y parameters of the mFlag as well, a conditional initialization in the onDraw works nicely.

Step nine: The trickiest part of this tutorial is the logic in our collision detection algorithm. This is where we use the constants CLOSE and CLOSER, which we defined in step five. The diagram in Figure B should help explain how these variables are used, as well as how tweaking them can increase of decrease the sensitivity of the game.
Figure B

Based on an algorithm of concentric rectangles, we use Android's built-in Rect data class to create the outer two rectangles. We must include a bit of normalization code to keep any rectangle from extending off the bounds of our canvas. After that, it's just a matter of using the Rect class's .contains() method. We must start in the center and work our way outward.

Step 10: With our GameBoard class doing the heavy lifting, all that remains is to implement our UI and "glue" code in Main.java. We start with a standard class that extends Activity, implementing both an on touch and on click listener. There are also a couple of class variables: one for toggling whether our flag is hidden and one for holding the drawing canvas.

Step 11: After wiring the listeners in our onCreate override, we need to implement the handlers. Let's focus on the onClick first. This method gets called in response to the button press. We invert the isFlagHidden state using a logical not operator (!). Then we call hideTheFlag() or giveUp() in our GameBoard class as appropriate.

@Override

publicvoid onClick(View v) {

if (v.getId() == R.id.the_button) {

TextView tv = (TextView)findViewById (R.id.the_label);

tv.setText("");

Button b = (Button) findViewById(R.id.the_button);

isFlagHidden = !isFlagHidden;

if (isFlagHidden) {

b.setText("Give Up!");

mGameBoard.hideTheFlag();

} else {

b.setText("Hide the Flag!");

mGameBoard.giveUp();

}

}

}

Step 12: We need to intercept and respond to the Touch events. We've already implemented all the methods we need to determine whether a user had found the flag. We just need to call our takeAGuess() method inside the GameBoard class, passing as parameters the current x and y coordinates of the user's finger. We also need to update the text label to let the user know what's up.

@Override

publicboolean onTouch(View v, MotionEvent event) {

if (v.getId() == R.id.the_canvas) {

if (event.getAction() == MotionEvent.ACTION_DOWN) {

if (isFlagHidden) {

TextView tv = (TextView)findViewById (R.id.the_label);

switch (mGameBoard.takeAGuess(event.getX(), event.getY())) {

caseBULLSEYE:

Button b = (Button) findViewById(R.id.the_button);

isFlagHidden = false;

b.setText("Hide the Flag!");

tv.setText("You found it!");

tv.setTextColor(Color.GREEN);

break;

caseHOT:

tv.setText("You're hot!");

tv.setTextColor(Color.RED);

break;
caseWARM:

tv.setText("Getting warm...");

tv.setTextColor(Color.YELLOW);

break;
caseCOLD:

tv.setText("You're cold.");

tv.setTextColor(Color.BLUE);

break;

}

}

}

returntrue;

}

returnfalse;

}

If you managed to follow the tutorial up to this point, you should have your own playable version of capture the flag. For those of you who'd prefer to download and import the entire project into Eclipse, you can grab it here.

Thank you to those readers who contacted me concerning the soundboard tutorial and requested I do something similar with Android's graphic subsystem. After spending all week at my daytime gig writing serious business apps, coding and sharing this simple game was a fun change of scenery. I encourage anyone reading this post to use the discussion thread or the email contact form if you have ideas for other subjects you'd like me to cover in the App Builder blog.

About William J. Francis

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

Full Bio

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intelligence Corps. Throughout his career William has published numerous technical articles, as well as the occasional short story.