Completing a Graphical Application

To see how Swing’s event-handling classes work in a Java program, you finish LottoMadness, the lottery simulation begun during Hour 14, “Laying Out a User Interface.”

At this point, LottoMadness is just a GUI. You can click buttons and enter text into text boxes, but nothing happens in response. In this workshop, you create LottoEvent, a new class that receives user input, conducts lotto drawings, and keeps track of the number of times you win. When the class is complete, you add a few lines to LottoMadness so that it makes use of LottoEvent. It often is convenient to divide Swing projects in this manner, with the GUI in one class and the event-handling methods in another.

The purpose of this application is to assess the user’s chance of winning a six-number lotto drawing in a lifetime. Figure 15.2 shows a screen capture of the program as it runs.

Figure 15.2. Running the LottoMadness application.

Image

Instead of using probability to figure this problem out, the computer conducts drawings in rapid succession and doesn’t stop until there’s a winner. Because the 6-out-of-6 win is extremely unlikely, the program also reports on any combination of three, four, or five winning numbers.

The interface includes 12 text fields for lotto numbers and two check boxes labeled Quick Pick and Personal. Six text fields, disabled for input, are used to display the winning numbers of each drawing. The other six text fields are for the user’s choice of numbers. Selecting the Quick Pick box chooses six random numbers for a user. Selecting Personal enables the user to select desired numbers.

Three buttons control the activity of the program: Stop, Play, and Reset. When the Play button is pressed, the program starts a thread called playing and generates Lotto drawings.

Pressing the Stop button stops the thread, and pressing Reset clears all fields so the user can start over. You learn about threads in Hour 19, “Creating a Threaded Program.”

The LottoEvent class implements three interfaces: ActionListener, ItemListener, and Runnable. The Runnable interface relates to threads and is covered in Hour 19. The listeners are needed to listen to user events generated by the application’s buttons and check boxes. The program does not need to listen to any events related to the text fields because they are used strictly to store the user’s choice of numbers. The user interface handles this storage automatically.

The class requires the use of the main Swing package, javax.swing, and Java’s event-handling package, java.awt.event.

The class has two instance variables:

gui, a LottoMadness object

playing, a Thread object used to conduct continuous lotto drawings

The gui variable is used to communicate with the LottoMadness object that contains the program’s GUI. When you need to make a change to the interface or retrieve a value from one of its text fields, you use the gui object’s instance variables.

For example, the play instance variable of LottoMadness represents the Play button. To disable this button in LottoEvent, you can use the following statement:

gui.play.setEnabled(false);

You can use the next statement to retrieve the value of the JTextField object got3:

String got3value = gui.got3.getText();

Listing 15.2 contains the full text of the LottoEvent class. Create a new empty Java file called LottoEvent in NetBeans to hold the source code.

