3. Textfields


Our next example is a program that asks two simple questions. The user is to answer each question by typing the answer into a box provided for that purpose. The program's response will appear in the button or space where the user clicks or types. The frame is to look as follows when it first comes up for the user to see. If you would like to see this MathQuiz program in action, click this execution link.




When you solve a programming problem from scratch, you start with a drawing of how things look, as in the figure. Next you make a design for the solution. The first step of the solution is obviously to create a window or frame that will appear on the screen. As stated earlier, you can do this using java MathQuizApp with a main method that contains the single statement new EFrame (new MathQuiz()); or you can use an applet whose start method contains setContentPane (new MathQuiz()); either works.


The accompanying block contains a reasonable design for the program, once the frame is created. See how easily it follows from the picture. And once you have a design for a program, the Java coding follows easily almost line-for-line. Note: A textfield is a rectangular area where the user can type input.



1. Add to the frame the question "What is the square of 30?"

2. Add to the frame a textfield where the user can type the answer to the question.

3. Add to the frame the question "How many bytes in a KB?"

4. Add to the frame a textfield where the user can type the answer to the question.

5. Suspend action until the user clicks a button or types in the textfield.



Listing 3 contains the MathQuiz class. The logic of the constructor follows the design:


         Line 2 adds a label that contains the question "What is the square of 30?".

         Line 3 adds a textfield where the user will type the answer; this SquareField object is wide enough to hold at least 8 characters.

         Line 4 pushes the next component down to the front of the next row of components.

         Line 5 adds another label that displays "How many bytes in a KB?".

         Line 6 adds a textfield where the user will type the answer; this BytesField object is wide enough to hold at least 8 characters.


An EField has the same setText(someString), getText(), and text(someString) methods that ELabels and EButtons have. The first two are inherited from Sun's JTextField class. Remember that text(someString) returns the EField itself, so you can use that value within thisEFrame.add(someComponent). If you want to use color in your programs, go to this language link.


A subclass of EField is generally defined inside the EPanel subclass. You usually have just one method in it: public void onEnter() (see lines 8 and 17) will execute when the user presses the ENTER key within the textfield. A SquareField object is defined here to react to the ENTER key by first looking at the text the user has typed; line 9 does the looking. If the user typed "900", this textfield displays the answer "good!" (line 10), otherwise it displays the answer "you're wrong." (line 13). You can set the width of an EField, measured in characters, with the instance method width(numCharacters); it returns the EField object itself. Note that onEnter() is not called by coding anywhere in the program; it is called by the user's action of pressing the ENTER key.



Listing 3 The MathQuiz program


public class MathQuiz extends EPanel


/** The View: Lay out the GUI components. */


public MathQuiz() // 1

{ add (new ELabel ("What is the square of 30?")); // 2

add (new SquareField().width (8)); //in characters // 3

add (lineBreak()); // 4

add (new ELabel ("How many bytes in a KB?")); // 5

add (new BytesField().width (8)); //in characters // 6

} //======================



/** Controllers: React to ENTER key. */


private class SquareField extends EField // 7

{ public void onEnter() // 8

{ if (this.getText().equals ("900")) // 9

{ this.setText ("good!"); //10

} //11

else //12

{ this.setText ("you're wrong."); //13

} //14

} //15

} //======================



private class BytesField extends EField //16

{ public void onEnter() //17

{ if (this.getText().equals ("1024")) //18

{ this.setText ("good!"); //19

} //20

else //21

{ this.setText ("you're wrong."); //22

} //23

} //24

} //======================




Exercise 3.1 If you changed the question in line 2 of MathQuiz to "What is the cube root of 64?", how would you have to change the rest of the program to make the textfield give the right responses?

Exercise 3.2 Change MathQuiz so that, instead of displaying the answer in the textfield, a little box pops up with the answer. Hint: Review what the popUp method does.

Exercise 3.3 Add a third question to MathQuiz that asks, "What is the square root of 144?". Allow both right answers to the question to have the "good!" response.

Exercise 3.4* Change GeogQuiz so that, each time the user clicks the picture, it changes, from Rome.jpg to Ireland.jpg to grand_canyon.jpg and then back to Rome.jpg. Hint: Put the picture on an EButton instead of an ELabel, and use the getPicture method.


4. Instance variables


The OrderForm class is designed to let the user make a purchase. It has the user fill out his/her name, address, and credit card number. The user types this information in three different textfields. Moreover, the user can click a button to say whether the company can email ads to the user. The frame looks as follows when the program begins. If you would like to see this OrderForm program in action, click this execution link.




