Adapting the QuizQuestion Class

Next, let's turn our attention to the QuizQuestion.as file. Our goal here is to modify the parameters in the constructor so that it uses XML instead. We have changed the QuizApp class so that it creates a series of new QuizQuestion instances in a loop. Each time through the loop, it sends an item of XML data to the constructor of the QuizQuestion class. Finally (as you may recall), QuizApp stores each QuizQuestion instance in an array.

So all we have to do is modify the constructor of the QuizQuestion class so that it just takes just one parameter, of the XML type. Then we'll modify the first few lines of this function, where the data is set. Everything else in the whole class will work exactly the same way. All we will have done is change out the source of the data. Here's the constructor function as it stands now: 

        public function QuizQuestion(theQuestion:String, theAnswer:int, ...answers) {
            //store the supplied arguments in the private variables:
            question = theQuestion;
            theCorrectAnswer = theAnswer;
            choices = answers;
            //create and position the textfield (question):
            questionField = new TextField();
            questionField.text = question;
            questionField.autoSize = TextFieldAutoSize.LEFT;
            questionField.x = questionX;
            questionField.y = questionY;
            addChild(questionField);
            //create and position the radio buttons (answers):
            var myGroup:RadioButtonGroup = new RadioButtonGroup("group1");
            myGroup.addEventListener(Event.CHANGE, changeHandler);
            for(var i:int = 0; i < choices.length; i++) {
                var rb:RadioButton = new RadioButton();
                rb.textField.autoSize = TextFieldAutoSize.LEFT;
                rb.label = choices[i];
                rb.group = myGroup;
                rb.value = i + 1;
                rb.x = answerX;
                rb.y = answerY + (i * spacing);
                addChild(rb);
            }
        }

First, change the insides of the parameter list to this version: 

public function QuizQuestion(snippet:XML) {

We get to just make up the parameter name, any meaningful name will do. I have chosen to call it "snippet," because it makes sense to me, it makes it sound like a fragment of the original XML, which is what it is. Each set of XML data coming to this class will look like this (I'll use the first one as an example):

<item>
  <question>What color is an orange?</question>
  <answer>2</answer>
  <choice>Orange</choice>
  <choice>Blue</choice>
  <choice>Purple</choice>
  <choice>Brown</choice>
</item> 

Now, the root node of this little bit of XML is <item>. Like I said previously, the curious little feature of working with XML is that the variable you set "becomes" the root node. So "snippet" takes the place of <item> when we make a path with dot syntax. So, for example, snippet.question is our way of gettting the question, snippet.answer is the answer, and snippet.choice[0] is the first choice.

That being the case, the first couple of lines of the constructor can be changed to:

question = snippet.question;
theCorrectAnswer = snippet.answer;

For the choices array, we're going to have to write another loop. But first, since we are no longer sending an array and then setting the choices array equal to that, the choices array never gets a value. So we need to instantiate the choices array as a new array before we can add anything to it (so add this line):

choices = new Array();

Now all we need to do is write another "for each" loop, so that the information in the XML can be pushed into the choices array:

for each(var string:String in snippet.choice) {
    choices.push(string);
}

Here's the completed QuizQuestion file:

package {
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.events.Event;
    import fl.controls.RadioButton;
    import fl.controls.RadioButtonGroup;
 
    public class QuizQuestion extends Sprite {
        private var question:String;
        private var questionField:TextField;
        private var choices:Array;
        private var theCorrectAnswer:int;
        private var theUserAnswer:int;
 
        //variables for positioning:
        private var questionX:int = 25;
        private var questionY:int = 25;
        private var answerX:int = 60;
        private var answerY:int = 55;
        private var spacing:int = 25;
 
        public function QuizQuestion(snippet:XML) {
            //store the supplied arguments in the private variables:
            question = snippet.question;
            theCorrectAnswer = snippet.answer;
            choices = new Array();
	    for each(var string:String in snippet.choice) {
		choices.push(string);
            }
            //create and position the textfield (question):
            questionField = new TextField();
            questionField.text = question;
            questionField.autoSize = TextFieldAutoSize.LEFT;
            questionField.x = questionX;
            questionField.y = questionY;
            addChild(questionField);
            //create and position the radio buttons (answers):
            var myGroup:RadioButtonGroup = new RadioButtonGroup("group1");
            myGroup.addEventListener(Event.CHANGE, changeHandler);
            for(var i:int = 0; i < choices.length; i++) {
                var rb:RadioButton = new RadioButton();
                rb.textField.autoSize = TextFieldAutoSize.LEFT;
                rb.label = choices[i];
                rb.group = myGroup;
                rb.value = i + 1;
                rb.x = answerX;
                rb.y = answerY + (i * spacing);
                addChild(rb);
            }
        }
 
        private function changeHandler(event:Event) {
            theUserAnswer = event.target.selectedData;
        }
        public function get correctAnswer():int {
            return theCorrectAnswer;
        }
        public function get userAnswer():int {
            return theUserAnswer;
        }
    }
}

Save the file, then test the application. It should behave exactly as the original did, but now it is getting its information from an external XML file instead!

In case you didn't know it already, the huge advantage of using XML as the data source is that it makes the application open-ended! In other words, you can add to (or delete from)  the questions that the application uses just by editing and saving (and maybe uploading) the XML file. The next time the application (the swf) runs, it will load the XML file again, and automatically use the new revised list of questions.

download Quiz - Finished.zip