Listing 15.2. The Full Text of LottoEvent.java


  1: import javax.swing.*;
  2: import java.awt.event.*;
  3:
  4: public class LottoEvent implements ItemListener, ActionListener,
  5:     Runnable {
  6:
  7:     LottoMadness gui;
  8:     Thread playing;
  9:
 10:     public LottoEvent(LottoMadness in) {
 11:         gui = in;
 12:     }
 13:
 14:     public void actionPerformed(ActionEvent event) {
 15:         String command = event.getActionCommand();
 16:         if (command.equals("Play")) {
 17:             startPlaying();
 18:         }
 19:         if (command.equals("Stop")) {
 20:             stopPlaying();
 21:         }
 22:         if (command.equals("Reset")) {
 23:             clearAllFields();
 24:         }
 25:     }
 26:
 27:     void startPlaying() {
 28:         playing = new Thread(this);
 29:         playing.start();
 30:         gui.play.setEnabled(false);
 31:         gui.stop.setEnabled(true);
 32:         gui.reset.setEnabled(false);
 33:         gui.quickpick.setEnabled(false);
 34:         gui.personal.setEnabled(false);
 35:     }
 36:
 37:     void stopPlaying() {
 38:         gui.stop.setEnabled(false);
 39:         gui.play.setEnabled(true);
 40:         gui.reset.setEnabled(true);
 41:         gui.quickpick.setEnabled(true);
 42:         gui.personal.setEnabled(true);
 43:         playing = null;
 44:     }
 45:
 46:     void clearAllFields() {
 47:         for (int i = 0; i < 6; i++) {
 48:             gui.numbers[i].setText(null);
 49:             gui.winners[i].setText(null);
 50:         }
 51:         gui.got3.setText("0");
 52:         gui.got4.setText("0");
 53:         gui.got5.setText("0");
 54:         gui.got6.setText("0");
 55:         gui.drawings.setText("0");
 56:         gui.years.setText("0");
 57:     }
 58:
 59:     public void itemStateChanged(ItemEvent event) {
 60:         Object item = event.getItem();
 61:         if (item == gui.quickpick) {
 62:             for (int i = 0; i < 6; i++) {
 63:                 int pick;
 64:                 do {
 65:                     pick = (int) Math.floor(Math.random() * 50 + 1);
 66:                 } while (numberGone(pick, gui.numbers, i));
 67:                 gui.numbers[i].setText("" + pick);
 68:             }
 69:         } else {
 70:             for (int i = 0; i < 6; i++) {
 71:                 gui.numbers[i].setText(null);
 72:             }
 73:         }
 74:     }
 75:
 76:     void addOneToField(JTextField field) {
 77:         int num = Integer.parseInt("0" + field.getText());
 78:         num++;
 79:         field.setText("" + num);
 80:     }
 81:
 82:     boolean numberGone(int num, JTextField[] pastNums, int count) {
 83:         for (int i = 0; i < count; i++) {
 84:             if (Integer.parseInt(pastNums[i].getText()) == num) {
 85:                 return true;
 86:             }
 87:         }
 88:         return false;
 89:     }
 90:
 91:     boolean matchedOne(JTextField win, JTextField[] allPicks) {
 92:         for (int i = 0; i < 6; i++) {
 93:             String winText = win.getText();
 94:             if ( winText.equals( allPicks[i].getText() ) ) {
 95:                 return true;
 96:             }
 97:         }
 98:         return false;
 99:     }
100:
101:     public void run() {
102:         Thread thisThread = Thread.currentThread();
103:         while (playing == thisThread) {
104:             addOneToField(gui.drawings);
105:             int draw = Integer.parseInt(gui.drawings.getText());
106:             float numYears = (float)draw / 104;
107:             gui.years.setText("" + numYears);
108:
109:             int matches = 0;
110:             for (int i = 0; i < 6; i++) {
111:                 int ball;
112:                 do {
113:                     ball = (int) Math.floor(Math.random() * 50 + 1);
114:                 } while (numberGone(ball, gui.winners, i));
115:                 gui.winners[i].setText("" + ball);
116:                 if (matchedOne(gui.winners[i], gui.numbers)) {
117:                     matches++;
118:                 }
119:             }
120:             switch (matches) {
121:                 case 3:
122:                     addOneToField(gui.got3);
123:                     break;
124:                 case 4:
125:                     addOneToField(gui.got4);
126:                     break;
127:                 case 5:
128:                     addOneToField(gui.got5);
129:                     break;
130:                 case 6:
131:                     addOneToField(gui.got6);
132:                     gui.stop.setEnabled(false);
133:                     gui.play.setEnabled(true);
134:                     playing = null;
135:             }
136:             try {
137:                 Thread.sleep(100);
138:             } catch (InterruptedException e) {
139:                 // do nothing
140:             }
141:         }
142:     }
143: }


The LottoEvent class has one constructor: LottoEvent(LottoMadness). The LottoMadness object specified as an argument identifies the object that is relying on LottoEvent to handle user events and conduct drawings.

