MakeQuiz and TakeQuiz
MakeQuiz and TakeQuiz
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
MakeQuiz and TakeQuiz are two apps that, in tandem, allow a teacher to create quizzes for a student. Parents can create fun trivia apps for their children during a long road trip, grade school teachers can build "Math Blaster" quizzes, and college students can build quizzes to help their study groups prepare for a final. This tutorial will walk you through creating both the MakeQuiz and the TakeQuiz app.
This tutorial is a followup of the QuizMe tutorial -- if you haven't completed that tutorial you should do so before continuing.
Getting started
Connect to the App Inventor web site and start a new project. Name it "MakeQuiz", and also set the screen's Title to "MakeQuiz". Open the Blocks Editor and connect to the phone.
Introduction
You'll design two apps, MakeQuiz for the "teacher" and TakeQuiz for the "student". With MakeQuiz:
- The user enters questions and answers in an input form.
- The previously entered question-answer pairs are displayed.
- The quiz is stored persistently, in a database.
TakeQuiz will work similarly to the QuizMe app-- in fact you'll build it using QuizMe as a basis. TakeQuiz will differ in that the questions asked will be those that were entered into the database using MakeQuiz.
This tutorial introduces the following App Inventor concepts:
- Input forms for allowing the user to enter information.
- Displaying lists: serializing a list variable to display it on separate lines.
- Persistent data: MakeQuiz will save the quiz questions and answers in a database and TakeQuiz will load them in from the database.
- Data Sharing: the two apps together illustrate how two phones (and even two apps) can communicate through a shared, web database:
Set up the Components for MakeQuiz
Use the Component Designer to create the interface for MakeQuiz. When you finish, it should look something like the snapshot below (there are also more detailed instructions below the snapshot).
You'll use the following components to create MakeQuiz. Drag each component from the Palette into the Viewer and name it as specified below:
Component Type | Palette Group | What you'll name it | Purpose of Component |
HorizontalArrangement | Screen Arrangement | HorizontalArrangement1 | Display the question prompt and textbox on one line |
Label | Basic | Label1 | The "Question:" prompt |
TextBox | Basic | QuestionText | User enters questions here |
HorizontalArrangement | Screen Arrangement | HorizontalArrangement2 | Display the answer prompt and textbox on one line |
Label | Basic | Label2 | The "Answer:" prompt |
TextBox | Basic | AnswerText | User enters answers here |
Button | Basic | SubmitButton | User clicks to submit a QA pair. |
Label | Basic | QuestionsAnswersLabel | This will display previously entered QAs |
TinyWebDB | Other Stuff | TinyWebDB1 | To store to and retrieve from database |
Set the properties of the components in the following way:
- Set the Text of Label1 to "Question": and the Text of Label2 to "Answer":
- Set the Hint of QuestionText to "enter a question" and the Hint of AnswerText to "enter an answer".
- Set the Text of SubmitButton to " Submit "
- Set the Text of QuestionsAnswersLabel to "Questions and Answers".
Put Label1 and QuestionText in HorizontalArrangement1, and Label2 and AnswerText in HorizontalArrangement2.
Add behaviors to the components
Open the Blocks Editor to add the behaviors for the app. As with the original QuizMe app, you'll first define some global variables for the QuestionList and AnswerList , but this time you won't provide fixed questions and answers. You'll need the following blocks:
Block type | Drawer | Purpose |
def variable ("QuestionList") | Definitions | Defines the QuestionList variable (rename it) |
def variable ("AnswerList") | Definitions | Defines the AnswerList variable (rename it) |
make a list | Lists | To set up the QuestionList for new items |
make a list | Lists | To set up the AnswerList for new items |
The blocks should look like this:
Note that, unlike the original QuizMe app, the lists are empty to begin with. This is because with MakeQuiz and TakeQuiz, all data is created by the user of the app (it is user-generated).
Handle the User's entries
The first behavior you'll build is for handling the user's input. Specifically, when the user enters a question and answer and clicks submit, you'll use add item to list blocks to update the QuestionList and AnswerList.
You'll need the following blocks:
Block type | Drawer | Purpose |
SubmitButton.Click | SubmitButton | This event is triggered when the user clicks this button. |
add items to list (2) | Lists | Use these to add the data entered by the user to the lists |
global QuestionList | My Definitions | Plug into list slot of first add items to list |
QuestionText.Text | QuestionText | User's entry; plug it into item slot of first add items to list |
global AnswerList | My Definitions | Plug into list slot of second add items to list |
AnswerText.Text | AnswerText | User's entry; plug it into item slot of second add items to list |
The blocks should look like this:
How the blocks work
add items to list appends each item to the end of a list. Here, the app takes the text the user has entered in the QuestionText and AnswerText text boxes and appends each to the corresponding list.
The behavior updates the hidden QuestionList and AnswerList variables, but the changes are not shown to the user. To display these lists, create a procedure "displayQAs" which makes a text object with both lists and places it into the QuestionsAnswersLabel. Then make sure to call the procedure at the bottom of SubmitButton.Click. You'll need the following blocks:
Block type | Drawer | Purpose |
procedure "displayQAs" | Definitions | Put the code to display questions and answers here |
set QuestionsAnswersLabel.Text to | QuestionsAnswersLabel | Display the lists in this label. |
make text | Text | Make a text object out of two lists and a colon separator |
global QuestionList | My Definitions | Plug into make text |
text (":") | Text | Plug into make text |
global AnswerList | My Definitions | Plug into make text |
call displayQAs | My Definitions | Place in the bottom of the SubmitButton.Click event-handler |
The blocks should look like this:
How the blocks work
The displayQAs procedure is called after the lists are modified in SubmitButton.Click. The QuestionsAnswersLabel is modified to display the two lists separated by a colon. By default, App Inventor displays lists with surrounding parenthesis and spaces between items:
(item1 item2 item3)
Of course, this is not the ideal way to display the lists, but it will allow you to test your behavior for now. Later, you'll modify the displayQAs procedure to display a list on separate lines, and to display each question together with its corresponding answer.
Test this behavior. On the phone, enter a question and answer and click Submit. The app should display the single entry in the QuestionList, a colon, and then the single entry in the AnswerList. Add a second question and answer to make sure the lists are being created correctly.
Blanking Out the Question and Answer
When a user submits a question-answer pair, you should clear the QuestionText and AnswerText text boxes so they're ready for a new entry.
You'll need the following blocks:
Block type | Drawer | Purpose |
set QuestionText.Text to | QuestionText | To blank out question |
set AnswerText.Text to | AnswerText | To blank out answer |
text (2 blank ones) |
Here's how the blocks for the updated event handler should look:
How the blocks work
When the user submits a new question and answer, they are added to their respective lists. At that point, the text in the QuestionText and AnswerText is "blanked out" with empty text blocks (you create an empty text block by clicking on the the block's text and pressing the delete button).
Test the behavior. Add some questions and answers. When you submit them, the QuestionText and AnswerText should show only their hint (when a TextBox's Text property is empty it shows the hint until the user clicks it).
Storing the questions and answers in a database
As is, the app records the questions and answers entered by the user, but only in the phone's temporary memory. All variables in App Inventor are transient data-- they are stored on the phone and the data only "lives" through one run of an app. When the user closes the app, the data is lost.
The MakeQuiz app needs to record the questions and answers in a database. This will allow the quiz creator (teacher) to always edit the latest update of the quiz. It will also allow the TakeQuiz app to load the quiz and administer it to the student.
You'll use the TinyWebDB component to store and retrieve the QuestionList and AnswerList to and from a database. TinyWebDB allows you to store data in AppInventor-compliant databases that live on the web instead of on the phone.
The general scheme for storing a list persistently is the following: each time a new item is added to the list, use TinyWebDB to update the database version of the list. Then each time the app is opened (Screen1.Initialize event), reload the database version of the list into the variable.
Start by storing the QuestionList and AnswerList in the database each time the user enters a new pair. You'll need the following blocks:
Block type | Drawer | Purpose |
TinyWebDB1.StoreValue | TinyWebDB1 | for storing questions in the database |
text ("questions") | Text | Plug in "questions" as the tag of StoreValue |
global QuestionList | My Definitions | Plug into the value slot of StoreValue |
TinyWebDB1.StoreValue | TinyWebDB1 | For storing answers in the database |
text ("answers") | Text | Plug in "answers" as the tag of StoreValue |
global AnswerList | My Definitions | Plug into the value slot of StoreValue |
The blocks should look like this:
How the blocks work
The last two rows of blocks store data in the database. The tag arguments label the data being stored so that you can retrieve it later. The QuestionList is stored with a tag of "questions" while the AnswerList is stored with a tag of "answers".
Although our example uses "questions" as the tag, you should actually use your own tag, e.g., "DavesQuestions", because by default, the database entries of all App Inventor programs share the same namespace and so your list of questions may get confused with someone else's list of questions since you both used the same tag. For more information, see Creating a Custom TinyWebDB service.
Test this behavior. TinyWebDB stores information on the web, so you can open a browser page to test whether this behavior is working. By default, TinyWebDB stores information at http://appinvtinywebdb.appspot.com . Browse there to check if your data was stored as desired. There will be many entries, as the default service is for testing and is used by many programmers. If you search on the page, you should find the tag-value pairs you just stored. Because the default service is shared amongst programmers and apps, it is only for testing. Fortunately, setting up your own database service is straight forward. For more information, see Creating a Custom TinyWebDB service.
Loading Data from the Database
Once the blocks for storing the lists are working, you can add the blocks for loading the lists back in each time the app begins.
Loading data from a TinyWebDB database is a two-step process. First, you request the data by calling TinyWebDB.GetValue and providing a tag.
GetValue only requests the data from the web service. After the request is made, your app can do other things, such as responding to user actions, while it waits for the TinyWebDB web service to send the data. When the data arrives, a TinyWebDB.GotValue event is triggered. When that event occurs, the data requested is in a variable named "valueFromWebDB". The tag you requested is in the variable "tagFromWebDB". You can access these variables in the TinyWebDB.GotValue event handler.
In this case, the app needs to request two things from the TinyWebDB web service, the questions and the answers, so the Screen1.Initialize will make two calls to getValue. When the data arrives and the GotValue event-handler is triggered, the app should check the tag to see which request has arrived, and then load the corresponding list. Once the lists are both loaded, displayQAs can be called to display them.
You'll need the following blocks:
Block type | Drawer | Purpose |
Screen1.Initialize | Screen1 | Event handler triggered when app begins |
TinyWebDB.GetValue (2) | TinyWebDB | To request the stored QuestionList and AnswerList |
text ("questions") | Text | use as the tag to retrieve QuestionList |
text ("answers") | Text | use as the tag to retrieve AnswerList |
TinyWebDB.GotValue | TinyWebDB1 | Event handler triggered when data arrives |
ifelse | Control | Need to ask which GetValue request arrived |
= block | Math | Compare tagFromWebDB to "questions" |
text ("questions") | Text | This is the tag that was used to store QuestionList |
value TagFromWebDB | My Definitions | An argument of GotValue, specifies which request |
set QuestionList to | My Definitions | If TagFromWebDB is "questions" this list will be set |
set AnswerList to | My Definitions | If TagFromWebDB is not "questions" this list will be set |
value ValueFromWebDB (2) | My Definitions | This holds the value returned from database |
if | Control | Check if both the lists are loaded before displaying |
= block | Math | Compare the lengths of the lists |
length of list (2) | Lists | Check if the length of the lists are the same |
global QuestionList | My Definitions | Plug into one of the length of list blocks |
global AnswerList | My Definitions | Plug into the other length of list blocks |
call displayQAs | My Definitions | Display the newly loaded questions and answers |
The blocks should look like:
How the blocks work
When the app begins, the Screen1.Initialize event is triggered. The app calls TinyWebDB1.GetValue twice, once to request the stored QuestionList and once to request the stored AnswerList. After requesting the data, the app can handle other events while it waits for the web database to answer the request.
When the data arrives from the web database, the TinyWebDB1.GotValue event is triggered. Since two requests were made, it will be triggered twice, once for each list.
In the GotValue event-handler, the value returned is in the variable valueFromWebDB . The corresponding tag is in tagFromWebDB . The blocks first check to see which request has returned (even though the request for "questions" is made first in Screen1.Initialize, there is no guarantee that the QuestionList will arrive first). If the tag is "questions", the valueFromWebDB is put into the variable QuestionList. Otherwise (else) it is placed in the AnswerList.
At the bottom of GotValue, the app checks to see if both lists have been loaded-- one way to check is to see if the lengths of the lists are the same. If they are, displayQAs is called to display them on the phone.
Test this behavior. Select Restart Phone in the Blocks Editor. When the app initializes, it should display the previously entered questions and answers. If you close the app and restart again, the previous quiz should still appear.
The version of MakeQuiz you've created so far works: the user can create a quiz and it will be stored persistently in the database. If you can accept the fact that the quiz is displayed in an inelegant way, you can proceed to creating the sister app, TakeQuiz, which lets a user step through the quiz and answer the questions.
If you'd like to learn how to display list data in a more elegant fashion, step through the steps directly below.
Displaying a List on Multiple Lines
In the app you've built so far, the question and answer lists are displayed separately and with the default list display format, e.g.,
(question1 question2 question3): (answer1 answer2 answer3)
In this section, you'll modify the app so that the newly entered question-answer pairs are displayed in tandem, one on each line:
question1:answer1
question2:answer2
question3:answer3
Displaying the data in this way is non-trivial, so you'll start by first displaying a single list, the QuestionList, with each item on a separate line.
To display a list on separate lines, you must serialize it . This means to build a single text object with all the items of the list in it and a special character, '\n', between each item. A "\n" within a text object appears as a newline character when displayed. For example, suppose the user has entered two questions, "What is the capital of California?" and "What is the capital of New York?" and these have succesfully been added to the QuestionList . Your displayQAs procedure should build a text object from QuestionList that looks like: "\nWhat is the capital of California\nWhat is the capital of New York?" When displayed on the phone, such text would appear as: What is the capital of California? What is the capital of New York?
All the changes will occur in the procedure displayQAs. You'll remove the blocks within that procedure, and use a foreach block to build the text by successively adding each question. The blocks within the foreach should add the current item (question) to the end of the text object (QuestionsAnswersLabel) you are building. You'll need the following blocks:
Block type | Drawer | Purpose |
set QuestionsAnswersLabel.Text to | QuestionsAnswersLabel | Initialize the label to empty before repeatedly adding to it |
text (blank) | Text | plug into set QuestionsAnswersLabel.Text to |
foreach | Control | For each item in list, you'll concatenate it to the QuestionsAnswersLabel |
name var | already in foreach | The current item of foreach; rename it to "question" |
global QuestionList | My Definitions | Plug this into "in list" slot of foreach |
set QuestionsAnswersLabel.Text to | QuestionsAnswersLabel | Iteratively build the text |
make text | Text | build a text object |
QuestionsAnswersLabel.Text | QuestionsAnswersLabel | Plug into make text |
value question | My Definitions | Plug into make text (make sure you've renamed foreach variable to "question") |
text ("/n") | Text | New-line character so following text on another line |
The blocks should look like this:
How the blocks work
When the procedure is called, the QuestionsAnswersLabel.Text is first set to the empty text. This is in preparation for the foreach block that comes next, which will build the text in QuestionsAnswersLabel.Text incrementally.
The foreach block specifies that the blocks within it are to be executed once for every item in QuestionList. If there are three questions, then the inner blocks will be executed three times. Within the foreach, the current item being processed is named question.
On each iteration, QuestionsAnswersLabel.Text is modified. Each time it is set to its previous value, along with the newline character, "\n" and the current question being processed.
When the foreach completes, QuestionsAnswersLabel.Text will be text string with all the items separated by newline characters. As the app is processing each iteration of the foreach , the phone display doesn't change. But when it completes, the text in QuestionsAnswersLabel.Text is displayed on the phone and the items appear on separate lines.
Test the behavior. On the phone, add another question and answer and click Submit. Now, only the questions should be displayed and they should be displayed on separate lines.
Including the answer in DisplayQAs
The displayQAs procedure you've created ignores answers and only displays questions. In the next version of displayQAs , you'll display the data as question-answer pairs, e.g.,
What is the capital of California?: Sacramento
What is the capital of New York?: Albany
Modify displayQAs. Use an index variable to access each answer as the foreach block walks through the questions. You'll need the following blocks:
Block type | Drawer | Purpose |
def var ("answer") | Definitions | To temporarily store each answer |
def var ("answerIndex") | Definitions | To keep track of which answer (and question) you're on |
text ("text") | Text | Initialize the variable answer to text. |
number (1) | Math | Initialize the variable answerIndex to 1 |
set answerIndex to | My Definitions | Re-initialize answerIndex each time displayQAs called |
number (1) | Math | To re-initialize answerIndex to 1 |
set answer to | My Definitions | set this variable each time in the foreach |
select list item | Lists | select from the list AnswerList |
global AnswerList | My Definitions | plug into list slot of select list item |
global answerIndex | My Definitions | Plug into index slot of select list item |
set answerIndex to | My Definitions | In order to increment it each iteration through loop |
global answerIndex | My Definitions | To increment answerIndex , add 1 to itself. |
number (1) | Math | To increment answerIndex |
The blocks should look like this:
How the blocks work
The foreach only allows you to iterate through one list. In this case, there are two lists and you need to step through and select each answer as you proceed through the questions. The strategy is to use an index variable, as was done with the currentQuestionIndex in the QuizMe tutorial.
For this behavior, the index is a position in the AnswerList, so its named answerIndex. The index is set to 1 before the foreach begins. Within the foreach, it is used to select the current answer from the AnswerList, and then it is incremented.
Test this behavior. On the phone, add some more question/answer pairs. The display should now show each question with its corresponding answer, and each question:answer pair on separate lines.
Final MakeQuiz App
Scan the Sample App to your Phone
Scan the following barcode onto your phone to install and run MakeQuiz.
.
TakeQuiz
Building TakeQuiz is simpler; it can be built with a few modifications to the QuizMe app you completed earlier (if you have not completed that tutorial, do so now before continuing).
Begin by opening your QuizMe app, choosing SaveAs, and naming the new project "TakeQuiz." This will leave your QuizMe app as is and allow you to use a copy of it as the basis for TakeQuiz.
This version of MakeQuiz/TakeQuiz does not handle images, so first remove the images from the TakeQuiz app:
- In the Component Designer, choose each image from the media palette and delete it. Also delete the Image1 component, which will remove all references to it from the Blocks Editor.
- In the Blocks Editor, drag the PictureList to the trash.
Since TakeQuiz will work with database data, drag a TinyWebDB component into the Component Designer viewer.
Now modify the blocks so that the quiz given to the user is the one from the database. First, since there are no fixed questions and answers, remove all of the actual question and answer text blocks from the make a list blocks within the QuestionList and AnswerList. The blocks should look like this:
Now modify your Screen1.Initialize so that it calls TinyWebDB.GetValue twice to load the lists. The blocks should look like this:
Finally, drag out a TinyWebDB.GotValue event-handler. This event-handler should look similar to the one used in MakeQuiz, but here you only want to show the first question and none of the answers. The blocks should look like this:
How the Blocks Work
When the app begins, Screen1.Initialize is triggered and the app requests the questions and the answers from the web database. When each of those requests arrives, the TinyWebDB.GotValue event-handler is triggered. The app asks which request has come in, using tagFromWebDB , and places the valueFromWebDB into the appropriate list. If it is the QuestionList being loaded, the first question is selected from QuestionList and displayed.
Test the behavior. Click Restart Phone App. Does the first question from the quiz you created with MakeQuiz appear? Can you take the quiz just as you did with QuizMe (except for the pictures)?
Final Program (Take Quiz)
Scan the Sample App to your Phone
Scan the following barcode onto your phone to install and run Take Quiz.
Variations
Once you get MakeQuiz and TakeQuiz working, you might want to explore some variations. For example,
- Incorporate images into the apps. The images will need to be URLs from the web, and the test creator will need to specify these URLs as a third item in the MakeQuiz form.
- Allow the user to delete items from the questions and answers. You can let the user choose using the ListPicker component, and you can remove an item with the remove list item
- Allow multiple quizzes to be created. You'll need to conceptualize the list of quizzes as a list of lists.
Review
Here are some of the ideas covered in this tutorial:
- You can store data persistently in a web database with the TinyWebDB component.
- You retrieve data from a TinyWebDB database by requesting it with GetValue . When the web database returns the data, the TinyWebDB.GotValue event is triggered. At that point, you can put the data in a list or process it in some way.
- TinyWebDB data can be shared amongst multiple phones and apps.
MIT and Google are grateful to Professor David Wolber, CS Professor at The University of San Francisco, for developing this tutorial. Done with MakeQuiz and TakeQuiz? Return to the other tutorials here.
Tutorial Version:
Tutorial Difficulty:
- Advanced
Tutorial Type:
- Data Storage
- Game