The application below shows a fully functioning combobox quiz. As with our previous examples, the content (title text, question text, and answer choices) are created at run-time. As a user interface component, a combobox is similiar to a radiobutton in that the user can only select one answer to any given question. The most significant difference from radiobuttons will be realized in our problem set up, where we no longer have to place each answer choice on the stage.
The Code
The following classes must be imported, and in addition the library must contain the ComboBox class. If you add a combobox to the stage and then delete it, it will persist in the library.
import fl.controls.ComboBox;
For ease of positioning and clearing problems, we create a "question holder" on which the question text and answer components will be placed.
var qHolder:MovieClip = new MovieClip();
qHolder.x = 30;
qHolder.y = 60;
addChild(qHolder);
The following variables control the size of questions and answers, including the font color and font size for the questions. Notice that we only specify answer width since all answers will fit in the same combobox. This information can be specified in an external xml file as well.
var qHeight:Number = 50;
var qWidth:Number = 500;
var aWidth:Number = 300;
var qSize:Number = 16;
var qColor:uint = 0x000000;
Set the title for the quiz within the script so that this can be generalized as we move toward xml data on remaining pages of this tutorial.
txtTitle.text = "History Quiz";
We create an array containing the two questions in this quiz:
var arrQuestion:Array = [ "What was the name of the Simon Stevin 1586 pamphlet that popularized decimal?", "What people wrote the collection of problems in the Rhind Papyrus?"];
We use an array of two arrays, each containing the answer choices for the respective questions:
var arrAnswers:Array = [["Liber Abbaci","Nine Chapters","The Tenth","Arithmetica","Principia"], ["English","Germans","Greeks", "Romans", "Indians", "Egyptians","Chinese"]];
The following array contains the indices of the correct answers to the two questions:
var arrCorrect:Array = [2, 3];
We need an array containing the indices of the user answers, initialized to -1 so that initially the user sees the "prompt" instead of one of the selection choices.
var arrUserAnswers:Array = [-1,-1];
We create a 0-1 array to signal that a question has the correct answer selected as well as variables for the number of questions for this set and for an "index" for the current question. In the setup function that follows, we will put all of the answer choices within a single combobox object, called cb.
var arrDone:Array = [0,0];
var numExamples:int = arrQuestion.length;
var index:int = 0;
var cb:ComboBox;
The setup function removes the old question and combobox, and adds the current question and combobox as children to qHolder. This is the heart of the script.
function setup():void {
var j:int;
/*
Since qHolder has no children other than the question and combobox for answers, we remove all of its children to clear the previous question.
*/
while (qHolder.numChildren > 0) {
qHolder.removeChild(qHolder.getChildAt(0));
}
// Update problem counter and clear feedback for the new problem.
txtCounter.text = "Problem " + String(index+1) + " of " + String(numExamples) + ".";
txtFeedback.text = "";
/*
The format of the question is set inside of the setup function in case the user wants to add the ability to handle custom formatting for individual questions in the same problem set.
*/
var qFormat:TextFormat = new TextFormat('Arial',qSize,qColor);
/*
We build the text field according to specified parameters and add it as a child of qHolder.
*/
var qField:TextField = new TextField();
qField.width = qWidth;
qField.height = qHeight;
qField.x = 0;
qField.y = 0;
qField.multiline = true;
qField.wordWrap = true;
qHolder.addChild(qField);
// Set the format and content of the textfield.
qField.defaultTextFormat = qFormat;
qField.text = String(index+1) + ". " + arrQuestion[index];
// Make the "check" visible if this question has already been answered correctly.
if (arrDone[index] == 1) {
mcCheck.visible = true;
}
else {
mcCheck.visible = false;
}
// Set up the combobox with appropriate width and prompt.
cb = new ComboBox();
qHolder.addChild(cb);
cb.width = aWidth;
cb.prompt = "Select the answer";
/*
We set the number of visible rows when the combobox is pulled down to be the smaller of 8 or the number of choices specified for the problem.
*/
cb.rowCount = 8;
if (arrAnswers[index].length < cb.rowCount) {
cb.rowCount = arrAnswers[index].length;
}
// Place combobox in appropriate place.
cb.move(qField.x + 10, qField.y + (qField.height+5));
// Put each answer into the combobox item list.
for (j=0; j<arrAnswers[index].length; j++) {
cb.addItem({ label: arrAnswers[index][j] } );
}
// The combobox will display the last user answer or the prompt.
cb.selectedIndex = arrUserAnswers[index];
/*
The updateAnswer function will update the arrUserAnswer array whenever the user makes a selection in the combobox.
*/
cb.addEventListener(Event.CHANGE, updateAnswer);
// Place CHECK button in appropriate spot on the stage.
btnCheck.x = qHolder.x + 10;
btnCheck.y = qHolder.y + (qField.height+5)*(2);
}
Changing the ComboBox object will update the "user answer" array, but the answer is not not checked here.
function updateAnswer(evt:Event):void {
var cb:ComboBox = ComboBox(evt.currentTarget);
arrUserAnswers[index] = cb.selectedIndex;
}
This button checks the answer and updates the arrDone array as well as makes the check mark visible if the answer is correct.
btnCheck.addEventListener(MouseEvent.CLICK, chkAnswer);
function chkAnswer(mevt:MouseEvent):void {
if (arrCorrect[index] == arrUserAnswers[index]) {
txtFeedback.text = "CORRECT!";
arrDone[index] = 1;
mcCheck.visible = true;
}
else {
txtFeedback.text = "NO, TRY AGAIN.";
arrDone[index] = 0;
mcCheck.visible = false;
}
}
Next and Previous buttons simply step through the problems (referenced by "index") in a cycle.
btnNext.addEventListener(MouseEvent.CLICK, goNext);
function goNext(mevt:MouseEvent):void {
index++;
if (index > (numExamples - 1)) {
index = 0;
}
setup();
}
btnPrev.addEventListener(MouseEvent.CLICK, goPrev);
function goPrev(mevt:MouseEvent):void {
index--;
if (index < 0) {
index = numExamples - 1;
}
setup();
}
The "QUIT" button calls the finish function.
btnDone.addEventListener(MouseEvent.CLICK, donePressed);
function donePressed(mevt:MouseEvent):void {
finish();
}
We have created and linked in the library the EndClip movie clip containing a dynamic text box that we can write results into.
function finish():void {
var stMessage:String;
var mcFinal:EndClip = new EndClip();
var score:Number = 0;
mcFinal.x = 5;
mcFinal.y = 5;
addChild(mcFinal);
// The score is the number that are currently answered correctly.
for (var i=0; i<numExamples; i++) {
score += arrDone[i];
}
stMessage = "You got " + String(score) + " of " + String(numExamples) + " problems correct. Click anywhere to go back ";
stMessage = stMessage + "and work on them some more. Problems without a check mark are not answered correctly.";
mcFinal.txtReport.text = stMessage;
mcFinal.addEventListener(MouseEvent.CLICK, closePanel);
}
When the mcFinal clip is clicked, the mcFinal clip is removed entirely.
function closePanel(mevt:MouseEvent):void {
var mcFinal:MovieClip = mevt.currentTarget as MovieClip;
mcFinal.removeEventListener(MouseEvent.CLICK, closePanel);
removeChild(mcFinal);
setup();
}
setup();
Download
Download the well-commented fla file for the application shown above.