The following methods are used in the class:

• The clearAllFields() method causes all text fields in the application to be emptied out. This method is handled when the user clicks the Reset button.

• The addOneToField() method converts a text field to an integer, increments it by one, and converts it back into a text field. Because all text fields are stored as strings, you have to take special steps to use them in expressions.

• The numberGone() method takes three arguments—a single number from a lotto drawing, an array that holds several JTextField objects, and a count integer. This method makes sure that each number in a drawing hasn’t been selected already in the same drawing.

• The matchedOne() method takes two arguments—a JTextField object and an array of six JTextField objects. This method checks to see whether one of the user’s numbers matches the numbers from the current lotto drawing.

The application’s actionPerformed() method receives the action events when the user clicks a button. The getActionCommand() method retrieves the label of the button to determine which component was clicked.

Clicking the Play button causes the startPlaying() method to be called. This method disables four components. Clicking Stop causes the stopPlaying() method to be called, which enables every component except for the Stop button.

The itemStateChanged() method receives user events triggered by the selection of the Quick Pick or Personal check boxes. The getItem() method sends back an Object that represents the check box that was clicked. If it’s the Quick Pick check box, six random numbers from 1 to 50 are assigned to the user’s lotto numbers. Otherwise, the text fields that hold the user’s numbers are cleared out.

The LottoEvent class uses numbers from 1 to 50 for each ball in the lotto drawings. This is established in Line 113, which multiplies the Math.random() method by 50, adds 1 to the total, and uses this as an argument to the Math.floor() method. The end result is a random integer from 1 to 50. If you replace 50 with a different number here and on Line 65, you could use LottoMadness for lottery contests that generate a wider or smaller range of values.

The LottoMadness project lacks variables used to keep track of things such as the number of drawings, winning counts, and lotto number text fields. Instead, the interface stores values and displays them automatically.

To finish the project, reopen LottoMadness.java in NetBeans. You only need to add six lines to make it work with the LottoEvent class.

First, add a new instance variable to hold a LottoEvent object:

LottoEvent lotto = new LottoEvent(this);

Next, in the LottoMadness() constructor, call the addItemListener() and addActionListener() methods of each user interface component that can receive user input:

// Add listeners
quickpick.addItemListener(lotto);
personal.addItemListener(lotto);
stop.addActionListener(lotto);
play.addActionListener(lotto);
reset.addActionListener(lotto);

Listing 15.3 contains the full text of LottoMadness.java after you have made the changes. The lines you added are shaded—the rest is unchanged from the previous hour.