The three textfield components itsName, itsAddress, itsCard, and the button component itsAds, have to be available for use in two or more methods. So they are declared outside of all methods. This makes them instance variables of OrderForm. A declaration of an instance variable is exactly as for a local variable except that we put the word private at the beginning of each such declaration. This means that the compiler will not allow any class outside of the OrderForm class to mention an instance variable. This is encapsulation -- it guarantees the integrity of the data you keep in those variables. You can, however, mention an instance variable anywhere inside the OrderForm class.


Each OrderForm object you create has its own set of four instance variables (though we usually make only one OrderForm object in our programs). The button in the OrderForm class is declared in line 4 as follows:


private EButton itsAds = new AdsButton().text ("yes");


This creates the button and initializes what it says on it to the word "yes". It also has the variable itsAds refer to the button. Any use of itsAds elsewhere in the program is a reference to this button.


The three textfields are defined similarly. They are added to the overall EPanel by a new kind of add method that has two parameters, such as the following in line 6:


add (new ELabel ("What is your name?"), itsName);


This add(someComponent, someOtherComponent) method in the EPanel class creates a small EPanel that contains the two components and then adds that small EPanel to the OrderForm EPanel. The effect is to "bind together" the two components by the small EPanel, so they cannot appear on different rows of the display. Typically, the first component is a label and the second is a textfield or button. The EPanel class also has a three-component add method add(comp1, comp2, comp3) for "binding together" three related components:.


The OrderForm class uses textfields that are just plain EFields; they do not belong to a subclass of EField and thus they do not have an onEnter method that contains coding. This means that, when the user click the ENTER key in the textfield, nothing happens. But when the user clicks the SubmitButton, all of the information in the three textfields and on the button is written to a file. You may ignore lines 23-28 on EOutputFile if you wish (unless your instructor says otherwise). It is not used elsewhere in this material. It is only here to show that data can be written to a disk file by an application (not by an applet). The close method makes sure all data goes to the physical file.


Listing 4 The OrderForm program


public class OrderForm extends EPanel


private EField itsName = new EField().width (20); // 1

private EField itsAddress = new EField().width (20); // 2

private EField itsCard = new EField().width (20); // 3

private EButton itsAds = new AdsButton().text ("yes"); // 4



/** The View: Lay out the GUI components. */


public OrderForm() // 5

{ add (new ELabel ("What is your name?"), itsName); // 6

add (new ELabel ("What is your address?"), itsAddress); // 7

add (new ELabel ("What is your credit card #?"), // 8

itsCard); // 9

add (new ELabel ("Can we send you email ads?"), itsAds); //10

add (new SubmitButton().text ("Click here when done")); //11

} //======================



/** Controllers: React to click of button. */


private class AdsButton extends EButton //12

{ public void onClick() //13

{ if (this.getText().equals ("yes")) //14

{ this.setText ("no"); //15

} //16

else //17

{ this.setText ("yes"); //18

} //19

} //20

} //======================



private class SubmitButton extends EButton //21

{ public void onClick() //22

{ EOutputFile file = new EOutputFile ("data.txt"); //23

file.println (itsName.getText()); //24

file.println (itsAddress.getText()); //25

file.println (itsCard.getText()); //26

file.println (itsAds.getText()); //27

file.close(); //28

} //29

} //======================



Exercise 4.1 Change OrderForm by adding a request for the user's phone number.

Exercise 4.2 Change MathQuiz in Listing 3 to have one instance variable, a label named myAnswer. When a person enters a value in either textfield, display the response of "good!" or "you're wrong" in myAnswer instead of in the textfield itself.

Exercise 4.3* Change GeogQuiz in Listing 1 to have the displayed picture switch from Rome.jpg to Ireland.jpg or vice versa, every time a RightButton is clicked. Hint: Have an instance variable that stores a reference to the ELabel with the picture.

