
Chapter 4. Discovering Loops and Methods
In this chapter, we will learn how to repeatedly execute portions of our code in a controlled and precise way by looking at different types of loops in Java. These include while
loops, do-while
loops, and for
loops. We will learn about the best occasions to use the different types of loops.
Then we will briefly cover the topic of random numbers. We will also see how the Java Random
class can be used. This will obviously be of great help in enhancing our math game.
Next, we will look at methods. They allow us to compartmentalize our code into more manageable blocks. We will then see how to share data between methods and divide programming tasks to simplify problems.
We will then use all that we have learned about loops, random numbers and methods on our math game project. For example, we will make the game change the question after each time we attempt the answer.
We will also add question difficulty levels and random questions within a range appropriate for a given difficulty level. We will show and update our score. The score goes up faster depending upon the difficulty level of the question answered (correctly). Eventually, even the best mathematicians among us should be beaten by the game. Nevertheless, most of us will hopefully get a bit further than what is shown in the next screenshot.
If the player gets a question wrong, the difficulty goes back to the easiest level and the score to zero. This is what the game will look like when we are done:

In this chapter we will:
- Learn about looping in multiple types of loops
- Learn how to generate random numbers in Java
- Learn everything about Java methods, including how to write and call them
- Significantly enhance our math game
Looping with loops
It would be completely reasonable to ask what loops have to do with programming, but they are exactly what the name implies. They are a way of performing the same part of the code more than once, or looping over the same part of code, but potentially for a different outcome each time.
This can simply mean doing the same thing until the code being looped over prompts the loop to end. It could prompt the loop after a predetermined number of times as specified by the loop code itself. It could also prompt the loop when a predetermined situation or condition is met. Or there could be a combination of more than one of these ways of prompting the loop to end. Along with if
, else
, and switch
, loops are part of the Java control flow statements.
We will look at all the major types of loop that Java offers us to control our code, and after we have looked at methods, we will use some of them to implement the enhancements to our math game. Let's proceed to our first type of loop.
While loops
A while
loop has the simplest syntax. Recollect the if
statements in evaluated to true
, then the code in the body of the if
block is executed. Similarly, in the while
loop, we put an expression that can evaluate to true
or false
, as shown in this code:
int x = 10; while(x > 0){ x--; //x decreases by one each pass through the loop }
What is happening here is that outside the while
loop, an integer, x
, is declared and initialized to 10
. Then the while
loop begins. Its condition is x > 0
, so it will continue looping through the code in its body until the condition evaluates to false
. Hence, the code will execute 10 times.
On the first pass, x
is equal to 10
, then 9
, then 8
, and so on. But once x
becomes equal to 0
, it is obviously no longer greater than 0
. So the program will exit the while
loop and continue with the first line of code after the loop.
Just like an if
statement, it is possible that the while
loop will not execute even once. Take a look at this example of a while
loop that never executes:
int x = 10; while(x > 10){ //more code here. //but it will never run unless x is greater than 10. }
Moreover, there is no limit to the complexity of the condition expression or the amount of code that can be written in the loop body:
int playerLives = 3; int alienShips = 10; while(playerLives >0 && alienShips >0){ //Entire game code here. //... //... //etc. } //continue here when either playerLives or alienShips = 0
The preceding while
loop would continue to execute until either playerLive
s or alienShips
becomes equal to or less than zero. As soon as one of these conditions occurs, the expression evaluates to false
and the program continues to execute from the first line of code after the while
loop.
It is worth noting that once the body of the loop has been entered, it will always complete, even if the expression evaluates to false
somewhere in between, as the condition is not checked again until the code tries to start another pass:
int x = 1; while(x > 0){ x--; //x is now 0 so the condition is false //But this line still runs //and this one //and me! }
The preceding loop body will execute exactly once. We can also set a while
loop that will run forever (which is unsurprisingly called an infinite loop), like this:
int x = 0; while(true){ x++; //I am going to get mighty big! }
Breaking out of a loop
We might use an infinite loop like the loop in the preceding example so that we can decide when to exit the loop from within its body. We would do this using the break
keyword when we are ready to leave the loop body, as shown in the following code:
int x = 0; while(true){ x++; //I am going to get mighty big! break; //No you're not haha. //code doesn't reach here }
You might have been able to guess that we can combine any of the decision making tools like if
, else
, and switch
within our while
loops and the rest of the loops we will look at in a minute:
int x = 0; int tooBig = 10; while(true){ x++; //I am going to get mighty big! if(x == tooBig){ break; } //No you're not haha. //code reaches here only until x = 10 }
It would be simple to go on for many more pages demonstrating the versatility of while
loops, but at some point, we want to get back to doing some real programming. So here is one last concept, combined with while
loops.
The continue keyword
The continue
keyword acts in a way similar to break
—up to a point. The continue
keyword will break out of the loop body but will also check the condition expression afterwards, so the loop could run again. The following example will show the use of continue
:
int x = 0; int tooBig = 10; int tooBigToPrint = 5; while(true){ x++; //I am going to get mighty big! if(x == tooBig){ break; } //No your not haha. //code reaches here only until x = 10 if(x >= tooBigToPrint){ //No more printing but keep looping continue; } //code reaches here only until x = 5 //Print out x }
Do-while loops
A do-while
loop is very much the same as a while
loop with the exception that it evaluates its expression after the body. This means that a do-while
loop will always execute at least once, as demonstrated in the following code:
int x= 0; do{ x++; }while(x < 10); //x now = 10
Note
The break
and continue
keywords can also be used in do-while
loops.
For loops
A for
loop has a slightly more complicated syntax than a while
and do-while
loop as it take three parts to initialize. Take a look at the following for
loop first. Then we will break it apart:
for(int i = 0; i < 10; i++){ //Something that needs to happen 10 times goes here }
The apparently obscure form of the for
loop is clearer when put like this:
for(declaration and initialization; condition; change after each pass through loop)
To clarify further, we have the following in a for
loop:
- Declaration and initialization: We create a new
int
variable,i
, and initialize it to 0. - Condition: Just like the other loops, this refers to the condition that must be evaluated to true for the loop to continue.
- Change after each pass through loop: In the preceding example,
i++
means that 1 is added toi
on each pass. We could also usei--
to reduce/decrementi
on each pass, which is shown in the following code:for(int i = 10; i > 0; i--){ //countdown } //blast off i = 0
Note
Note that break
and continue
can also be used in for
loops.
The for
loop essentially takes control of initialization, condition evaluation, and the control variable on itself. We will be using a for
loop to enhance our math game right after we take a look at random numbers and methods.
Random numbers in Java
Before we dive into methods, we will first take a look at how we can create random numbers because this is how we will generate our random questions.
All the hard work is done for us by the Random
class. First we need to create an object of the Random
type:
Random randInt = new Random();
Then we use our new object's nextInt
method to generate a random number within a certain range:
int ourRandomNumber = randInt.nextInt(10);
The range for the number that we enter starts from zero. So the preceding line of code will generate a random number between 0 and 9. If we want a random number between 1 and 10, we just do this:
ourRandomNumber++;
Tip
Often in these early chapters, we need to accept there is a bit of magic going on in objects like Random. In Chapter 6, OOP – Using Other People's Hard Work, we will be ripping open the black boxes and even making our own. We will be able to write our own classes and our own methods within those classes.
A good start is a look at regular vanilla methods, which we will do next.
Methods
So what exactly are Java methods? A method is a collection of variables, expressions, and control flow statements. We have already been using lots of methods; we just haven't looked inside any yet.
Learning about Java methods will be the last topic for this chapter before we get practical and use what we have learned to enhance our math game.
The structure of a method
The first part of a method that we write is called the signature. Here is a made-up example of a signature:
public boolean shootLazers(int number, string type)
Add an opening and closing pair of curly braces with some code that the method performs, and we have a complete method, or a definition. Here is a made-up but syntactically correct method:
private void setCoordinates(int x, int y){ //code to set coordinates goes here }
We could then use our new method from another part of our code, like this:
//I like it here
setCoordinates(4,6);//now I am going off to setCoordinates method
//Phew, I'm back again - code continues here
At the point where we call setCoordinates
, our program's execution would branch to the code contained within that method, which would run until it reaches its end or is told to return. Then the code would continue running from the first line after the method call.
Here is another example of a method, complete with the code to make the method return to the code that called it:
int addAToB(int a, int b){ int answer = a + b; return answer; }
The call to use the preceding method could look like this:
int myAnswer = addAToB(2,4);
Clearly, we don't need to write methods to add two int
variables together, but the preceding example helps us see a little more of the working of methods. First, we pass the values 2
and 4
. In the signature of the method, the value, 2
, is assigned to int a
and the value, 4
, is assigned to int b
.
Within the method body, the a
and b
variables are added and used to initialize a new variable, which is the int
answer. The return answer
line does just that. It returns the value stored in answer
to the calling code, causing myAnswer
to be initialized with the value of 6
.
Notice that each of the method signatures in the preceding examples varies a little. The reason for this is that the Java method signature is quite flexible, allowing us to build exactly the methods we require.
Exactly how the method signature defines how the method must be called and how a method must return a value, if it must, deserves further discussion. Let's give each part of that signature a name so that we can break it into chunks and learn about the parts separately.
Tip
Here is a method signature with its parts labeled and ready for discussion. You can also take a look at the following table to further identify which part of the signature is which. This will make the rest of our discussion on methods straightforward.
Modifier | return type | name of the method (parameters)
Here are a few examples that we have used so far so that you can clearly identify the part of the signature under discussion:

Modifier
In our previous examples, we only used a modifier twice, partly because the method doesn't have to use the modifier. The modifier is a way of specifying which code can use your method. Some of the types of modifiers are public
and private
. Actually, regular variables can have modifiers too, such as these:
//Most code can see me public int a; //Code in other classes can't see me private string secret = "Shhh, I am private";
Modifiers (for methods and variables) are an essential Java topic but they are best dealt with when we discuss the other vital Java topic we have skirted around a few times so far—objects and classes.
Note
As previously promised, these mysterious objects will be revealed in Chapter 6, OOP – Using Other People's Hard Work. However, as we can see from our example methods and from the fact that all the examples we have written so far work just fine, modifiers are not necessary to facilitate our learning so far.
Return type
Next up is the return
type. Like a modifier, a return
type is also optional, although it is more immediately useful to us. So let's look a bit closer. We have seen that our methods can get anything done. But what if we need the result from what they have done? The simplest example of a return type we have seen so far was this:
int addAToB(int a, int b){
int answer = a + b;
return answer;
}
In this code, the return
type in the signature is highlighted. So the return
type is int
. The addAToB
method sends back (returns) to the code that called it a value that will fit in an int
variable.
The return
type can be any Java type we have seen so far. The method, however, does not have to return a value at all. In this case, the signature must use the void
keyword as the return
type. When the void
keyword is used, the method body must not attempt to return a value as this will cause a compiler error. It can, however, use the return
keyword without a value. Here are some combinations of return type and uses of the return
keyword that are valid:
void doSomething(){ //our code //I'm done going back to calling code here //no return is necessary }
Another combination of return
and void
is as follows:
void doSomethingElse(){ //our code //I can do this as long as I don't try and add a value return; }
The following code is yet another combination of return
and void
:
void doYetAnotherThing(){ //some code if(someCondition){ //if someCondition is true returning to calling code //before the end of the method body return; } //More code that might or might not get executed return; //As I'm at the bottom of the method body //and the return type is void, I'm //really not necessary but I suppose I make it //clear that the method is over. } String joinTogether(String firstName, String lastName){ return firstName + lastName; }
We can call each of the preceding methods one by one, like this:
//OK time to call some methods doSomething(); doSomethingElse(); doYetAnotherThing(); String fullName = joinTogether("Jeff ","Minter") //fullName now = Jeff Minter //continue with code from here
Note
The preceding code would execute all the code statements in each method one by one. If the method signature had parameters, the code that calls the method would look slightly different.
Name of a method
When we design our own methods, the method name is arbitrary, but there is a convention to use verbs that clearly explain what the method will do. Another convention is of the first letter of the first word in the name being lower case, and the first letter of each of the subsequent words being uppercase. This is called camel case because the shape the name can form has a hump in it:
XGHHY78802c(){ //code here }
This name is perfectly legitimate and will work. However, let's take a look at a much clearer example that uses the conventions:
doSomeVerySpecificTask(){ //code here } getMySpaceShipHealth(){ //code here } startNewGame(){ //code here }
These are much clearer method names.
Now let's take a look at parameters.
Parameters
We know that a method can return a result to the calling code. What if we need to share some data values from the calling code with the method? Parameters allow us to share values with the method. We have already seen an example with parameters when looking at return types. We will look at the same example but a little more closely:
int addAToB(int a, int b){
int answer = a + b;
return answer;
}
The parameters in this code are highlighted. Notice that in the first line of the method body, we use a + b
as if they are already declared and initialized. Well, that's because they are. The parameters of the method signature is their declaration, and the code that calls the method initializes them:
int returnedAnswer = addAToB(10,5);
Also, as we have partly seen in previous examples, we don't have to use int
in our parameters. We can use any Java type, including types we design ourselves. We can mix and match types as well. We can also use as many parameters as necessary to solve our problem. An example of mixed Java types might help:
void addToAddressBook(char firstInitial, String lastName, String city, int age){ //all the parameters are now living breathing, //declared and initialized variables //code to add details to address book goes here }
It's now time to get serious about our body.
Getting things done in the method body
The body is the part we have been avoiding so far with comments like this:
//code here //some code
But actually, we know exactly what to do here already. Any Java syntax we have learned so far will work in the body of a method. In fact, if we look back, all of the code we have written so far has been in a method, albeit somebody else's method. For example, we wrote code in the onCreate
and onClick
methods.
The best thing we can do next is write some methods that actually do something in the body.
Using methods
We don't have to mess around with our math game project. We will quickly create a new blank project for each of the next two explorations into methods.
We also don't need to spend time making a UI. We will use the Android console to view the results and discuss the implications of our examples of methods. As we are using the Android console to view the results of our work with methods, we will need to run all of these examples on the Android emulator, not on a real device.
Note
It is possible to set up a real device to output to the console, but we have not covered that in this book. If you want to find out more about using your actual device for debugging, take a look at the article at http://developer.android.com/tools/device.html.
As usual, you can open the already typed code files in the usual way. The next two examples on methods can be found in the Packt Publishing code download in the Chapter4
folder and the AWorkingMethod
and ExploringMethodOverloading
subfolders.
Tip
The following is a quick reminder on how to create a new blank project.
- Close any currently open projects by navigating to File | Close Project.
- Click on New Project....
- The Create New Project configuration window will appear. Fill in the Application name field and Company Domain with
packtpub.com
, or you could use your own company website's name here instead. - Now click on the Next button. On the next screen, ensure that the Phone and tablet checkbox has a tick in it. Now we have to choose the earliest version of Android we want to build our app for. Go ahead and play with a few options in the drop-down selector. You will see that the earlier the version we select, the greater the percentage of devices our app can support. However, the trade-off here is that the earlier the version we select, the fewer cutting-edge Android features we can have in our apps. A good balance is to select API 8: Android 2.2 (Froyo). Go ahead and do that now as shown in the next screenshot.
- Click on Next. Now select Blank Activity and click on Next again.
- On the next screen, simply change Activity Name to
MainActivity
and click on Finish. - As we did in Chapter 2, Getting Started with Android, to keep our code clear and simple, you can delete the two unneeded methods (
onCreateOptionsMenu
andonOptionsItemSelected
) and their associated@override
and@import
statements, but this is not necessary for the example to work.
For a detailed explanation and images of creating a new project, see Chapter 2, Getting Started with Android.
A working method
First, let's make ourselves a simple working method, complete with return types and a fully functioning body.
This method will take three numbers as parameters and return a true
or false
value to the calling code depending upon whether one of the three numbers was randomly generated within the method or not:
- Create a new blank project called
A Working Method
. - In this method, we will use the
Random
class we saw earlier and itsrandInt
method as a part of the demonstration. Copy the code for this method after the closing bracket ofonCreate
but before the closing bracket ofMainActivity
. When you are prompted to import any classes, simply click on OK:boolean guessANumber(int try1, int try2, int try3){ //all the Log.i lines print to the Android console Log.i("info", "Hi there, I am in the method body"); //prove our parameters have arrived in the method //By printing them in the console Log.i("info", "try1 = " + try1); Log.i("info", "try2 = " + try2); Log.i("info", "try3 = " + try3);
- Now we declare a Boolean variable called
found
and initialize it tofalse
. We will changefound
totrue
if and when we guess the random number correctly. Next, we declare our random number and print some useful values to the console://we use the found variable to store our true or false //setting it to false to begin with boolean found = false; //Create an object of the Random class so we can use it Random randInt = new Random(); //Generate a random number between 0 and 5 int randNum = randInt.nextInt(6); //show our random number in the console Log.i("info", "Our random number = " + randNum);
- The last portion of code in our method tests to see whether there is a match for any of our passed-in parameters, prints some output, and then returns
true
orfalse
using thefound
variable to the calling code in theonCreate
method://Check if any of our guesses are the same as randNum if(try1 == randNum || try2 == randNum || try3 == randNum){ found = true; Log.i("info", "aha!"); }else{ Log.i("info", "hmmm"); } return found; }
- Now write this code just before the closing bracket of the
onCreate
method to call the code and print some values to the Android console://all the Log.i lines print to the Android console Log.i("info", "I am in the onCreate method"); //Call guessANumber with three values //and if true is returned output - Found it! if(guessANumber( 1,2,3 )) { Log.i("info", "Found It!"); }else{//guessANumber returned false -didn't find it Log.i ("info", "Can't find it"); } //continuing with the rest of the program now Log.i("info", "Back in onCreate");
- Launch an emulator.
- Run the app on the emulator.
- All our console messages have a tag called info. The console window will already have appeared underneath the editor window. We can filter its contents to only show our messages by typing
info
in the search box, as shown in the following screenshot:
In the preceding screenshot, you can see the search filter and the console output. We will now run through the code and explain the output.
For clarity, here is the precise console output, without the extraneous date, time, and package name added to the beginning of each line. Remember that we are dealing with a random number, so your output may vary:
info: I am in the onCreate method info﹕Hi there, I am in the method body info﹕try1 = 1 info﹕try2 = 2 info﹕try3 = 3 info﹕Our random number = 0 info﹕hmmm info﹕Can't find it info﹕Back in onCreate
Here is what is happening. In step 2, we started writing our first method. We called it guessANumber
. It has three int
parameters and will return a Boolean. Remember that these three int
parameters become fully initialized variables. First of all, however, in our method, we simply output the values of the new variables passed in as parameters as well as a message confirming that the code in our method is currently being executed:
boolean guessANumber(int try1, int try2, int try3){ //all the Log.i lines print to the Android console Log.i("info", "Hi there, I am in the method body"); //prove our parameters have arrived in the method //By printing them in the console Log.i("info", "try1 = " + try1); Log.i("info", "try2 = " + try2); Log.i("info", "try3 = " + try3);
In step 3, we added more code to our method. We declared and initialized a Boolean variable called found
, which we will use to return a value to the calling code and let the calling code know whether one of the parameters passed in was the same as the random number:
//we use the found variable to store our true or false //setting it to false to begin with boolean found = false;
Next (still in step 3), we generated a random number in the same way as we did earlier in the chapter. We also used Log
to output the random number so that we can examine what went on:
//Create an object of the Random class so we can use it Random randInt = new Random(); //Generate a random number between 0 and 5 int randNum = randInt.nextInt(6); //show our random number in the console Log.i("info", "Our random number = " + randNum);
In step 4, we used an if
statement with the logical OR operator to detect whether any of the passed-in parameters matches the random number we just generated, as shown in the following code:
//Check if any of our guesses are the same as randNum if(try1 == randNum || try2 == randNum || try3 == randNum){
If the condition is true, that is, if any of try1
, try2
, or try3
equals randNum
, then the following code is run. Our found
Boolean value is set to true
and a message is printed:
found = true; Log.i("info", "aha!");
If the condition is not true, the else
statement is executed, a different message is printed, and the found
variable is left the same as it was—false
:
}else{ Log.i("info", "hmmm"); }
Finally, in our method, we return the found
variable, which will be either true
or false
, to the calling code:
return found; }
Now we look at step 5, which is the code in the onCreate
method, which calls our guessANumber
method in the first place. We start by simply printing a message saying that we are in onCreate
at the moment:
//all the Log.i lines print to the Android console Log.i("info", "I am in the onCreate method");
Then we make the call to guessANumber
with the three parameters. In this case, we use 1, 2, and 3, but any int
values would have worked. However, we wrap the call in an if
statement. This means that the return
value from the method will be used to evaluate the if
statement. Simply put, if true
is returned, the if
statement will be executed and "Found It!" will be printed:
//Call guessANumber with three values //and if true is returned output - Found it! if(guessANumber(1,2,3)){ Log.i("info", "Found It!"); }
On the contrary, if false
is returned, the else
statement gets executed and "Can't find it" is printed:
else{//guessANumber returned false -didn't find it Log.i ("info", "Can't find it"); } //continuing with the rest of the program now Log.i("info", "Back in onCreate");
Remember that we are dealing with random numbers, so you might need to run it a few times before you see this output:

Of course, you should note that the guesses sent to the function as parameters are arbitrary. As long as all the numbers are between 0 and 5 and are not duplicated, they will together have a 50 percent chance of finding the random number.
On a closing note, if you've to read only one tip in this whole book, it should be this one.
Tip
Printing variable values to the console is a great way to examine what is going on inside your game and to find bugs.
Let's look at another example of methods.
Exploring method overloading
As we are learning, methods are really diverse and deep as a topic, but hopefully, taking a step at a time, we will see they are not daunting in any way. We will be using what we have learned about methods when we enhance our math game. We will be exploring methods even more deeply in create a new project to explore method overloading.
As we will now see, we can create more than one method with the same name, provided the parameters are different. The code in this project is vastly simpler than that of the last project. It is how this code works that might appear slightly curious until we analyze it later:
- Create a new blank project called
Exploring Method Overloading
. - In the first method, we will simply call it
printStuff
and pass anint
variable via a parameter to be printed. Copy the code for this method after the closing bracket ofonCreate
but before the closing bracket ofMainActivity
. When you are prompted to import any classes, simply click on OK:void printStuff(int myInt){ Log.i("info", "This is the int only version"); Log.i("info", "myInt = "+ myInt); }
- We will also call the second method
printStuff
but pass astring
variable to be printed. Copy the code for this method after the closing bracket ofonCreate
but before the closing bracket ofMainActivity
. Again, when you are prompted to import any classes, simply click on OK:void printStuff(String myString){ Log.i("info", "This is the String only version"); Log.i("info", "myString = "+ myString); }
- Yet again, we will call this third method
printStuff
but pass astring
variable and anint
variable to be printed. As before, copy the code for this method after the closing bracket ofonCreate
but before the closing bracket ofMainActivity
:void printStuff(int myInt, String myString){ Log.i("info", "This is the combined int and String version"); Log.i("info", "myInt = "+ myInt); Log.i("info", "myString = "+ myString); }
- Now write this code just before the closing bracket of the
onCreate
method to call the methods and print some values to the Android console://declare and initialize a String and an int int anInt = 10; String aString = "I am a string"; //Now call the different versions of printStuff //The name stays the same, only the parameters vary printStuff(anInt); printStuff(aString); printStuff(anInt, aString);
- Launch an emulator.
- Run the app on the emulator.
Here is the console output:
info﹕ This is the int only version info﹕ myInt = 10 info﹕ This is the String only version info﹕ myString = I am a string info﹕ This is the combined int and String version info﹕ myInt = 10 info﹕ myString = I am a string
As you can see, Java has treated three methods with the same name as totally different methods. This, as we have just demonstrated, can be really useful. It is called method overloading.
Tip
Method overloading and overriding confusion
Overloading and overriding are defined as follows:
- Overloading occurs when we have more than one method with the same name but different parameters
- Overriding occurs when we essentially replace a method with the same name and the same parameter list
We know enough about overloading and overriding to complete this book, but if you are brave and your mind is wandering, you can override an overloaded method. However, that is something for another time.
This is how the preceding code works. In each of the three steps (2, 3, and 4), we create a method called printStuff
, but each printStuff
method has different parameters, so each is a different method that can be called individually:
void printStuff(int myInt){ ... } void printStuff(String myString){ ... } void printStuff(int myInt, String myString){ ... }
The body of each of the methods is simple. It just prints the passed-in parameters and confirms which version of the method is being called currently.
The next important part of our code is when we make it plain which method we want to call, using the appropriate parameters. In step 5, we call each of them in turn, using the appropriate parameters so that Java knows the exact method required:
printStuff(anInt); printStuff(aString); printStuff(anInt, aString);
Now we know more than enough about methods, loops, and random numbers to make some improvements to our math game.
Enhancing our math game
We are going to add some features to our math game using what we have just learned about methods and loops.
As usual, the code is available for copying in the Chapter4
folder of the code download. The project is in the MathGameChapter4
subfolder and encompasses all the remaining phases of improvement covered in this chapter, including enhancing the UI, amending our game activity, setQuestion
, updateScoreAndLevel
, isCorrect
, and calling our new methods.
We will make the game change the question after each time we attempt the answer.
We will also add difficulty levels to questions and random questions but within a range appropriate for that difficulty level.
We will show and update our score. The score goes up faster depending on the difficulty level of the question answered correctly.
If the player gets a question wrong, the difficulty goes back to the easiest level and the score to zero.
Enhancing the UI
Let's get on with modifying our math game UI to incorporate our new game features. We will be adding a TextView to display the score and another TextView to display the level.
- Open the
activity_game.xml
file in the editor window. We will add a new TextView to the very bottom of our UI for our score. - Drag a Large Text element from Palette and place it to the left, below our three answer buttons.
- Now we need to change the id property so that we can access our new TextView from our Java code. Ensure that the new TextView is selected by clicking on it. Now, in the Properties window, change the id property to
textScore
. - For the sake of clarity (although this step serves no use in programming), change the text property to
Score:999
. - Now put another Large Text element to the right of the one we just configured and change the id property to
textLevel
. The lower part of our UI should now look like this: - Once again, for the sake of clarity (although this step serves no use in programming), change the text property to
Level:4
. - Save the project.
We have just added two new TextView elements and assigned them both an ID that we can refer to in our Java code.
Tip
You have probably realized by now that the precise layout and size of our UI elements are unimportant as far as getting the game to work is concerned. This gives us a lot of flexibility in designing layouts for different screen sizes. As long as each layout for each screen size contains the same element types with the same IDs, the same Java code will work for different layouts. If you want to know more about designing for multiple screen sizes, take a look at http://developer.android.com/training/multiscreen/screensizes.html.
Now that we have our enhanced UI and an understanding of how the Java Random
class works, we can add the Java code to implement our new features.
The new Java code
As previously explained, the project code is available in the Chapter4
folder of the downloadable code. The project is called MathGameChapter4
and encompasses all the improvements covered in this chapter.
In this phase, we will be adding lots of new code, moving some existing code, and modifying some existing code too. As so much is changing, we are going to approach the code from the very beginning. The new code will be explained completely, the code that has moved will be pointed out with a reason, and the code that has stayed the same and in the same place will have the least explanation.
We will first make some modifications and deletions to our existing code. We will then look at designing and implementing each of our new methods to improve our code and add our new features.
Amending GameActivity
First, let's perform the necessary amendments and deletions to our current code:
- Open the
GameActivity.java
file in the editor window. - We now need to consider the scope of the objects that represent our UI elements. Both
textObjectPartA
andtextObjectPartB
need to be accessible from the methods we will be creating soon. So let's move their declarations, as we did with the multi-choice buttons in the previous chapter, out of theonCreate
method so that they are accessible everywhere in ourGameActivity
class. The following code shows all our declarations so far. They are present immediately after the start of theGameActivity
class. The recently added (or moved) declarations are highlighted. Notice that we have also added declarations for our two new TextViews and for the score and level displays. In addition, there are two newint
variables that we can manipulate for our score and to keep track of our level. They arecurrentScore
andcurrentLevel
:public class GameActivity extends Activity implements View.OnClickListener{ int correctAnswer; Button buttonObjectChoice1; Button buttonObjectChoice2; Button buttonObjectChoice3; TextView textObjectPartA; TextView textObjectPartB; TextView textObjectScore; TextView textObjectLevel; int currentScore = 0; int currentLevel = 1;
- All of the code that assigns text to our Buttons or TextViews objects, and the code that initializes the parts of our question and assigns the values for our wrong answers, are now going to change and move, so we need to delete it all. Everything shown in the following code is to be deleted:
//Here we initialize all our variables int partA = 9; int partB = 9; correctAnswer = partA * partB; int wrongAnswer1 = correctAnswer - 1; int wrongAnswer2 = correctAnswer + 1;
- The following code snippet needs to be deleted too:
//Now we use the setText method of the class on our objects //to show our variable values on the UI elements. textObjectPartA.setText("" + partA); textObjectPartB.setText("" + partA); //which button receives which answer, at this stage is arbitrary. buttonObjectChoice1.setText("" + correctAnswer); buttonObjectChoice2.setText("" + wrongAnswer1); buttonObjectChoice3.setText("" + wrongAnswer2);
- For clarity and context, here is the entire
onCreate
method as it currently stands. There is nothing new here, but you can see your code, which links our Button and TextView objects that we declared in step 2. Again, this code includes our two new TextViews, which are highlighted, but everything else, which is described in steps 3 and 4, is deleted. As before, there is a piece of code that makes our game listen to button clicks:protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //The next line loads our UI design to the screen setContentView(R.layout.activity_game); /*Here we get a working object based on either the button or TextView class and base as well as link our new objects directly to the appropriate UI elements that we created previously*/ textObjectPartA = (TextView)findViewById(R.id.textPartA); textObjectPartB = (TextView)findViewById(R.id.textPartB); textObjectScore = (TextView)findViewById(R.id.textScore); textObjectLevel = (TextView)findViewById(R.id.textLevel); buttonObjectChoice1 = (Button)findViewById(R.id.buttonChoice1); buttonObjectChoice2 = (Button)findViewById(R.id.buttonChoice2); buttonObjectChoice3 = (Button)findViewById(R.id.buttonChoice3); buttonObjectChoice1.setOnClickListener(this); buttonObjectChoice2.setOnClickListener(this); buttonObjectChoice3.setOnClickListener(this); }//onCreate ends here
- Now we will delete some more code that we don't need because we are going to make it more efficient by compartmentalizing it into our new methods and adding our new features at the same time. So in our
onClick
method, in each case of ourswitch
statement, we want to delete theif
and theelse
statements. We will be completely rewriting these, but we will leave in place the code that initializes ouranswerGiven
variable. OuronClick
method will now look like this:@Override public void onClick(View view) { //declare a new int to be used in all the cases int answerGiven=0; switch (view.getId()) { case R.id.buttonChoice1: //initialize a new int with the value contained in buttonObjectChoice1 //Remember we put it there ourselves previously answerGiven = Integer.parseInt("" + buttonObjectChoice1.getText()); break; case R.id.buttonChoice2: //same as previous case but using the next button answerGiven = Integer.parseInt("" + buttonObjectChoice2.getText()); break; case R.id.buttonChoice3: //same as previous case but using the next button answerGiven = Integer.parseInt("" + buttonObjectChoice3.getText()); break; } }
- Save your project.
Wow! That was a lot of code, but as we saw along the way, there were no new concepts. In step 2, we simply moved the initialization of our Button and TextView objects to a place where they will now be visible from anywhere within our class.
In steps 3 and 4, we did a fair bit of deletion because we will no longer be making the question or populating the multi-choice buttons in onCreate
, as this is not flexible enough. We will soon see how we improve on this.
In step 6, we deleted the code that tested whether the answer was correct or incorrect. However, as we saw, we still initialized the answerGiven
variable in the same way—in the appropriate case of our switch
statement in the onClick
method.
Great! Now we are ready to consider and design some new methods to compartmentalize our code, avoid repetitions in it, and add our extra features. Consider the following methods that we will soon implement.
The methods
We will now walk through writing some methods. As we will see, the methods will compartmentalize our code and prevent the implementation of our new features from causing the code to become too long and sprawling:
- We will write a
setQuestion
method to prepare a question of appropriate difficulty. - We will write an
updateScoreAndLevel
method that will do just that. We will also write anisCorrect
method that one of our other methods will use to evaluate the correctness of the answer. - Then we will strategically place the code that calls our new methods.
We will do each of these tasks one at a time and explain the code along the way, as leaving the explanation to the end will make referring to individual steps cumbersome.
We will use many of the features of Java that we learned in this chapter and the previous chapter. These include the following:
- Methods
- A
for
loop - The switch control structure
So let's get started with our first method.
The setQuestion method
We determined that we needed a method to prepare a question for us; setQuestion
seems like a decent name for such a method. Every time our player gives an answer by tapping one of the three multi-choice buttons, a new question will need to be prepared.
This method will need to generate values for our partA
and partB
variables as well as show them in our TextViews referenced by the textObjectPartA
and textObjectPartB
objects. In addition, the method will need to assign the new correct answer to our correctAnswer
variable, which will then be used to calculate some suitable incorrect answers. Finally, the method will show both the correct and incorrect answers on our multi-choice buttons.
Furthermore, our setQuestion
method will need to take into account the level held in currentLevel
to determine the range or difficulty of the question it will ask. Let's go through the code. If you want to type this code as we go, then just make sure you place it after the closing bracket of onClick
but before the closing bracket of our GameActivity
class:
- First of all, we have the method signature and the opening curly brace before the body of our method:
void setQuestion(){
- This tells us that the return type is
void
, sosetQuestion
will not return a value to the code that calls it. Also, there are no parameters here, so it does not need any value passed for it to work. Let's see what it does. Now we enter the code to generate the two parts of the question://generate the parts of the question int numberRange = currentLevel * 3; Random randInt = new Random(); int partA = randInt.nextInt(numberRange); partA++;//don't want a zero value int partB = randInt.nextInt(numberRange); partB++;//don't want a zero value
- In the previous step, we declared a new
int
variable,numberRange
, and initialized it by multiplying the player'scurrentLevel
value by3
. Then we got a newRandom
object calledrandInt
and used it to generate new values based onnumberRange
. We did this to thepartA
andpartB
variables. As the value ofcurrentLevel
increases, so potentially does the difficulty of the question. Now, just as we have written in the past, we write this:correctAnswer = partA * partB; int wrongAnswer1 = correctAnswer-2; int wrongAnswer2 = correctAnswer+2; textObjectPartA.setText(""+partA); textObjectPartB.setText(""+partB);
- We assigned the answer of our new multiplication question to
correctAnswer
. Then we declared and assigned two incorrect answers to the newint
variables,wrongAnswer1
andwrongAnswer2
. We also used thesetText
method of our TextView objects to display the question to the player. Notice that we have not yet displayed the correct and incorrect answers. Here it is. Try to work out what is happening here://set the multi choice buttons //A number between 0 and 2 int buttonLayout = randInt.nextInt(3); switch (buttonLayout){ case 0: buttonObjectChoice1.setText(""+correctAnswer); buttonObjectChoice2.setText(""+wrongAnswer1); buttonObjectChoice3.setText(""+wrongAnswer2); break; case 1: buttonObjectChoice2.setText(""+correctAnswer); buttonObjectChoice3.setText(""+wrongAnswer1); buttonObjectChoice1.setText(""+wrongAnswer2); break; case 2: buttonObjectChoice3.setText(""+correctAnswer); buttonObjectChoice1.setText(""+wrongAnswer1); buttonObjectChoice2.setText(""+wrongAnswer2); break; } }
- In the preceding code, we used our
Rando
m object,randInt
, to generate a number between 0 and 2, and assigned the value to a newint
variable calledbuttonLayou
t. We then usedbuttonLayou
t to switch between all its possible values: 0, 1, or 2. Eachcase
statement sets the correct and incorrect answers to the multi-choice buttons in a slightly different order, so the player can't just keep tapping the same button over and over to achieve a massive score. Notice the extra closing bracket after the closing bracket of the switch. This is the end of oursetQuestion
method.
We explained the code fairly thoroughly as we went through it but it might be worthwhile to just take a closer look at some parts again.
In step 1, we saw our method signature with a void
return type and no parameters. In step 2, we generated some random numbers that will be within a certain range. This range isn't as obvious as it might seem at first. First, we assigned, declared, and initialized numberRange
like this:
int numberRange = currentLevel * 3;
So if the player is at the first question, then currentLevel
will hold the value 1
and numberRange
will be initialized as 3
. Then we made a new Random
object as previously discussed and entered this line of code:
int partA = randInt.nextInt(numberRange);
What occurs here is that the nextInt
method of the Random
object, randInt
, will return a value of either 0, 1, or 2 because we have given it a seed of 3. We don't want any zeros in our game because they result in very easy multiplication, so we enter this:
partA++;//don't want a zero value
This operator, which you probably remember from Chapter 3, Speaking Java – Your First Game, when we discussed operators, adds 1 to partA
. We then do exactly the same to our partB
variable, which means that assuming that the player is still on level 1, they will have a question that will be one of the following:
1 x 1, 1 x 2, 1 x 3, 2 x 1, 2 x 2, 2 x 3, 3 x 1, 3 x 2, or 3 x 3
As the level increases, the potential range of the question increases significantly. So at level 2, the options are that either part of the question could be from 1 to 6; for level 3, from 1 to 9; and so on. It is still possible to get an easy question on a higher level but it becomes less likely as the levels advance. Finally in this step, we display the question to the player using the setText
method.
In step 3, we have seen before but this time we varied it slightly. We calculate and assign a value for correctAnswer
, and declare and assign values to wrongAnswer1
and wrongAnswer2
, which will hold the wrong answer choices for our buttons.
Part 3 varies very slightly from what we did in onCreate
in the previous chapter because we subtract and add 2 to wrongAnswer1
and wrongAnswer2
, respectively. This makes guessing the answer to multiplication questions a little harder because you can't eliminate answers based on whether they are odd or even.
Step 4 simply randomizes which buttons the correct and incorrect answers will be placed on. We don't need to keep track of this because when the time comes to compare the value on the button pressed with the correct answer, we can simply use our Java code to discover it as we did in Chapter 3, Speaking Java – Your First Game.
The updateScoreAndLevel method
The name of this method speaks for itself. Because the keeping of the score is not simple and because we want higher levels to yield higher scores, we will compartmentalize the code to keep our program readable. If we then want to make modifications to the scoring system, they can all take place in there.
Let's write the code.
- This code can go anywhere within the opening and closing braces of
GameActivity {}
, but it is good practice to place them in the approximate order they will be used. So why not start adding your code after the closing brace ofsetQuestion
but obviously before the closing brace ofGameActivity
? Here is the method signature with the opening brace:void updateScoreAndLevel(int answerGiven){
- This tells us that our method does not return a value but that it does receive an
int
, which it will require to do its stuff. The name of the parameter is a big clue to what we will be passing. We will see that in action in the body in a minute, but if passing the player's answer to this method instead of theisCorrect
method is a bit confusing, we will see things become clearer in the next chunk of code. Here is the next part of the code to add:if(isCorrect(answerGiven)){ for(int i = 1; i <= currentLevel; i++){ currentScore = currentScore + i; } currentLevel++; }
- There is a lot happening here, so we will dissect it more once we have the method completed. Basically, it calls the
isCorrect
method (which we will write soon) and if the response istrue
, adds to the player's score in afor
loop. After that, the method adds 1 tocurrentLevel
. Here comes theelse
part of the code in case the response fromisCorrect
isfalse
:else{ currentScore = 0; currentLevel = 1; }
- If the response is
false
, that is, if the player got the answer wrong, thecurrentScore
variable is set to0
and the level back to1
. Finally for this method, we type the following://Actually update the two TextViews textObjectScore.setText("Score: " + currentScore); textObjectLevel.setText("Level: " + currentLevel); }
- In the previous step, we updated the actual TextViews that the player sees with the newly determined score and level. The method then ended and the control of the program returned to the code that called
updateScoreAndLevel
to begin with. Save your project.
We explained most of the code as we went but it might be good to quickly review it and dig a bit deeper into certain parts, especially the call to isCorrect
in that odd-looking if
statement.
In step 1, we began with the method signature. Then in step 2, we began with the aforementioned curious if
:
if(isCorrect(answerGiven)){
We have seen this type of statement before in the A working method example in the Methods section of this chapter. What is happening here is that the call to isCorrect
is replacing the statement to be evaluated, or rather it is the statement to be evaluated. So isCorrect
is called with the answerGiven
variable. The answerGiven
variable, as you might remember, was passed to updateScoreAndLevel
. This time, it is passed to the isCorrect
method, which will do some work with it and perhaps a few other things. Then it will return to the if
statement a value of true
or false
. The value will be true if the question is answered correctly and false if not.
Assuming the if
statement evaluates to true, the program runs this bit of code (also from step 2):
for(int i = 1; i <= currentLevel; i++){ currentScore = currentScore + i; } currentLevel++;
The code enters a for
loop where the starting variable i
is initialized to 1 like this: int i = 1;
. Furthermore, the loop is instructed to continue as long as i
is less than or equal to our currentLevel
variable. Then within the for
loop, we add i
to the current score. As an example, let's assume that the player has just got a question correct and we enter the for
loop with currentLevel
at 1. The player's score is still at 0 because this is their first correct answer.
At pass 1, we get the following:
i = 1
, so it is equal tocurrentLevel
, which is also 1. So we enter thefor
loopi = 1
, socurrentScore
equals 0- We add
i
, which is1
, tocurrentScore
- Our
currentScore
variable is now equal to1
At pass 2, the following steps take place:
i
is incremented to 2, so it is now greater thancurrentLevel
, which is 1- The
for
loop condition evaluates tofalse
and we continue with the code after thefor
loop currentLevel
is increased by 1 to 2
Now let's look at that for
loop again assuming that the player gets the next question correct as well, and we are back in updateScoreAndLevel
. This time, isCorrect
has evaluated true and we enter the for
loop but with a slightly different situation than the last time.
At pass 1, the following steps take place:
i = 1
, soi
is less thancurrentLevel
is 2 and we enter thefor
loopi = 1
,currentScore
= 1
- We add
i
, which is equal to 1, tocurrentScore
- Our
currentScore
variable is now equal to 2
At pass 2, we have the following steps happening:
i
is incremented to 2 and it is now equal tocurrentLevel
, which is also 2i = 2
,currentScore = 2
- We add
i
, which is now equal to 2, tocurrentScore
- Our
currentScore
variable is now equal to 4
At pass 3, the following steps take place:
i
is incremented to 3 and it is now greater thancurrentLevel
, which is 2.- The
for
loop condition evaluates to false and we continue with the code after thefor
loop. - The value of
currentLevel
is increased by 1 to 3. So the next time, we will have an extra pass through ourfor
loop.
What is happening is that with each level, the player is being rewarded with another pass through the for
loop, and each pass through the for
loop adds a greater value to their score. To summarize what happens in the for
loop, here is a brief table of values showing how the player's score is increased based on the currentLevel
variable:

Note
Of course, we could have kept it really simple and not used a for
loop. We could just use currentScore = currentScore + level
perhaps, but that doesn't offer an ever increasing reward in the same way as our current solution does and we wouldn't have been able to practice our for
loops either.
If if(isCorrect(answerGiven))
evaluates to false
, it simply resets the score to 0 and the level to 1 in step 3. Step 4 then updates our TextViews for the score and the level using the variables we have just discussed.
Now we have just one more method to write. Of course, this is the isCorrect
method, which we just called.
The isCorrect method
This method is nice and simple because we have seen all of the relevant code before. It is just the method signature and the return value that we need to look at carefully:
- Enter the code just after the closing brace of the
updateScoreAndLevel
method but before the closing brace of theGameActivity
class. Type the method signature like this:boolean isCorrect(int answerGiven){
- Here we can see that the method must return a Boolean value,
true
orfalse
. If it doesn't, then the program won't compile. This guarantees that when we use this method as the evaluation expression in theupdateScoreAndLevel
method, we will definitely get a result. It can be true or false. The signature also shows us theanswerGiven
variable passed in, ready for us to use. Type this code, which will determine that result:boolean correctTrueOrFalse; if(answerGiven == correctAnswer){//YAY! Toast.makeText(getApplicationContext(), "Well done!", Toast.LENGTH_LONG).show(); correctTrueOrFalse=true; }else{//Uh-oh! Toast.makeText(getApplicationContext(), "Sorry", Toast.LENGTH_LONG).show(); correctTrueOrFalse=false; }
- We have seen almost all of the preceding code before. The exception is that we declare a Boolean variable,
correctTrueOrFalse
, which we assign totrue
if the player answers correctly and tofalse
if not. We know whether the player is correct or not because we compareanswerGiven
tocorrectAnswer
in theif
statement. Notice that we have also triggered the appropriate Android pop-up toast message as we did before. Finally, we do this:return correctTrueOrFalse; }
We just returned whatever value is contained within correctTrueOrFalse
. So the critical if
statement in updateScoreAndLevel
, which we discussed in detail, will know what to do next.
To make sure we understand what is happening in isCorrect
, let's go through the sequence of events in our code. In step 1 we have the method signature. We see that we will return a true
or false
value and receive int
.
In step 2, we declare a Boolean variable called correctTrueOrFalse
to hold the value we will soon return. Then we test for a right or wrong answer with if(answerGiven == correctAnswer)
. If the two compared values match, a congratulatory message pops up and we assign true
to our Boolean variable. Of course, if the if
statement is false
, we offer commiserations to the player and assign false
to our important Boolean.
Finally in step 3, we send back true
or false
so that the updateScoreAndLevel
method can proceed with its work.
We have now implemented all our methods. It's time to put them to work.
Calling our new methods
Of course, our shiny new methods won't do anything until we call them. So here is the plan to call these methods:
- When the game starts, we want to set a new question for the player. Therefore, as the last line of code in our
onCreate
method, we can call oursetQuestion
method like this:setQuestion(); }//onCreate ends here
- Then we turn our attention to the
onClick
method, which already detects which button has been pressed and loads the player's answer into ouranswerGiven
variable. So at the end of theonClick
method, after the closing brace of theswitch
statement, we just call this function:updateScoreAndLevel(answerGiven);
- This sends our player's attempted answer to
updateScoreAndLevel
, which evaluates the answer usingisCorrect
, adds points, and increments the score if the answer is correct or resets the score and level if not. All that we need now is another question. Add this line. It will ask another question:setQuestion();
So now what happens is that the player starts our math game by clicking on its icon on their Android device. Our GameActivity
class declares a few variables that we need access to throughout:
int correctAnswer; Button buttonObjectChoice1; Button buttonObjectChoice2; Button buttonObjectChoice3; TextView textObjectPartA; TextView textObjectPartB; TextView textObjectScore; TextView textObjectLevel; int currentScore = 0; int currentLevel = 1;
Then onCreate
initializes some variables and gets our buttons ready to receive clicks from the player before asking the first question by calling setQuestion
. The game then waits for the player to attempt an answer. When the player attempts an answer, it is dealt with by onClick
, updateScoreAndLevel
, and isCorrect
. Then the program control comes back to onClick
again, setQuestion
is called again, and we wait for the player's answer once more.
Finishing touches
Our math game is coming along nicely. Unfortunately, we have to move on soon. The project has served its purpose to demonstrate some fundamentals of Java programming as well as some key Android features. Now we need to start introducing some more game-related topics.
Before we go on, there are two really easy things to make our game a bit more cool and complete. In case you are wondering about the High Scores button, we will see how that can be implemented when we look at our next game project in Chapter 5, Gaming and Java Essentials. You will then have enough information to easily come back and implement high scores on your own.
The other feature that would really round off our game and make it more playable is an overall or per question time limit. Perhaps even increasing the score based on how quickly the correct answer is given will help. We need some new Java tricks up our sleeves before we can do that, but we will see how we can measure and respond to time in Chapter 5, Gaming and Java Essentials, when we talk about threads.
Now we will quickly learn two improvements:
- Locking the screen orientation
- Changing the home screen image
Going full screen and locking orientation
You might have noticed that if you rotate your device while the app is running, not only does your game UI get distorted but the game progress is also lost. What goes wrong is that when the device is rotated, the onPause
and onStop
methods are called. Then the app is restarted. We could handle this by overriding the onPause
method and saving our data. We will do this later. For now we don't want the screen to rotate anyway, so if we stop it we solve two problems in one.
While adding code to this file, Android Studio may try to "help" by adding extra formatting. If you get red error indicators, you can compare your AndroidManifest.xml
file with the one in the code download in the Chapter4/MathGameChapter4
folder. Alternatively, you can simply replace the contents of your file with the contents of the file in the download. The step-by-step changes are detailed in this guide just to highlight what is changing:
- This is the first step in locking the app to portrait. Open the
AndroidManifest.xml
file. It is located directly below theres
folder in the Project Explorer. Find the first opening<activity
in the code. - Enter a new line as follows:
android:screenOrientation="portrait"
- Repeat step 2 after the second instance of
<activity
. We have now locked both the menu and game screens in portrait mode. - To make the game full screen, in the same file, find the following text and add the line in bold after it but before the closing
>
sign:<activity android:name="com.packtpub.mathgamechapter4.app.MainActivity" android:label="@string/app_name" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> </activity>
- Make the same change to the
GameActivity
activity like this. Again, here is the code in context in order to avoid mistakes with these>
signs:<activity android:name="com.packtpub.mathgamechapter4.app.GameActivity" android:label="@string/title_activity_game" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> </activity>
- Save the project.
Now, when you rotate the device during gameplay, the portrait orientation will be fixed.
Adding a custom image (instead of the Android icon)
We probably don't want to have the Android image on our finished game home screen, so here is the procedure to change it. This quick guide relies on you having an image you would like to use:
- First, we need to add the required image to the layout folder. Copy your image file by clicking on it in Windows Explorer and using Ctrl + C.
- Now find the
drawable-mdpi
folder in the Android Studio Project Explorer. Click on the folder. - Paste the image to the folder using Ctrl + V.
- Now the image is a part of our project. We simply need to choose it in the same way as we chose the image of the Android robot previously. Open
activity_main.xml
in the editor window and click on ImageView (currently an Android robot). - In the Properties window, find the src property. Click on it and then on ....
- Search for your image and select it.
- Save your project.
- You now have the image of your choice on the home screen.
Self-test questions
Q1) Guess what is wrong with this method:
void doSomething(){ return 4; }
Q2) What will x
be equal to at the end of this code snippet?
int x=19; do{ x=11; x++; }while(x<20)
Summary
We came a really long way in this chapter. You got a serious handle on Java loops and took your first, fairly deep look into Java methods and how to use them. You learned how to generate random numbers and significantly enhanced your math game using all of the knowledge you gained.
As the chapters proceed, the games will get more and more real-game-like. In the next chapter, we will make a game to test the player's memory. It will have sound, animation and will actually save the player's high scores too.
Congratulations on your progress so far but let's keep going.