Listing 15.3. The Full Text of LottoMadness.java


  1: import java.awt.*;
  2: import javax.swing.*;
  3:
  4: public class LottoMadness extends JFrame {
  5:     LottoEvent lotto = new LottoEvent(this);
  6:
  7:     // set up row 1
  8:     JPanel row1 = new JPanel();
  9:     ButtonGroup option = new ButtonGroup();
 10:     JCheckBox quickpick = new JCheckBox("Quick Pick", false);
 11:     JCheckBox personal = new JCheckBox("Personal", true);
 12:     // set up row 2
 13:     JPanel row2 = new JPanel();
 14:     JLabel numbersLabel = new JLabel("Your picks: ", JLabel.RIGHT);
 15:     JTextField[] numbers = new JTextField[6];
 16:     JLabel winnersLabel = new JLabel("Winners: ", JLabel.RIGHT);
 17:     JTextField[] winners = new JTextField[6];
 18:     // set up row 3
 19:     JPanel row3 = new JPanel();
 20:     JButton stop = new JButton("Stop");
 21:     JButton play = new JButton("Play");
 22:     JButton reset = new JButton("Reset");
 23:     // set up row 4
 24:     JPanel row4 = new JPanel();
 25:     JLabel got3Label = new JLabel("3 of 6: ", JLabel.RIGHT);
 26:     JTextField got3 = new JTextField("0");
 27:     JLabel got4Label = new JLabel("4 of 6: ", JLabel.RIGHT);
 28:     JTextField got4 = new JTextField("0");
 29:     JLabel got5Label = new JLabel("5 of 6: ", JLabel.RIGHT);
 30:     JTextField got5 = new JTextField("0");
 31:     JLabel got6Label = new JLabel("6 of 6: ", JLabel.RIGHT);
 32:     JTextField got6 = new JTextField("0", 10);
 33:     JLabel drawingsLabel = new JLabel("Drawings: ", JLabel.RIGHT);
 34:     JTextField drawings = new JTextField("0");
 35:     JLabel yearsLabel = new JLabel("Years: ", JLabel.RIGHT);
 36:     JTextField years = new JTextField("0");
 37:
 38:     public LottoMadness() {
 39:         super("Lotto Madness");
 40:         setLookAndFeel();
 41:         setSize(550, 270);
 42:         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 43:         GridLayout layout = new GridLayout(5, 1, 10, 10);
 44:         setLayout(layout);
 45:
 46:         // Add listeners
 47:         quickpick.addItemListener(lotto);
 48:         personal.addItemListener(lotto);
 49:         stop.addActionListener(lotto);
 50:         play.addActionListener(lotto);
 51:         reset.addActionListener(lotto);
 52:
 53:         FlowLayout layout1 = new FlowLayout(FlowLayout.CENTER,
 54:             10, 10);
 55:         option.add(quickpick);
 56:         option.add(personal);
 57:         row1.setLayout(layout1);
 58:         row1.add(quickpick);
 59:         row1.add(personal);
 60:         add(row1);
 61:
 62:         GridLayout layout2 = new GridLayout(2, 7, 10, 10);
 63:         row2.setLayout(layout2);
 64:         row2.add(numbersLabel);
 65:         for (int i = 0; i < 6; i++) {
 66:             numbers[i] = new JTextField();
 67:             row2.add(numbers[i]);
 68:         }
 69:         row2.add(winnersLabel);
 70:         for (int i = 0; i < 6; i++) {
 71:             winners[i] = new JTextField();
 72:             winners[i].setEditable(false);
 73:             row2.add(winners[i]);
 74:         }
 75:         add(row2);
 76:
 77:         FlowLayout layout3 = new FlowLayout(FlowLayout.CENTER,
 78:             10, 10);
 79:         row3.setLayout(layout3);
 80:         stop.setEnabled(false);
 81:         row3.add(stop);
 82:         row3.add(play);
 83:         row3.add(reset);
 84:         add(row3);
 85:
 86:         GridLayout layout4 = new GridLayout(2, 3, 20, 10);
 87:         row4.setLayout(layout4);
 88:         row4.add(got3Label);
 89:         got3.setEditable(false);
 90:         row4.add(got3);
 91:         row4.add(got4Label);
 92:         got4.setEditable(false);
 93:         row4.add(got4);
 94:         row4.add(got5Label);
 95:         got5.setEditable(false);
 96:         row4.add(got5);
 97:         row4.add(got6Label);
 98:         got6.setEditable(false);
 99:         row4.add(got6);
100:         row4.add(drawingsLabel);
101:         drawings.setEditable(false);
102:         row4.add(drawings);
103:         row4.add(yearsLabel);
104:         years.setEditable(false);
105:         row4.add(years);
106:         add(row4);
107:
108:         setVisible(true);
109:     }
110:
111:     private void setLookAndFeel() {
112:         try {
113:             UIManager.setLookAndFeel(
114:                 "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"
115:             );
116:         } catch (Exception exc) {
117:             // ignore error
118:         }
119:     }
120:
121:     public static void main(String[] arguments) {
122:         LottoMadness frame = new LottoMadness();
123:     }
124: }



Note

