Solution for
Programming Exercise 7.1
THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to
the following exercise from this on-line
Java textbook.
Exercise 7.1:
Exercise 5.2 involved a class,
StatCalc.java, that could compute
some statistics of a set of numbers. Write an applet that uses
the StatCalc class to compute and display statistics of
numbers entered by the user. The applet will have an instance
variable of type StatCalc that does the computations.
The applet should include a JTextField
where the user enters a number. It should have four labels that
display four statistics for the numbers that have been entered:
the number of numbers, the sum, the mean, and the standard deviation.
Every time the user enters a new number, the statistics displayed
on the labels should change. The user enters a number by
typing it into the JTextField and pressing return.
There should be a "Clear" button that clears out all the data.
This means creating a new StatCalc object and resetting
the displays on the labels. My applet also has an "Enter" button
that does the same thing as pressing the return key in the
JTextField. (Recall that a JTextField generates
an ActionEvent when the user presses return, so your applet
should register itself to listen for ActionEvents from the
JTextField.) Here is my solution to this problem:
Discussion
In my applet, I used four labels to display results and another
label at the top of the applet to display a message to the user.
Aside from these labels, one row of the applet holds three other
components: a JTextField and two JButtons. The
content pane of the applet, which holds all the components,
uses a GridLayout with six rows. Five
of the rows hold JLabels. The other row contains a
JPanel that holds the JTextField and JButtons.
This JPanel uses a GridLayout with three columns and
just one row.
The applet has an init() method that creates and lays out the
components. Since I want the applet to look nice, I set a background color
and a foreground color for most of the components. (The JTextField
and JButtons use the default black foreground color.) I set the
labels to be opaque, to make sure that the background of each label will
actually be filled in with the label's background color.
After looking
at my first attempt, I decided to use a Monospaced font for the display
labels. In a Monospaced font, all the characters are the same size.
This makes it possible to line up the output values vertically by putting
the same number of characters in each label. To make it easy to
play with the colors and fonts, I declared three named constants
final static Color labelBG = new Color(240,225,200); // beige
final static Color labelFG = new Color(180,0,0); // dark red
final static Font labelFont = new Font("Monospaced", Font.PLAIN, 12);
I could then make one of the labels, such as countLabel, with
the commands:
countLabel = new JLabel("Number of Entries: 0");
countLabel.setBackground(labelBG);
countLabel.setForeground(labelFG);
countLabel.setOpaque(true);
countLabel.setFont(labelFont);
However, since there are four labels to create,
I wrote a subroutine to create a display label to show a given string:
private JLabel makeLabel(String text) {
// A utility routine for creating the labels that are used
// for display. This routine is called by init().
JLabel label = new JLabel(text);
label.setBackground(labelBG);
label.setForeground(labelFG);
labe.setOpaque(true);
label.setFont(labelFont);
return label;
}
Then, in the init() method, the labels can be created with
four lines, instead of 16:
countLabel = makeLabel(" Number of Entries: 0");
sumLabel = makeLabel(" Sum: 0.0");
meanLabel = makeLabel(" Average: undefined");
standevLabel = makeLabel(" Standard Deviation: undefined");
Utility routines like makeLabel() are very commonly used when
there are a lot of similar components to create. Note that when the labels
are first created, the text on the labels is appropriate for a dataset
that contains zero elements. In particular, if there are no data,
the average and standard deviation are undefined.
The applet registers itself to listen for action events from the
JTextField and from the JButtons. In the actionPerformed()
method, the function evt.getSource() is called to find the Object
that generated the event. This will be either the numberInput box,
the enterButton, or the clearButton. The source of the
event is checked to decide how to respond. (This is an alternative to checking the
event's action command.)
If the user clicked the "Clear" button, the response is to
create a new StatCalc object and to reset the display labels to
reflect the fact that there is no data in the dataset. It's important to
understand the effect of the command "stats = new StatCalc();".
The applet will continue to use the same StatCalc variable,
stats. However, now the variable refers to a new StatCalc
object. The new object does not yet have any data in its dataset.
The next time the user enters a number, the dataset will get its first value.
Always keep in mind the difference between variables and objects.
Also, keep in mind that you have to think in terms of changing the state
of the applet in response to events. I change the applet's state by
starting to use a new StatCalc object, and the display labels
are changed to keep them consistent with the new state.
When the user clicks the "Enter" button or presses return in the
JTextField, we have to get the user's input and add it to
the StatCalc object. This will cause the values of the
four statistics to change. We have to change the display labels to show
the new values. The code for getting the user's number from the
input box comes from Section 7.4. It includes a
check to make sure that the user's input is a legal number. If the
input is not legal, then I show an error message in the JLabel
named message and return from the actionPerformed()
method without entering any new data:
double num; // The user's number.
try {
num = Double.parseDouble(numberInput.getText());
}
catch (NumberFormatException e) {
// The user's entry is not a legal number.
// Put an error message in the message label
// and return without entering a number.
message.setText("\"" + numberInput.getText() +
"\" is not a legal number.");
numberInput.selectAll();
numberInput.requestFocus();
return;
}
The commands "numberInput.selectAll();" and "numberInput.requestFocus();"
are there as a convenience for the user. The first command selects all the text
in the number input box. The second command gives the input focus to the input
box. That way, the user can just start typing the next number, without
having to click on the input box or erase the content of the box.
(Since the contents of the box are selected, they will disappear automatically
when the user starts typing, to be replaced with the new input. A surprising number
of people have never learned that text selections work this way.)
Once we have the user's number,
the command "stats.enter(num);" adds the number num
into the dataset. The statistics about the data set can be obtained by
calling the functions stats.getCount(), stats.getSum(),
stats.getMean(), and stats.getStandardDeviation().
This information can be found by reading the source
code for the StatCalc class.
The Solution
/*
In this applet, the user enters numbers in a text field box.
After entering each number, the user presses return (or clicks
on a button). Some statistics are displayed about all the
numbers that the user has entered.
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class StatsApplet extends JApplet implements ActionListener {
final static Color labelBG = new Color(240,225,200);
final static Color labelFG = new Color(180,0,0);
final static Font labelFont = new Font("Monospaced", Font.PLAIN, 12);
JLabel countLabel; // A label for displaying the number of numbers.
JLabel sumLabel; // A label for displaying the sum of the numbers.
JLabel meanLabel; // A label for displaying the average.
JLabel standevLabel; // A label for displaying the standard deviation.
JLabel message; // A message at the top of the applet. It will
// show an error message if the user's input is
// not a legal number. Otherwise, it just tells
// the user to enter a number and press return.
JButton enterButton; // A button the user can press to enter a number.
// This is an alternative to pressing return.
JButton clearButton; // A button that clears all the data that the
// user has entered.
JTextField numberInput; // The input box where the user enters numbers.
StatCalc stats; // An object that keeps track of the statistics
// for all the numbers that have been entered.
public void init() {
/* Create all the objects used by the applet. The applet
will listen for action events from the buttons and from
the text field. A JTextField generates an ActionEvent
when the user presses return. */
stats = new StatCalc();
numberInput = new JTextField();
numberInput.setBackground(Color.white);
numberInput.addActionListener(this);
enterButton = new JButton("Enter");
enterButton.addActionListener(this);
clearButton = new JButton("Clear");
clearButton.addActionListener(this);
JPanel inputPanel = new JPanel(); // A panel that will hold the
// JTextField and JButtons.
inputPanel.setLayout( new GridLayout(1,3) );
inputPanel.add(numberInput);
inputPanel.add(enterButton);
inputPanel.add(clearButton);
countLabel = makeLabel(" Number of Entries: 0");
sumLabel = makeLabel(" Sum: 0.0");
meanLabel = makeLabel(" Average: undefined");
standevLabel = makeLabel(" Standard Deviation: undefined");
message = new JLabel("Enter a number, press return:",
JLabel.CENTER);
message.setBackground(labelBG);
message.setForeground(Color.blue);
message.setOpaque(true);
message.setFont(new Font("SansSerif", Font.BOLD, 12));
/* Use a GridLayout with 6 rows and 1 column, and add all the
components that have been created to the applet. */
setBackground(Color.blue);
getContentPane().setBackground(Color.blue);
getContentPane().setLayout( new GridLayout(6,1,2,2) );
getContentPane().add(message);
getContentPane().add(inputPanel);
getContentPane().add(countLabel);
getContentPane().add(sumLabel);
getContentPane().add(meanLabel);
getContentPane().add(standevLabel);
} // end init();
private JLabel makeLabel(String text) {
// A utility routine for creating the labels that are used
// for display. This routine is called by init().
JLabel label = new JLabel(text);
label.setBackground(labelBG);
label.setForeground(labelFG);
label.setFont(labelFont);
label.setOpaque(true);
return label;
}
public Insets getInsets() {
// Leave a 2-pixel border around the edges of the applet.
return new Insets(2,2,2,2);
}
public void actionPerformed(ActionEvent evt) {
// This is called when the user clicks one of the buttons or
// presses return in the input box. The response to clicking
// on the Enter button is the same as the response to pressing
// return in the JTextField.
Object source = evt.getSource(); // Object that generated
// the action event.
if (source == clearButton) {
// Handle the clear button by starting with a new,
// empty StatCalc object and resetting the display
// labels to show no data entered. The TextField
// is also made empty.
stats = new StatCalc();
countLabel.setText(" Number of Entries: 0");
sumLabel.setText(" Sum: 0.0");
meanLabel.setText(" Average: undefined");
standevLabel.setText(" Standard Deviation: undefined");
numberInput.setText("");
}
else if (source == enterButton || source == numberInput) {
// Get the user's number, enter it into the StatCalc
// object, and set the display on the display labels
// to reflect the new data.
double num; // The user's number.
try {
num = Double.parseDouble(numberInput.getText());
}
catch (NumberFormatException e) {
// The user's entry is not a legal number.
// Put an error message in the message label
// and return without entering a number.
message.setText("\"" + numberInput.getText() +
"\" is not a legal number.");
numberInput.selectAll();
numberInput.requestFocus();
return;
}
stats.enter(num);
countLabel.setText(" Number of Entries: " + stats.getCount());
sumLabel.setText(" Sum: " + stats.getSum());
meanLabel.setText(" Average: " + stats.getMean());
standevLabel.setText(" Standard Deviation: "
+ stats.getStandardDeviation());
}
/* Set the message label back to its normal text, in case it has
been showing an error message. For the user's convenience,
select the text in the TextField and give the input focus
to the text field. That way the user can just start typing
the next number. */
message.setText("Enter a number, press return:");
numberInput.selectAll();
numberInput.requestFocus();
} // end ActionPerformed
} // end StatsApplet
[ Exercises
| Chapter Index
| Main Index
]