Back to Examples
package com.example.flagguessinggamev1;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
//New Imports:
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.res.AssetManager;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.Log;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MainActivity extends Activity {
// String used when logging error messages using class Log to distinguish
// this activity's error messages from others that are being written to the log
private static final String TAG = "FlagQuiz Activity";
//Number of flags in the game
private static final int FLAGS_IN_GAME = 10;
private List fileNameList; // flag file names
private List gameCountriesList; // countries in current game
private Set regionsSet; // world regions in current game
//set vs list: a set is unordered and contains unique elements
//HashSet uses a hash table for storage and thus the user has no control
//over where in the set the element will be stored. You can't use and
//index to access the element. So use ArrayLists here if you want to
//access elements by their index.
private String correctAnswer; // correct country for the current flag
private int totalGuesses; // number of guesses made
private int correctAnswers; // number of correct guesses
private SecureRandom random; // used to randomize the game
//SecureRandom produce cryptographically secure random numbers
private Handler handler; // used to delay loading next flag
private Animation shakeAnimation; // animation for incorrect guess
private TextView questionNumberTextView; // shows current question #
private ImageView flagImageView; // displays a flag
private LinearLayout guessLinearLayout; // answer Buttons
private TextView answerTextView; // displays Correct! or Incorrect!
//**********************************************************************
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fileNameList = new ArrayList();
gameCountriesList = new ArrayList();
regionsSet = new HashSet();
//HashSet creates a collection that uses a hash table for storage.
regionsSet.add("Asia");
regionsSet.add("Africa");
regionsSet.add("North_America");
regionsSet.add("South_America");
regionsSet.add("Europe");
regionsSet.add("Oceania");
random = new SecureRandom();
handler = new Handler(); // handles the delay of next flag by 2 secs
// load the shake animation that's used for incorrect answers
shakeAnimation = AnimationUtils.loadAnimation(this, R.anim.incorrect_shake);
shakeAnimation.setRepeatCount(3); // animation repeats 3 times
// get references to GUI components
questionNumberTextView = (TextView) findViewById(R.id.questionNumberTextView);
flagImageView = (ImageView) findViewById(R.id.flagImageView);
guessLinearLayout = (LinearLayout) findViewById(R.id.buttonsLinearLayout);
answerTextView = (TextView) findViewById(R.id.answerTextView);
// set questionNumberTextView's text
//This will format and put the 'question' string resource which is
//Question %1$d of %2$d in the textview.
questionNumberTextView.setText(getResources().getString(R.string.question, 1, FLAGS_IN_GAME));
// Configure listeners for the 3 guess buttons in the linearlayout:
for (int btn = 0; btn < 3; btn++) {
Button button = (Button) guessLinearLayout.getChildAt(btn);
button.setOnClickListener(guessButtonListener);
}
//Start a new game:
resetGame();
}
//**********************************************************************
// set-up and start the next game - populate the arrays
public void resetGame() {
//use AssetManager to get image file names from the assets folder
//AssetManager: Provides access to an application's raw asset
AssetManager assets = this.getAssets();
fileNameList.clear(); // remove elements in the list of image names
try {
// loop through each region
for (String region : regionsSet) {
// get a list of all flag image files in this region's folder
String[] paths = assets.list(region);
for (String path : paths) {
fileNameList.add(path.replace(".png", ""));
}
}
} catch (IOException exception) {
//Log the exception for debugging. e() logs error msgs
Log.e(TAG, "Error loading image file names", exception);
}
correctAnswers = 0; // reset the number of correct answers made
totalGuesses = 0; // reset the total number of guesses the user made
gameCountriesList.clear(); //remove elements in prior list of countries
int flagCounter = 1;
int numberOfFlags = fileNameList.size();
// add FLAGS_IN_GAME random file names to the gameCountriesList
while (flagCounter <= FLAGS_IN_GAME) {
// returns a random index between the range 0-number of flags
int randomIndex = random.nextInt(numberOfFlags);
// get the random image file name from fileNameList
String fileName = fileNameList.get(randomIndex);
//add a country name to the list only if it has not been added yet
if(!gameCountriesList.contains(fileName)) {
gameCountriesList.add(fileName); // add the file to the list
++flagCounter;
}
}
loadNextFlag(); // start the game by loading the first flag
} // end method resetGame
//**********************************************************************
// after the user guesses a correct flag, load the next flag and buttons
private void loadNextFlag() {
// get file name of the next flag and remove it from the list
String nextImage = gameCountriesList.remove(0);
correctAnswer = nextImage; // update the correct answer
answerTextView.setText(""); // clear answerTextView
// display current question number
questionNumberTextView.setText(getResources().getString(R.string.question, (correctAnswers + 1), FLAGS_IN_GAME));
// extract the region from the next image's name. Remember the file names
// in gameCounrtyList are formated as: regionName-countryName
String region = nextImage.substring(0, nextImage.indexOf('-'));
// use AssetManager to load next image from assets folder
//'this' means: this activity
AssetManager assets = this.getAssets();
try {
// get an InputStream to the asset representing the next flag
//ex: Africa/Africa-Algeria.png
InputStream stream = assets.open(region + "/" + nextImage + ".png");
// load the asset as a Drawable and display on the flagImageView
Drawable flag = Drawable.createFromStream(stream, nextImage);
flagImageView.setImageDrawable(flag);
} catch (IOException exception) {
Log.e(TAG, "Error loading " + nextImage, exception);
}
Collections.shuffle(fileNameList); // shuffle file names
//shuffle: Randomly permutes the list elements
// put the correct answer at the end of fileNameList
int correct = fileNameList.indexOf(correctAnswer);
fileNameList.add(fileNameList.remove(correct));
// add 3 guess Buttons
// place Buttons in currentTableRow
for (int btn = 0; btn < guessLinearLayout.getChildCount(); btn++) {
// get reference to Button to configure
Button newGuessButton = (Button) guessLinearLayout.getChildAt(btn);
newGuessButton.setEnabled(true);
// get country name and set it as newGuessButton's text
String fileName = fileNameList.get(btn);
newGuessButton.setText(getCountryName(fileName));
}
// randomly replace one Button with the correct answer
int btn = random.nextInt(3); // pick random button out of the three btns
String countryName = getCountryName(correctAnswer);
((Button) guessLinearLayout.getChildAt(btn)).setText(countryName);
} // end method loadNextFlag
//**********************************************************************
// parses the country flag file name and returns the country name
//country names are of the following format: North_America-Costa_Rica
private String getCountryName(String name) {
String country = name.substring(name.indexOf("-")+1);
return country.replace('_', ' ');
}
//**********************************************************************
//utility method that disables all buttons when the user gusses the country
private void disableButtons() {
for(int btn = 0; btn < guessLinearLayout.getChildCount(); btn++) {
guessLinearLayout.getChildAt(btn).setEnabled(false);
}
}
//**********************************************************************
//guessButtonListener is an anonymous inner class object that implements
//the OnClickListener interface which responds to button click events.
private OnClickListener guessButtonListener = new OnClickListener() {
@Override
public void onClick(View v) {//v is a Button here
Button guessButton = ((Button) v);
String guess = guessButton.getText().toString();
String answer = getCountryName(correctAnswer);
totalGuesses++; // increment number of guesses the user has made
if (guess.equals(answer)) {// if the guess is correct
++correctAnswers; // increment the number of correct answers
// display correct answer in green text
answerTextView.setText(answer + "!");
answerTextView.setTextColor(getResources().getColor(R.color.correct_answer));
disableButtons(); // disable all guess Buttons
// if the user has correctly identified FLAGS_IN_QUIZ flags
if (correctAnswers == FLAGS_IN_GAME) {
// DialogFragment to display game stats and start new game
DialogFragment gameResultsDialog = new DialogFragment() {
// create an AlertDialog and return it
@Override
public Dialog onCreateDialog(Bundle bundle) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setCancelable(false);
builder.setMessage(getResources().getString(R.string.results,totalGuesses, (1000 / (double) totalGuesses)));
// "Reset game" Button
builder.setPositiveButton(R.string.reset_quiz,new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog,int id) {
resetGame();
}
}); // end call to setPositiveButton
return builder.create(); // return the AlertDialog
} // end method onCreateDialog
}; // end DialogFragment anonymous inner class
// use FragmentManager to display the DialogFragment
gameResultsDialog.show(getFragmentManager(), "Game Results");
} else {
// load the next flag after a 1-second delay
handler.postDelayed(new Runnable() {
//new Rnnable(): anonym inner class that implements the runnable interface
@Override
public void run() {
loadNextFlag();
}
}, 2000); // 2000 milliseconds for 2-second delay
}
} else {
flagImageView.startAnimation(shakeAnimation); // play shake
// display "Incorrect!" in red
answerTextView.setText(R.string.incorrect_answer);
answerTextView.setTextColor(getResources().getColor(R.color.incorrect_answer));
guessButton.setEnabled(false); // disable incorrect answer
}
} // end method onClick
}; // end answerButtonListener
}//End MainActivity
Back to Examples