The book’s website at www.java24hours.com contains a link to an applet version of the LottoMadness program. At the time of this printing, 410,732,244 drawings have been conducted, which equals 3.9 million years of twice-weekly drawings. There have been 6,364,880 3-out-of-6 winners, 337,285 4-out-of-6 winners, 6,476 5-out-of-6 winners, and 51 6-out-of-6 winners (roughly one out of every 8 million drawings). The first person to win this fictional lottery was Bill Teer on August. 14, 2000, more than four years after the applet went online. His numbers were 3, 7, 1, 15, 34, and 43, and it only took him 241,225 drawings (2,319.47 years) to win.


After you add the shaded lines, you can run the application, which is capable of testing your lotto skills for thousands of years. As you might expect, these lotteries are an exercise in futility. The chance of winning a 6-out-of-6 lotto drawing in a lifetime is extremely slim, even if you live as long as a biblical figure.

Summary

You can create a professional-looking program with a modest amount of programming by using Swing. Although the LottoMadness application is longer than many of the examples you have worked in during the last 14 hours, half of the program was comprised of statements to build the interface.

If you spend some time running the application, you become even more bitter and envious about the good fortune of the people who win these six-number lottery drawings.

My most recent run of the program indicates that I could blow $27,000 and the best 266 years of my life buying tickets, only to win a handful of 4-out-of-6 and 3-out-of-6 prizes. In comparison to those odds, the chance to make your Java programming skills pay off is practically a sure thing.

Q&A

Q. Do you need to do anything with the paint() method or repaint() to indicate that a text field has been changed?

A. After the setText() method of a text component is used to change its value, you don’t need to do anything else. Swing handles the updating necessary to show the new value.

Q. Why do you often import a class and also one of its subclasses, as in Listing 15.1 when you import java.awt.* and java.awt.event.*? Could the first of these statements include the second?

A. Though the names of the java.awt and java.awt.event packages look like they are related, there’s no such thing as inheritance for packages in Java. One package cannot be a subpackage of another.

When you use an asterisk in an import statement, you are making all the classes in a package available in a program.

The asterisk only works on classes, not packages. The most a single import statement can load is the classes of a single package.

Q. Why is the actor Michael J. Fox identified by his middle initial?

A. There already was a Michael Fox in the Screen Actor’s Guild, forcing the future Family Ties and Back to the Future star to choose another name for his professional work. Michael Andrew Fox is his real name, but he didn’t like the sound of Andrew or Andy Fox and Michael A. Fox sounded like he was admiring his own good looks.

He settled on Michael J. Fox as an homage to the character actor Michael J. Pollard.

The other Michael Fox was an actor who appeared on episodes of Perry Mason, Burke’s Law, and numerous other TV shows and movies until his death in 1996.

Workshop

After the LottoMadness program has soured you on games of chance, play a game of skill by answering the following questions.

Quiz

1. Why are action events called by that name?

A. They occur in reaction to something else.

B. They indicate that some kind of action should be taken in response.

C. They honor cinematic adventurer Action Jackson.

2. What does this signify as the argument to an addActionListener() method?

A. this listener should be used when an event occurs.

B. this event takes precedence over others.

C. this object handles the events.

3. Which component stores user input as integers?

A. JButton

B. JTextField or JTextArea

C. Neither A nor B

Answers

1. B. Action events include the click of a button and the selection of an item from a pull-down menu.

2. C. The this keyword refers to the current object. If the name of an object is used as an argument instead of the this statement, that object would receive the events and be expected to handle them.

3. B. JTextField and JTextArea components store their values as text, so you must convert their values before you can use them as integers, floating-point numbers, or other nontext values.

Activities

If the main event of this hour didn’t provide enough action for your tastes, interface with the following activities:

• Add a text field to the LottoMadness application that works in conjunction with the Thread.sleep() statement in the LottoEvent class to slow down the rate that drawings are conducted.

• Modify the LottoMadness project so it draws five numbers from 1 to 90.

To see Java programs that implement these activities, visit the book’s website at www.java24hours.com.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.91.196