Lesson 3.4 Buttons & Text
The Button Class
In Lesson 2 you met the Label object which doesn't know a lot of tricks. The button's middle name, by contrast, is action. If you want something to happen in your Java program, use a Button object to start it off.
In Java, buttons are instance of the Button class, located in the java.awt package. Buttons normally contain text and respond to being “clicked” or “pressed”. Clicking or pressing a button usually triggers an action in your program.
Every Button object, like all of the other objects in the java.awt package, has a "silent" partner which really does all of the work. This silently-created partner is called the button's peer.
A peer is a native component created by the operating-system that "stands-in" for the Java Button object. You issue commands to the Button object and the Button object forwards those comands to the peer. If the user clicks on the peer, the peer notifies your Button object, and your Button object notifies you.
| As far as the user is concerned, however, the peer is the actual button, and your Java Button object is the illusion. But from your point of view, as a programmer, you never really interact with the peer at all. |
Creating and Placing a Button
The syntax you use to create a Button is almost exactly the same as that used to create a Label object. If anything, the Button constructor syntax is simpler than that of the Label class. Your only choice with Button is whether to create an empty Button, or to add some text. There is no alignment or other niceties.
Here is a small applet that does nothing more than create a Button object and add it to its surface.
import java.awt.*;
import java.applet.*;
public class DoIt extends Applet
{
Button doItBtn = new Button("Do It");
public void init()
{
add(doItBtn);
}
}
|
Handling Events
While the doItBtn in the DoIt applet is just fine as far as it goes, it really doesn't go far enough. Just like the Label, it doesn't appear to do anything. But appearances can be deceiving.
In reality, the doItBtn Button is doing quite a lot. Everytime you click on it or move your mouse over its surface, the Button object is generating events. The real problem is, nobody is listening.
Java 1.0 Events
The earliest Java 1.0 event model was a "hierarchical broadcast" model. Here's how it works.
In Java, every user-interface component must live inside a "container". The Java Container class is simply a special kind of component that is capable of holding other components [including other containers]. The only components which don't need a container are the top-level containers Frame and Window.
In the Java 1.0 model, when someone clicks a button, the button notifies its container. The container can choose to handle the event, or it can pass it up to its own container. It doesn't really matter whether the button's container really wants to deal with the button's events; the events are routed to the container anyway.
This model works just fine when containers only have to deal with one or two components. When programmers create more sophisticated applications--those containing hundreds of components--each of those components starts notifying their containers anytime anything at all happens. As a result, the applications slow to a crawl, even under a light load. The communications overhead is too great.
You'll learn more about the Java 1.0 model in Lesson 5. For right now, though, you'll learn to handle Java 1.1 events.
Java 1.1 Events
The Java 1.1 event model works like subscribing to a newsletter or magazine. You can tell a component to notify you when a certain event occurs--you can "subscribe" so to speak.
When that event occurs, the component notifies only those objects which have subscribed to the particular event. If no one subscribes, then the component keeps the event information to itself.
ActionEvents
When a Button object is clicked, it generates a specific kind of event object called an ActionEvent.
| ActionEvent objects are not specific to buttons; if you press the ENTER key inside a TextField object, or, if you double-click an entry in a List object, the component will generate an ActionEvent object. |
To perform an action when a Button object is clicked you must:
- Tell Java that you are prepared to deal with ActionEvent objects; that you are ActionEvent certified, so-to-speak.
- Tell the Button object to notify you whenever it is clicked.
- Write a method to deal with the ActionEvent object when it arrives.
Let's fix DoIt, so that it actually "does it", OK? We'll call the fixed version ReallyDoIt.
Step 1: Preparation
Preparation is simple. You simply import the package containing the ActionEvent class, and tell Java that you are prepared to deal with ActionEvents when they arrive. You do that by adding the line
| implements ActionListener |
to the ReallyDoiIt class header. Here's what your code should look like after making this change.
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class ReallyDoIt extends Applet
implements ActionListener
{
Button doItBtn = new Button("Do It");
public void init()
{
add(doItBtn);
}
} |
Step 2: Get on the List
The second step is to tell the Button object, doItBtn, to notify us if an ActionEvent is generated, and to pass it our way. We do this by sending the addActionListener(this) message.
The addActionListener() method tells doItBtn to add a subscriber to its internal list of components that should be notified whenever an ActionEvent occurs. The argument this tells doItBtn where to send the notification: to our applet.
Here's ReallyDoIt.java after Step 2.
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class ReallyDoIt extends Applet
implements ActionListener
{
Button doItBtn = new Button("Do It");
public void init()
{
doItBtn.addActionListener(this);
add(doItBtn);
}
} |
Step 3: Write Your Script
How do you suppose that the Button object, doItBtn, will notify you when an ActionEvent occurs? What it will do is send a particular message, passing along the ActionEvent object as an argument. The name of the message--and the method you must write-is actionPerformed().
Inside the actionPerformed() method, you can carry out the actions you want to occur when doItBtn is clicked.
Let's put what you've learned in the earlier lessons in this lesson to work by making doItBtn into a primitive Web counter. Here's how:
- Add a primitive int field called timesPressed, and initialize it to zero.
- Add an actionPerformed() method, and inside the method, carry out the following steps:
- Increment the timesPressed field.
- Create a String local variable called btnText that says "I've been pressed X times". Use the String concatenation operator to replace the X with the actual number of times doItBtn has been pressed.
- Send doItBtn the setLabel() message, passing btnText as the argument.
| To change the text that appears on the face of a Button object, you use the setLabel() and getLabel() methods, unlike the Label object where you use the setText() and getText() methods. |
That's all there is to it. Here's the code for the finished ReallyDoIt applet as well as the applet itself.
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class ReallyDoIt extends Applet
implements ActionListener
{
int timesPressed = 0;
Button doItBtn = new Button(
" I've been pressed 0 times ");
public void init()
{
doItBtn.addActionListener(this);
add(doItBtn);
}
public void actionPerformed(ActionEvent e)
{
timesPressed++;
String btnText =" I've been pressed " +
timesPressed + " times ";
doItBtn.setLabel(btnText);
}
}
|
TwoButton.java
While the ReallyDoIt applet really does it, it only does one thing. What do you do if you want to have two buttons, each doing something different?
To do this, you have two choices:
- Have both buttons send their event notifications to the same actionPerformed() method, and inside the method, determine which Button object sent you the ActionEvent. This is the model used in Java 1.0, so we'll postpone learning how to do that until we cover the action() method in Lesson 5.
- Create separate "event-handler" classes to carry out the work of each component. That's what we'll do here.
Introducing Inner Classes
Such separate event-handler classes are not put in separate files like the classes you've been creating so far. Instead, they are created right inside your existing class definition like this:
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class TwoButton extends Applet
{
Button red = new Button(" Red ");
Button green = new Button(" Green ");
public void init()
{
add(red);
add(green);
}
class DoRed implements ActionListener
{
public void
actionPerformed(ActionEvent e)
{
// Actions when red is clicked
}
} |
class DoGrn implements ActionListener
{
public void
actionPerformed(ActionEvent e)
{
// Actions when green is clicked
}
} |
}
|
The class TwoButton contains two attributes--the Button objects red and green--and a single method, init(). It also contains two inner class definitions, each specifically designed to handle ActionEvents generated by pressing one particular Button object.
Here's what you should notice about these two class definitions:
- They are created inside the TwoButton class definition, but not inside a method. Notice the closing brace that marks the end of the TwoButton class following the defintion of the doGrn class.
- Neither class needs to be public because they will only be used in the context of the TwoButton class.
- Both classes implement ActionListener, signaling the Java compiler that they are certified to handle ActionEvents. Neither class extends Applet, like TwoButton.
- Both classes contain an actionPerformed() method, identical to the method you wrote for the ReallyDoIt applet. Because both classes have declared that the implement ActionListener, Java will try to call their actionPerformed() method when an ActionEvent occurs.
|
A Problem with TwoButton.class?
|
| If you download and compile the code for TwoButton.java, you might be surprised to find that even though the DoGrn and DoRed classes are defined inside the TwoButton class, the machine code is not stored inside the TwoButton.class file.
Instead, Java creates a separate class file for every class, regardless of where the definition occurs. Because DoGrn and DoRed are inner classes, Java combines the names of the classes and produces three class files when you compile TwoButton.java:
- TwoButton.class
- TwoButton$DoGrn.class
- TwoButton$DoRed.class
To run the applet, all three class files must be present. |
Doing the Work
If you compile and run TwoButton.java, you'll see that it creates its two buttons successfully, but neither button does anything. You need to write some code to carry out you wishes.
Since we've named the Button objects red and green, let's have each of them set the applet's background color to the appropriate color, and then request that the applet repaint itself. You can do that by adding the following lines to the actionPerformed() method in doRed:
setBackground(Color.red);
repaint(); |
and these similar lines to the actionPerformed() method in doGrn:
setBackground(Color.green);
repaint(); |
Connecting the Pieces
The final step is to create a DoRed object and a DoGrn object, and then to have each Button object add the appropriate event-handler object to its list of subscribers [listeners].
Do that by adding the following four lines to the TwoButton init() method:
DoRed redHandler = new DoRed();
red.addActionListener(redHandler);
DoGrn greenHandler = new DoGrn();
green.addActionListener(greenHandler); |
After you compile your applet [and build an HTML file to host it] you'll find that pressing the "Red" button causes the background to turn red and pressing the "Green" button causes the background to turn green.
Meet the TextField
A Button object is useful when you want to carry out an action, and a Label object is useful when you want to impart some information to your user. When you need to get some information, however, nothing beats a TextField.
A TextField object is a single-line input area where the user of your applet can enter text. The TextField class has four different constructors:
|
Constructor
|
Meaning
|
| TextField() |
An empty TextField, usually big enough to hold one or two characters. |
| TextField(int n) |
A TextField big enough to hold n characters of average width. |
| TextField(String s) |
A TextField holding the String s. The field will be sized accordingly. |
| TextField(String s, int n) |
A TextField big enough to hold n characters, initially filled with the String s. |
Creating TextFields
Here's an applet that creates four TextField objects named oneTF, twoTF, threeTF, and fourTF :
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class TF extends Applet
{
TextField oneTF = new TextField();
TextField twoTF = new TextField(20);
TextField threeTF =
new TextField("No. 3");
TextField fourTF =
new TextField("No. 4", 20);
public void init()
{
add(oneTF);
add(twoTF);
add(threeTF);
add(fourTF);
}
}
|
Data-In & Data-Out
When you use TextField objects, the user types information into the TextField, and you use the getText() method to retrieve the information and to store it in a String, like this:
| String oneStr = oneTF.getText(); |
TextField objects also generate ActionEvents if the user presses the ENTER key while entering data. Generally, however, you won't assign each TextField object to a speparate ActionListener class, like you did for TwoButton.java. Instead, you'll normally use a Button connected to an ActionListener. When the Button is pressed, you'll retrieve the information from all of your fields.
Something to Talk About
Here's an exercise to make sure you understand how to handle Button events. Copy the code from TF.java [the last program in this the Buttons section], and make the following changes:
- Add a Button object and a Label object as fields.
- Add the Button and the Label to your applet surface inside the init() method.
- Add an actionPerformed() method to your TF class, and make the necessary changes to the class header so that Java knows it is there. In the actionPerformed() method, retrieve the text from all four TextFields, concatenate the text into a single String, and place the result in the Label object.
- Add the necessary code to init() so that your applet is notified when your Button object is pressed.
Please continue to the next section of this lesson.
|