8.
More useful features of E-objects
When
you use one window to open other windows, you do not want all windows to
disappear just because you close some subordinate window (which you can do with
someFrame.dispose()). Only the main window should be created as an
EFrame that terminates the whole program. The EFramelet class is provided for
this purpose. If you use new EFramelet(someEPanel) instead of EFrame,
you get a smaller frame, only 500 pixels wide and 300 pixels tall, and closing
that window will not terminate the program. EFramelet is otherwise the same as
EFrame, so you can use setSize and setTitle.
The
next sample program is named DiceThrow. When DiceThrow is to be run as a main
frame, use new
EFrame (new DiceThrow()). But if you wanted, for instance, to let a user of the BankView frame
take a break from work and run the DiceThrow, you would add a button to
BankView whose onClick method says new EFramelet (new DiceThrow()). So clicking that button
would make the dice game frame appear. But closing the DiceThrow frame would
not lose the BankView frame as well.
Listing 8 displays two dice
and two buttons. When the user clicks the first button, the dice start rolling
fairly fast. When the user clicks the second button, the dice stop rolling.
This program requires that you have six pictures named "1die.jpg",
"2die.jpg", ..., "6die.jpg", showing the six possible die
faces, in the same folder with the program that is using them. It uses a random
number generator: You get one of 0, 1, 2, 3, 4, 5 from randy.nextInt(6) (line 13). In general, randy.nextInt(n) gives one of the n nonnegative numbers less than n.
The accompanying figure shows how the frame could look after several clicks.
Initially, though, both dice show a 1-spot. If you would like to see this DiceThrow program in
action, click this execution link.
What makes the dice roll
fast? Lines 13-16 put a random die number on the labels and then set their timers
on delays of 0.15 and 0.09 seconds, respectively. Thereafter, every 0.12
seconds, each die's timer beeps, which executes the coding in the onBeep
method. Why those numbers? Because experimentation showed they work well.
The
ELabel method getPicture() returns the name of the current picture. The
onBeep coding (line 19) gets the first character charAt(0) of the
picture's name (which will be '1', '2', '3', '4', '5', or '6') and subtracts
the Unicode value for '0' from the first character's Unicode value. That will
produce one of the numbers 1, 2, 3, 4, 5, or 6. (One difference is that
a character can be part of a string but a number cannot; a number can be
doubled but a character cannot). The coding calculates the next number (lines
21-26; the next after 6 is 1). It then displays that picture and sets the delay
for the next beeping to 0.12 seconds. Note: n++ changes n to be 1 more
than it was, i.e., it increments n; and n-- changes n to be 1
less than it was, i.e., it decrements n.
Listing 8 The DiceThrow program
public
class DiceThrow extends EPanel
{
private java.util.Random randy = new java.util.Random(); // 1
private ETimer one = new RollTimer(); // 2
private ETimer two = new RollTimer(); // 3
private final int NUM_SIDES = 6; // 4
/** The View: Lay out the GUI components. */
public DiceThrow() // 5
{ add (new GoButton().text
("Click to roll the 2 dice"));
// 6
one.setPicture ("1die.jpg"); // 7
two.setPicture ("1die.jpg"); // 8
add (one, two); //
binds them together // 9
add (new StopButton().text ("Click to stop")); //10
} //======================
/** Controllers: React to click of button or
ENTER key. */
private class GoButton extends EButton //11
{ public void onClick() //12
{ int n = 1 +
randy.nextInt (NUM_SIDES);
//13
one.delay (150).setPicture (n + "die.jpg"); //14
n = 1 + randy.nextInt (NUM_SIDES); //15
two.delay (90).setPicture (n + "die.jpg"); //16
} //17
} //======================
private class RollTimer extends ETimer //18
{ public void onBeep() //19
{ int n =
this.getPicture().charAt (0) - '0';
//20
if (n >= NUM_SIDES) //21
{ n = 1;
//22
} //23
else //24
{ n++; //25
} //26
this.delay (120).setPicture (n + "die.jpg"); //27
} //28
} //======================
private class StopButton extends EButton //28
{ public void onClick() //29
{ one.stop();
//30
two.stop(); //31
} //32
} //======================
}
Additional features of
EFrames
The EFrame you get with new EFrame(someEPanel) (a) has a blank title on it, (b) has a width of 700
pixels, (c) has a height of 600 pixels, and (d) will terminate the program when
the window is closed (i.e., revert to the command window if that is where you
started it from). You may want to override these values.
The
EFrame method setSize(widthInt, heightInt) lets you set the width and
height of the frame, measured in pixels. And the EFrame method setTitle
(someString) puts that string in the title bar of the frame. For instance,
you can have a wide (520 pixels) but short (130 pixels) frame for the DiceThrow
program, with the title "Dice Throw" at the top of the window, using
the following main method (calling the setVisible method aligns the
components with the borders).
class DiceThrowApp // to
create a frame from the command window
{
public static void main (String[ ] args)
{ EFrame
frame = new EFrame (new DiceThrow());
frame.setSize (520, 130);
frame.setTitle ("Dice Throw");
frame.setVisible (true);
} //======================
}
Other
occasionally useful features of E-objects
The
following two methods in the EPanel class are sometimes useful. Each one pops
up a box that displays a message and gets input from the user. The names come
from JavaScript.
Ø
prompt(messageString) displays the message and waits for the user to
enter a string of characters. That string is returned to the calling statement.
Example: String
input = prompt("What is your name?") could obtain the user's name for later use.
Ø
confirm(messageString) displays the message and waits for the user to
indicate yes or no. It returns true if the user indicates yes. Example: The
test of if (confirm ("play again?"))... sees if the user wants to
play a game again.
The EButton class has the
same setPicture and getPicture methods that ELabel has, as well as a
convenience method picture("fileName"), analogous to
text("textName"), that returns the EButton itself, so it can be used
in, e.g., add
(new XButton().picture("y.gif")).
Each of the classes of
components has a setToolTipText(someString) method that displays an
explanatory message when the cursor lingers over the component.
If you know a little HTML,
you can change the format of the text displayed on EButtons and EFields. For instance, you could replace line 6 of
the DiceThrow program by the following, which underlines the word
"Click" and breaks for a new line after "roll".
add (new GoButton().text
("<html><u>Click</u>
to roll<br> the 2 dice</html>");
Exercise 8.1 Change DiceThrow to make the initial arrangement of
dice two random choices, rather than always starting with "1die.jpg".
Exercise 8.2 Change DiceThrow to have it stop rolling the dice
after each has changed 15 times, even if the user has not clicked the button
yet. Hint: Count to 30, then cancel delays.
Exercise 8.3 How would you change DiceThrow to have the same effect
if the pictures were named "die1.jpg" etc.?
Exercise 8.4* Revise DiceThrow to roll three dice at a time instead
of two.
Exercise 8.5* A paragraph in this section describes how you could have
the DiceThrow played from the BankView frame. Do what it says and try it out.
Exercise 8.6* The DiceThrow has both dice increase the number by 1
each time. How would you have the second die decrease by 1 while the first die
increases by 1?
Exercise 8.7** After working Exercise 8.5, revise it to make the
user stop playing the dice game after 40 seconds. Have a variable ETimer
itsStop = new Stopper(), have a click of the button execute
itsStop.delay(40000), and have the onBeep() method of the Stopper class execute
the dispose method for the DiceThrow frame.
9. Summary of E-object
classes
This section lists all
E-object classes and methods described in this material. [R] means the method
returns a value that is useful.
Group 1: These are
classes you normally do not make subclasses of.
new EFrame (somePanel) [subclass of Sun's JFrame] terminates the program when
closed
new EFramelet (somePanel) [subclass of Sun's JFrame] does not terminate the
program
x.dispose() //
close the frame
x.setTitle (titleString) //
set the title at the top of the frame
x.setSize (width, height) //
make it width pixels wide and height pixels tall
x.setVisible (true) //
make the frame appear on the screen;
// only needed if modified after creating it
new ELabel (textString) [subclass of Sun's JLabel]
new ELabel (textString,
fileString) put the text as a caption
under the picture in the file
x.getText() [R] // return the String written
on the component
x.setText (textString) //
change what the component says on it
x.getPicture() [R] // return the file name of the
picture on it ("" if none)
x.setPicture (fileString) //
place the picture of that file name on the label
new ETextArea
(numberOfRows, numberOfColumns)
[subclass of Sun's JScrollPane]
x.say (stringAppended) //
add this string as the next line in the textarea
x.getText() [R] // return the String written
on the component
x.setText (titleString) //
change what the component says on it; so "" clears it
Group 2: You subclass
an EPanel to add components and instance variables to it. You subclass the other three listed here to
react to events (the subclass defines onClick, onEnter, or onBeep).
new EPanel() [subclass
of Sun's JPanel]
x.add (someComponent) //
insert the component in the panel, book-reading order
x.add (c1, c2) //
insert a panel containing components c1 and c2
x.add (c1, c2, c3) //
insert a panel containing components c1, c2, and c3
x.lineBreak() [R] // return a component that makes
the start of a new row
x.popUp (messageString) //
pop-up a separate window with a message
x.prompt (someString) [R] //
pop-up a separate window with a message;
// return a String input by the user
x.confirm (someString) [R] //
pop-up a separate window with a request for
// confirmation; return a boolean
new EButton() [subclass
of Sun's JButton]
x.getText() [R] // return the String written
on the component
x.setText (textString) //
change what the component says on it
x.text (textString)
[R] // place the text on
the component; return the component
x.getPicture() [R] // return the file name of the
picture on it ("" if none)
x.setPicture (fileString) //
place the picture of that file name on the button
x.picture (fileString) //
place the picture of that name on the button; return the EButton
x.onClick() //
respond to click if a subclass redefines it
x.setEnabled (boolean) //
enable or disable response to a click
new EField() [subclass
of Sun's JField]
x.getText() [R] // return the String written
on the component
x.setText (textString) //
change what the component says on it
x.text (textString)
[R] // place the text on
the component; return the component
x.onEnter() //
respond to ENTER key if a subclass redefines it
x.setEnabled (boolean) //
enable or disable response to ENTER key
x.width (numCharacters)
// make it many characters
wide; return the EField itself
subclass of ETimer() [subclass
of ELabel]
x.onBeep() //
action to be taken; it must be defined in each subclass
x.delay (milliseconds)
[R] // execute onBeep() after a
delay; return the ETimer itself
x.stop() //
terminate the timer, so onBeep() will not be executed
and all ELabel methods, by inheritance
Group 3: These are
classes that are not essential to basic GUI programming. You normally do not make subclasses of
these.
new EOutputFile
(fileString) [subclass of Sun's
PrintWriter]
x.println (messageString) // prepare the information for printing to the file
x.close() // print all information to the file and terminate the connection