Exercise 4.4* Change OrderForm by adding two buttons for extra-cost shipping, one for express mail and one for airmail. Make them yes/no buttons that are initially "no". Set them so that making one "yes" changes the other to "no" (you can't have it both ways).



5. Basic String operations


If you already know about the String methods length, substring, and charAt, Unicodes, and using a plus sign for string concatenation, you can skip directly to the next section if you wish. This section does not describe new GUI features, but it provides further practice with what you have learned so far.


The ArtQuiz class has eight true/false questions (though the coding presented here only shows two of them, to save space). Each time the user clicks a button to give the answer, a smiley-face appears on a label named toReact if the answer is right and a frowny-face appears if the answer is wrong. Also, the total number right so far and the total number wrong so far are displayed on two labels named numRight and numWrong.


The frame looks as follows when the user has already clicked "true" for each question. If you would like to see this ArtQuiz program in action, click this execution link.





The variables numRight, numWrong, and toReact have to be available for use in two or more methods. So they are declared in lines 1-3 as instance variables of ArtQuiz objects, outside of all methods.


The numRight and numWrong labels are initialized with some text. The toReact label does not have an initial text, because it shows a picture (smiley-face or frowney-face) each time the user gives an answer. These labels are added to the EPanel as a group by the line 12 (adding all at once binds them together). The pictures are in a subfolder named "pic" (lines 15 and 21).


If you have a string of characters s, then s.length() is the number of characters in the string. For instance, "cat".length() is 3, "stew".length() is 4, and "".length() is zero. Also, s.charAt(0) is the character in the first position of s, s.charAt(1) is the character in the second positiion of s, etc. (numbering in Java normally starts from 0). For instance, "cat".charAt(0) is 'c' and "cat".charAt(2) is 't'. "cat".charAt(3) is not allowed.


Each character has a numeric value called its Unicode value. The Unicode for digit '0' is 48, for digit '1' is 49, up through 57 for digit '9'. Therefore, '3' minus '0' is the number 3, '7' minus '0' is the number 7, etc. In general, if c is a digit character, then c - '0' is the number that we represent in writing by the character c. We use this in lines 13-14 to add 1 to the count of right answers so far: Similarly, lines 16-17 compute the new count of wrong answers so far.


Putting a plus sign between two string values creates a new string consisting of the first string followed by the second string. This is called string concatenation. For instance, if s has the value "Bill", then s + " rules" has the value "Bill rules". Putting a plus sign between a numeric value and a string of characters, as in count+"right" where count is an int variable, converts the number to the character(s) that represent it in writing.


If s is any String value, the expression s.substring(1) is the string consisting of all characters except the first one. In general, s.substring(k) is all but the first k characters. You will see this substring String method used from time to time later in this material.



Listing 5 The ArtQuiz program


public class ArtQuiz extends EPanel


private ELabel numRight = new ELabel ("0 right"); // 1

private ELabel numWrong = new ELabel ("0 wrong"); // 2

private ELabel toReact = new ELabel(""); // no text // 3


/** The View: Lay out the GUI components. */


public ArtQuiz() // 4

{ add (new ELabel ("Mozart was a dancer")); // 5

add (new WrongButton().text ("true")); // 6

add (new RightButton().text ("false")); // 7

add (new ELabel ("Rembrandt was a painter")); // 8

add (new RightButton().text ("true")); // 9

add (new WrongButton().text ("false")); //10

// several more questions would be here

add (lineBreak()); //11

add (numRight, toReact, numWrong); //12

} //======================


/** Controllers: React to click of button or to ENTER key. */


private class RightButton extends EButton //13

{ public void onClick() //14

{ toReact.setPicture ("pic/smileyface.gif"); //15

int count = 1 + numRight.getText().charAt (0) - '0'; //16

numRight.setText (count + " right so far"); //17

} //18

} //======================


private class WrongButton extends EButton //19

{ public void onClick() //20

{ toReact.setPicture ("pic/frowneyface.gif"); //21

int count = 1 + numWrong.getText().charAt (0) - '0'; //22

numWrong.setText (count + " wrong so far"); //23

} //24

} //======================



Exercise 5.1 Change ArtQuiz to add the true/false question for "da Vinci was a sculptor".

Exercise 5.2 What difference would it make if you left out the "1+" part of lines 16 and 22? What if you also added 1 to count in lines 17 and 23?

Exercise 5.3 The ArtQuiz class starts giving bad information if the user answers more than 9 questions right. Change ArtQuiz so that it does not try to add any more to numRight when the count of right answers reaches 9; do the same for numWrong.

Exercise 5.4* There are e.g. 8 true/false questions altogether in the finished ArtQuiz class, a test-taker could click the correct answer to one of them 8 times and then show off the score of 8 right and 0 wrong. Change ArtQuiz to prevent this as follows: Each time a button is clicked, put a period at the beginning of its text, e.g., change "true" to ".true". But do not count it as an answer if its text already has a period (because it was already counted as an answer). Hint: Check whether the first character on the button == '.'.

Exercise 5.5* Add two instance variables to ArtQuiz, both of int type, both initialized to zero. One will keep count of the number of wrong answers and the other will keep track of the number of right answers. Replace lines 16 and 22 to use these two variables appropriately. Also include the protective change described in Exercise 5.3.


Link to next section (Part C)