QuizMe
QuizMe
App Inventor Classic • App Inventor Classic • FOR APP INVENTOR 2 CLICK HERE• App Inventor Classic • App Inventor Classic
This information pertains to App Inventor 1 (Classic). For tutorials about App Inventor 2, go to the App Inventor 2 Tutorials.
What you're building
Download Alternate Example Version (Book Chapter PDF)
QuizMe is a trivia game about baseball, but you can use it as a template to build quizzes on any topic. With QuizMe:
- The user steps through a series of questions, clicking a button to proceed to the next question.
- The user enters an answer for each question and the app reports whether each answer is correct or not.
With QuizMe, the quiz questions are always the same unless you, the programmer, change them. Later, you can create MakeQuiz & TakeQuiz, an app that lets users of the app create and modify the quiz questions.
This tutorial assumes you are familiar with the basics of App Inventor -- using the Component Designer to build a user interface, and using the Blocks Editor to specify event-handlers. If you are not familiar with the basics, try stepping through some of the basic tutorials before continuing.
Getting Started
Connect to the App Inventor web site and start a new project. Name it QuizMe, and also set the screen's Title to "QuizMe". Open the Blocks Editor and connect to the phone.
Also download the following pictures of baseball players and save them on your computer. Later, you'll load these images into your project.
Introduction
You'll design the quiz game so that the user proceeds from question to question by clicking a Next button, and receives simple correct/incorrect feedback on each answer.
This tutorial introduces:
- Defining and displaying lists of information.
- Sequencing through a list using an index variable -- a variable that keeps track of a position in a list.
- Conditional behaviors-- performing certain operations only when a condition is met.
- Switching an image to show a different picture at different times.
Set up the Components
Use the component designer to create the interface for QuizMe. When you finish, it should look something like the snapshot below (there are also more detailed instructions below the snapshot).
To create this interface, first load the images you downloaded into the project. Click on the Add button in the Media area and select one of the downloaded files (e.g., Larsenberra.jpg). Then do the same for the other three images.
Next, create the following components by dragging them from the Palette into the Viewer.
Component Type | Palette Group | What you'll name it | Purpose of Component |
Image | Basic | Image1 | The picture part of the question |
Label | Basic | QuestionLabel | Displays the current question |
HorizontalArrangement | Screen Arrangement | HorizontalArrangement1 | Organizes the AnswerPrompt and Text |
Label | Basic | AnswerPromptLabel | Text prompting for an anwer |
TextBox | Basic | AnswerText | User will enter answer here. |
Label | Basic | RightWrongLabel | Correct/Incorrect is displayed here. |
HorizontalArrangement | Screen Arrangement | HorizontalArrangement2 | Organizes the AnswerButton and NextButton |
Button | Basic | AnswerButton | User clicks to submit an answer |
Button | Basic | NextButton | User clicks to proceed to the next answer |
Set the properties of the components as described below:
Component | Action |
Image1 | Set its Picture property to "Larsenberra.jpg". This is the first picture that should appear. |
QuestionLabel | Change Text property to "question" |
AnswerPromptLabel | Change Text property to "Enter Answer:". On the Viewer screen, move this label into HorizontalArrangement1. |
AnswerText | Change Hint to "please enter an answer". On the Viewer screen, move AnswerText into HorizontalArrangement1. |
AnswerButton | Change Text property to "Submit". On the Viewer, move the button into HorizontalArrangment2. |
NextButton | Change Text property to "Next". Move the button into HorizontalArrangement2. |
RightWrongLabel | Change Text property to "correct/incorrect". |
Add Behaviors to the Components
Open the Blocks Editor to add the behaviors for the app. First, you'll define two list variables, QuestionList to hold the list of questions, and AnswerList to hold the list of corresponding answers.
To define the two list variables, you'll need the following blocks:
Block Type | Drawer | Purpose |
def variable | Definitions | Defines the QuestionList variable (rename it) |
def variable | Definitions | Defines the AnswerList variable (rename it) |
make a list | Lists | Used to insert the items of the QuestionList |
text (3 of them) | Text | The actual questions |
make a list | Lists | Used to insert the items of the AnswerList |
text (3 of them) | Text | The actual answers |
You create global variables by dragging in a def variable block from the Definitions drawer and double-clicking the default name "variable" to change its name. The def variable block has a slot for the initial value of the variable. The variable can represent a number or text, or even a list, for which you can plug in a make a list block into the variable definition.
The blocks should look like this:
Define the Hidden Index Variable
Each time the user clicks the NextButton to proceed through the quiz, the app needs to remember which question it is on. In programming, to remember something, you define a new variable. In this case, the app needs to remember the current question number -- the index into the list QuestionList.
To create the variable currentQuestionIndex, you'll need the following blocks:
Block Type | Drawer | Purpose |
def variable | Definitions | Defines the currentQuestionIndex variable (rename it) |
number (1) | Math | Set the initial value of currentQuestionIndex to 1 |
The blocks should look like this:
Display the first question
To start, you'll ignore the answers and just work on the behavior to sequence through the questions. The desired behavior is the following: when the app starts, the first question should appear in the label named QuestionLabel. When the user clicks the NextButton, the second question should appear. When the user clicks again, the third should appear. When the last question is reached, clicking the NextButton should result in the first question once again appearing in the QuestionLabel.
With App Inventor, you select particular items in a list with the select list item block. The block asks you to specify the list and an index--a position in the list. If a list has three items, the indexes 1, 2, and 3 are valid.
For QuizMe, when the app starts, the app should choose the first question in the list and display it in the QuestionLabel component.
For this app initialization behavior, you'll need the following blocks:
Block Type | Drawer | Purpose |
Screen1.Initialize | Screen1 | When the app begins, this event-handler is triggered. |
set QuestionLabel.Text to | QuestionLabel | Need to put the first question in QuestionLabel |
select list item | Lists | Need to select the first question from QuestionLabel |
global QuestionList | My Definitions | The list to select from |
number (1) | Math | Select the first question by using an index of 1 |
The blocks should look like this:
How the blocks work
The Screen1.Initialize event is triggered when the app begins. The first item of the variable QuestionList is selected and placed into QuestionLabel.Text. So when the app begins, the user will see the first question.
Test this behavior. Click Restart Phone App (or Connect Phone if not connected). What appears on the phone? If you created the QuestionList as described above, the first item of QuestionList, "Who pitched a perfect game in the World Series?", should appear in the QuestionLabel.
Iterating Through the Questions
Now program the behavior of the NextButton. You've already defined the currentQuestionIndex to remember the question the user is on. When NextButton is clicked, the app needs to increment this variable, e.g., change it from 1 to 2 or from 2 to 3, etc., and then use the resulting value to select the new "current" question. For this behavior, you'll need the following blocks:
Block Type | Drawer | Purpose |
NextButton.Click | NextButton | When user clicks Next, this event-handler is triggered. |
set currentQuestionIndex to | My Definitions | Need to put the first question in QuestionLabel |
+ block | Math | Used to increment currentQuestionIndex |
global currentQuestionIndex | My Definitions | New value will be old value + 1 |
number (1) | Math | For the + 1 |
set QuestionLabel.Text to | QuestionLabel | Need to display the next question here |
select list item | Lists | Need to select the first question from QuestionList |
global QuestionList | My Definitions | Plug into list slot of call select list item |
global currentQuestionIndex | My Definitions | Plug into index slot of call select list item, we want nth item |
The blocks should look like this:
How the Blocks Work
The first row of blocks increments the variable currentQuestionIndex. If currentQuestionIndex has a 1 in it, it is changed to 2. If it has a 2, it is changed to 3, and so on. Once the currentQuestionIndex variable has been changed, the app uses it to select the "current" question.
Recall that in the Screen.Initialize event-handler, the app selected the first question to display:
When the NextButton is clicked, the app doesn't choose the first item in the list, or the 2nd or 3rd, it chooses the currentQuestionIndex-th item.
The blocks are executed in a right-to-left manner. The app first evaluates the index parameter of select list item, which is the variable currentQuestionIndex. The number is stored in currentQuestionIndex is used as the index when the select list item is executed.
When the NextButton is clicked for the first time, the increment blocks will set currentQuestionIndex from 1 to 2, so the app will select the second item from QuestionList, "who pitched the first perfect game of 2010?". The second time NextButton is clicked, currentQuestionIndex will be set from 2 to 3, and the app will select the 3rd question in the list, "who pitched the first perfect game of the modern era?"
Test this behavior. Test the behavior of the NextButton to see if the app is working correctly thus far. To test, play the role of the user and click the NextButton on the phone. Does the phone display the second question, "Who pitched the first perfect game of 2010?" It should, and the third question should appear when you click the NextButton again. If this is working, pat yourself on the back quickly, and then go on. Try clicking the NextButton again (a third time). You should see an error: "Attempting to get item 4 of a list of length 3". The app has a bug-- do you know what the problem is?
The problem with the app is that it always increments the currentQuestionIndex variable when the NextButton is clicked. When currentQuestionIndex is already 3 and the user clicks the NextButton, the app changes currentQuestionIndex from 3 to 4, then calls select list item to get the currentQuestionIndex-th , or in this case, the 4th item. Since there are only three items in the variable QuestionList, Android complains.
What the app needs to do is ask a question-- check a condition-- when the NextButton is clicked, and execute different blocks dependending on the answer. One way to ask the question is to ask, "is the variable currentQuestionIndex already 3?" If the answer is yes, you should set currentQuestionIndex back to 0 so the user is taken back to the first question.
You'll need the following blocks:
Block Type | Drawer | Purpose |
if test then-do | Control | To ask if user is on last question |
= block | Math | to test if currentQuestionIndex is 3 |
global currentQuestionIndex | My Definitions | - |
number 3 | Math | 3 is number of items in the list |
set currentQuestionIndex to | My Definitions | set to 0 to go back to first question |
Number 0 | Math | set to 0 because next blocks will increment to 1 |
The modified NextButton.Click event-handler should look like this:
How the blocks work
When the NextButton is clicked, the app first checks to see if currentQuestionIndex has a 3 in it. If it does, currentQuestionIndex is set back to 0 so that when 1 is added to it with the blocks below, it will be 1 and the quiz will loop back to display the first question. Note that only the blocks inset within the if-test-then-do block are dependent on the condition-- the increment and set QuestionLabel.Text to blocks are executed under all conditions.
Test this behavior. Because variables like currentQuestionIndex are hidden, they are often the source of bugs in a program. Fortunately, App Inventor provides a way to make such hidden variables transparent during testing. Specifically, App Inventor allows you to "watch" how the value of a variable change as an app progresses. For this test, right-click the currentQuestionIndex def block in the Blocks Editor and choose Watch. Then choose Restart Phone App. The def block will then appear with a watch box showing the initial value of currentQuestionIndex (which should be 1): Now pick up the phone and click the NextButton. The second question, "who pitched the most recent perfect game in the major leagues?" should appear in the QuestionLabel on the phone, as before. On the App Inventor screen, 2 should appear in the currentQuestionIndex memory cell: When you click again, the third question should appear on the phone and 3 should appear in the memory cell . If you click again, 1 should appear in currentQuestionIndex and the first question on the phone.
A Maintainable App: Making it Easy to Modify the Questions
Next, you'll modify the app to make it easy to add and remove elements from the list. You'll rewrite the blocks so that they'll work on any list, not just one with exactly three items. To begin, add a fourth question to QuestionList and another answer into AnswerList. The blocks should look like this:
Test the modified app. Click the NextButton a number of times. You should see that the fourth question never appears, no matter how many times you click Next.
The problem is that the test to see if the user is on the last question is too specific: it asks if the currentQuestionIndex variable is 3:
You could just change the number 3 to a 4, and the app would again work correctly. The problem with this solution, however, is that each time you modify the questions and answers, you also have to remember to make this change. Such dependencies in a computer program often lead to bugs, especially as an app grows in complexity. It's much better to set the program up so that it will work no matter how many questions there are. Such generality is even more important when the list you are working with changes dynamically, e.g., a quiz app that allows the user to add new questions.
The better solution is to ask the question in a more general way. You really want to know if the current question the user is on-- the value of currentQuestionIndex -- is as large as the number of items in QuestionList. If the app asks the question in this more general manner, it will work even when you add to or remove items from the QuestionList. To modify the NextButton.Click event-handler you'll replace the previous test that referred directly to 3. You'll need the following blocks:
Block Type | Drawer | Purpose |
length of list | Lists | asks how many items are in QuestionList |
global QuestionList | My Definitions | put into list slot of length of list |
Your NextButton.Click event-handler should now appear as:
How the Blocks Work
The if test now compares the currentQuestionIndex to the length of the QuestionList. So if currentQuestionIndex has a 4 in it, and the length of the QuestionList is 4, then the currentQuestionIndex will be set to 0 (and then 1 after the increment operation in the first row of blocks after the if). Note that, because the blocks no longer refer to 3 or any specific size, the behavior will work no matter how many items are in the list.
Test the modified behavior. When you click the NextButton, does the app now sequence through the four questions, moving to the first one after the fourth?
Switching the Image for Each Question
The current app shows the same image, no matter what question is being asked. You can change this so an image pertaining to each question appears when the NextButton is clicked. Earlier, you added four pictures as media for the project. Now, you'll create a third list, PictureList, with the names of the image files as its items. and you'll modify the NextButton.Click event-handler to switch the picture each time.
First, create a PictureList and initialize it with the names of the image files. Be sure that the names are exactly the same as the names of the files that were loaded in to the media of the project. Here's how the blocks for the PictureList should look:
Next, you need to modify the NextButton.Click event-handler so that it modifies the picture depending on what question the user is on. If you set the Image.Picture property to a file name of an image that has been loaded, that image will appear. To modify NextButton.Click, you'll need the following blocks:
Block Type | Drawer | Purpose |
set Image1.Picture to | Image1 | set this to change the picture |
select list item | Lists | need to select the picture corresponding to current question |
global PictureList | My Definitions | select a file name from this list |
global currentQuestionIndex | My Definitions | select the currentQuestionIndex-th item |
Here is how the blocks should look:
How the Blocks Work
The currentQuestionIndex serves as the index for the QuestionList and the currentQuestionIndex is 1, the app selects the first question and the first picture. When currentQuestionIndex is 2, the app selects the second question and second picture. Of course this scheme depends on the lists being in sync and indeed they are. For instance, the first picture, LarsenBerra.jpg, is a picture of Don Larsen, and Don Larsen is the answer to the first question, "Who pitched a perfect game in the World Series?" Test the modified behavior. Does a different image appear each time you click the NextButton?
Evaluating Answers
Next, you'll add blocks that report whether the user has answered a question correctly or not. The user enters the answer in AnswerText and then clicks the AnswerButton. The app must compare the user's entry with the answer to the "current" question, using an ifelse block to check. The RightWrongLabel should be modified to report whether or not the answer is correct. You'll need the following blocks for this behavior:
Block Type | Drawer | Purpose |
AnswerButton.Click | AnswerButton | the behavior is triggered when user clicks the AnswerButton |
ifelse | Control | if answer is correct, do one thing, else do another |
= block | Math | to ask if answer is correct |
AnswerText.Text | AnswerText | the user's answer is in this textbox |
select list item | Lists | to select the current answer from AnswerList |
global AnswerList | My Definitions | The list to select from |
global currentQuestionIndex | My Definitions | the question number (and answer number) the user is on |
set RightWrongLabel.Text to | RightWrongLabel | report the answer here |
text block "correct" | Text | if answer is right |
set RightWrongLabel.Text to | RightWrongLabel | report the answer here |
text block "incorrect" | Text | if answer is wrong |
The blocks should look like this:
How the Blocks Work
The ifelse test reads, "is the user's answer (AnswerText.Text) equal to the currentQuestionIndex-th item in the AnswerList?" If currentQuestionIndex is 1, the app will compare the user's answer with the first item in AnswerList, "Don Larsen". If currentQuestionIndex is 2, the app will compare the user's answer with the second answer in the list, "Dallas Braden", and so on. If the test result is positive, the then-do blocks are executed and the RightWrongLabel is set to "correct!". If the test is false, the else-do blocks are executed and the RightWrongLabel is set to "incorrect".
Test the modified app. Try answering one of the questions. It should report whether or not you answered the question exactly as is specified in the AnswerList. Test with both a correct and incorrect answer (because text is being compared, the test is case-sensitive). Click the NextButton and answer a second question. Does it still work? It should, but you might notice that when you click the NextButton, the "correct"/"incorrect" text and the previous answer are still there. Though it's fairly innocuous, such user interface issues will definitely be noticed by the users of your app.
To blank out the RightWrongLabel and the AnswerText, you'll put the following blocks within the NextButton.click event-handler:
Block Type | Drawer | Purpose |
set RightWrongLabel.Text to | RightWrongLabel | the label to blank out |
text (blank) | Text | When Next is clicked, erase old answer critique |
set AnswerText.Text to | AnswerText | the user's answer from previous question |
text (blank) | Text | When Next is clicked, erase old answer |
The blocks should look like this:
How the Blocks Work
When the NextButton is clicked, the user is moving on to the next question, so the top two rows of the event-handler blank out the RightWrongLabel and the AnswerText.
Test this behavior. Answer a question and click Submit, then click the NextButton. Does your previous answer and the apps critique disappear?
Final Program
QuizMe! Final Version:
Package the final version of the app by choosing Package For Phone | Barcode from the Component Designer menu. When the barcode appears, use the barcode scanner on your phone to download and install the app.
Variations
Once you get a quiz working, you might want to explore some variations. For example,
- Instead of just showing images for each question, try playing a sound clip or a short video. With sound, you can turn your quiz app into a Name That Tune app.
- The quiz is very rigid in terms of what is accepted as a valid answer. There are a number of ways to modify this. One is to use the text.contains block to see if the user's answer is contained in the actual answer. Another is to provide multiple answers for each question, and check by iterating (using foreach) through them to see if any match.
- Transform the quiz so that it is multiple-choice. The list of answers will need to be a list of lists, with each sub-list holding the answer choices. Use the ListPicker component to allow the user to choose an answer.
Review
Here are some of the ideas covered in this tutorial:
- Apps can be written in a general manner so that they work with any data list.
- Index variables are used to track the current position within a list. When you increment them, be careful about reaching the end of the list.
Scan the Sample App to your Phone
Scan the following barcode onto your phone to install and run the sample app.
Download Source Code
If you'd like to work with this sample in App Inventor, download the source code to your computer, then open App Inventor, go to the My Projects page, and choose More Actions | Upload Source.
MIT and Google are grateful to Professor David Wolber, CS Professor at The University of San Francisco, for developing this tutorial. Done with QuizMe? Return to the other tutorials here.
Tutorial Version:
Tutorial Difficulty:
- Advanced
Tutorial Type:
- Game
- Data Storage