 |
Introduction to Swing
|
This introduction
to using Swing in Java will walk you through the basics of
Swing. This covers topics of how to create a window, add
controls, postion the controls, and handle events from the
controls.
The
Main Window
Almost all GUI applications have a main or top-level
window. In Swing, such window is usually instance of
JFrame or JWindow.
The difference between those two classes is in simplicity – JWindow
is much simpler than JFrame (most noticeable are visual
differences - JWindow does not have a title bar, and does
not put a button
in the operating system task bar). So, your applications
will almost always start with a JFrame.
Though you can instantiate a JFrame and
add components to it, a good practice is
to encapsulate and group the code for a single
visual frame in a separate class. Usually,
I subclass the JFrame and initialize all
visual elements of that frame in the constructor.
Always
pass a title to the parent class constructor – that
String will be displayed in the title bar
and on the task bar. Also,
remember to always initialize frame size
(by calling setSize(width,height)), or your
frame will not be noticeable on the screen.
package com.neuri.handsonswing.ch1;
import javax.swing.JFrame;
public class MainFrame extends JFrame
{
public MainFrame()
{
super("My title");
setSize(300, 300);
}
}
Now
you have created your first frame, and
it is time to display it. Main frame is
usually displayed from the main method – but resist the urge
to put the main method in the frame class. Always try to separate the code
that deals with visual presentation from the code that deals with application
logic – starting and initializing the application is part of application
logic, not a part of visual presentation. A good practice is to create an Application
class, that will contain initialization code.
package com.neuri.handsonswing.ch1;
public class Application
{
public static void main(String[] args)
{
// perform any initialization
MainFrame mf = new MainFrame();
mf.show();
}
}
If
you run the code now, you will see an empty
frame. When you close it, something
not quite obvious will happen (or better
said, will not happen). The application will
not end. Remember that the Frame is just
a visual part of application, not application
logic – if you do not request application
termination when the window closes, your
program will still run in the background
(look for it in the process list). To avoid
this problem, add the following line to the
MainFrame constructor:
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Before
Java2 1.3, you had to register a window
listener and then act on the window closing
event
by stopping the application. Since Java2
1.3, you can specify a simple action that
will happen when a window is closed with
this shortcut. Other options are HIDE_ON_CLOSE
(the default – window is closed but
application still runs) and DO_NOTHING_ON_CLOSE
(rather strange option that ignores a click
on the X button in the upper right corner).
Adding
Components
Now is the time to add some components to
the window. In Swing (and the Swing predecessor,
AWT) all visual objects are subclasses of
Component class. The Composite pattern was
applied here to group visual objects into
Containers, special components that can contain
other components. Containers can specify
the order, size and position of embedded
components (and this can all be automatically
calculated, which is one of the best features
of Swing).
JButton
is a component class that represents a
general purpose button – it can have
a text caption or an icon, and can be pressed
to invoke an action. Let’s add the
button to the frame (note: add imports for
javax.swing.* and java.awt.* to the MainFrame
source code so that you can use all the components).
When
you work with JFrame, you want to put objects
into it’s content pane – special
container intended to hold the window contents.
Obtain the reference to that container with
the getContentPane() method.
Container
content = getContentPane();
content.add(new JButton("Button 1"));
If you try to add more buttons to the
frame, most likely only the last
one added will be displayed. That
is because the default behavior of
JFrame content
pane is to display a single component, resized to cover the entire area.
Grouping
Components
To
put more than one component into
a place intended for a single component,
group them
into a container. JPanel is a general
purpose
container, that is perfect for grouping
a set of components into a “larger” component.
So, let’s put the buttons into
a JPanel:
JPanel panel=new JPanel();
panel.add(new JButton("Button 1"));
panel.add(new JButton("Button 2"));
panel.add(new JButton("Button 3"));
content.add(panel);

Layout
Management Basics
One of the best features of Swing is
automatic component positioning and resizing.
That
is implemented trough a mechanism known
as Layout management. Special objects – layout
managers – are responsible for sizing,
aligning and positioning components. Each
container can have a layout manager, and
the type of layout manager determines the
layout of components in that container. There
are several types of layout managers, but
the two you will most frequently use are
FlowLayout (orders components one after another,
without resizing) and BorderLayout (has a
central part and four edge areas – component
in the central part is resized to take as
much space as possible, and components in
edge areas are not resized). In the previous
examples, you have used both of them. FlowLayout
is the default for a JPanel (that is why
all three buttons are displayed without resizing),
and BorderLayout is default for JFrame content
panes (that is why a single component is
shown covering the entire area).
Layout for a container is defined using
the setLayout method (or usually in the constructor).
So, you could change the layout of content
pane to FlowLayout and add several components,
to see them all on the screen.
The best choice for the window content pane
is usually a BorderLayout with a central
content part and a bottom status (or button)
part. The top part can contain a toolbar,
optionally.
Now,
let’s combine several components
and layouts, and introduce a new component – JTextArea.
JTextArea is basically a multiline editor.
Initialize the frame content pane explicitly
to BorderLayout, put a new JTextArea into
the central part and move the button panel
below.
package com.neuri.handsonswing.ch1;
import java.awt.*;
import javax.swing.*;
public class MainFrame extends JFrame
{
public MainFrame()
{
super("My title");
setSize(300,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container content = getContentPane();
content.setLayout(new BorderLayout());
JPanel panel = new JPanel(new FlowLayout());
panel.add(new JButton("Button 1"));
panel.add(new JButton("Button 2"));
panel.add(new JButton("Button 3"));
content.add(panel, BorderLayout.SOUTH);
content.add(new JTextArea(), BorderLayout.CENTER);
}
}
Notice
that the layouts for content pane and the
button panel are explicitly defined. Also
notice the last two lines of code – this is the other version
of add method, which allows you to specify the way the component is added.
In this case, we specify the area of BorderLayout layout manager. Central part
is called BorderLayout.CENTER, and other areas are called BorderLayout.NORTH
(top), BorderLayout.SOUTH (bottom), BorderLayout.WEST (left) and BorderLayout.EAST
(right). If you get confused about this, just remember land-maps from your
geography classes.
Scrolling
Now, type a few lines of text into the text
area – once you type enough
lines (or press Enter enough times), the cursor will disappear from the screen.
Swing is different from other GUI toolkits, and visual components do not support
scrolling unless you tell them to – but, on the other hand, this way
you can put scrollbar around almost anything. To enable scrolling, just put
the component (or a container with other components) into a JScrollPane – this
is a special container that adds scrollbars to it’s content. Change the
last line of the previous class to
content.add(new JScrollPane(new JTextArea()),
BorderLayout.CENTER);
to
see a scrolling text area inside your window.
Other
Interesting Components
Experiment a bit with other components that
you will be using often. Try to add the following
components in the example frame:
- JTextField
- a single line text field. For example,
the following line will add a text
field with 10 spaces for characters to
the panel:
- JLabel
- a simple textual label. Use this
component in front of text fields to
describe their function.
- JComboBox
- a drop down menu (optionally editable)

panel=new JPanel(new FlowLayout());
panel.add(new JLabel("Enter your name"));
panel.add(new JTextField(10));
String options[] = new String[]{ "Option
1","Option 2","Option
2" };
panel.add(new JComboBox(options));
content.add(panel, BorderLayout.NORTH);
Handling
Actions and Events
Now that you have learned how to put
components on the screen, you will learn
how to react to user actions with those components. The central mechanism
for this is the Observer pattern, implemented
in Swing with event listeners. Components
publish notifications about events, and event listeners receive these notifications.
For example, to execute a code when user presses a button, you should define
a listener for the button pressing event and register it with that button.
package com.neuri.handsonswing.ch1;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class MainFrame extends JFrame
{
public MainFrame()
{
super("My title");
setSize(300,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container content = getContentPane();
content.setLayout(new BorderLayout());
JPanel panel = new JPanel(new FlowLayout());
JButton button1 = new JButton("Button
1");
panel.add(button1);
button1.addActionListener( new MyButtonListener(this));
panel.add(new JButton("Button 2"));
panel.add(new JButton("Button 3"));
content.add(panel, BorderLayout.SOUTH);
content.add(new JScrollPane(new JTextArea()),
BorderLayout.CENTER);
panel = new JPanel(new FlowLayout());
panel.add(new JLabel("Enter your name"));
panel.add(new JTextField(10));
String options[] = new String[]{"Option
1","Option 2","Option 2"};
panel.add(new JComboBox(options));
content.add(panel, BorderLayout.NORTH);
}
private class MyButtonListener implements
ActionListener
{
private JFrame parentComponent;
MyButtonListener(JFrame parentComponent)
{
this.parentComponent=parentComponent;
}
public void actionPerformed(ActionEvent
e)
{
JOptionPane.showMessageDialog(parentComponent, "BUTTON
PRESSED!");
}
}
}
In
this example, we created an ActionListener
and attached it to a button. ActionListener
interface has just one method: actionPerformed.
That method
is called when an action occures. The event type ActionEvent is the most
common in Swing - most components produce
an ActionEvent. Components may also produce
other events - such as change of the current selection, text or size. For
now,
you should be most concerned with ActionEvent. In any case, in an IDE that
can display methods of a class and look for methods that begin with add and
end with Listener (for example, addFocusListener) to see what listener/event
types a component supports.
One more interesting thing in this example
is usage of class JOptionPane. That class
has many utility methods that help you display
standardised input dialogs and message dialogs.
This being said, it is time to note that
the above example is not written in the style
usual for Swing. Since the only usage of
a listener is to call a method, it is ofter
written as an anonymous inner class.
button1.addActionListener(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(MainFrame.this,"BUTTON PRESSED!");
}
});
This way, you do not have to write another
class like MyActionListener for every listener
- the code is much shorter and (arguably)
easier to read. Note
the strange notation for the parent component - (MainFrame.this). Since the
anonymous action listener instance is a fully-fledged object, using only
this would point to that object. MainFrame.this
points to the instance of MainFrame
that contains the embedded action listener.
For now, just note that there are also other
ways of receiving event notification then
installing a listener for every object on
the screen. For example, the following few
lines installs a global listener for the
F1 key pressing:
KeyStroke ks=KeyStroke.getKeyStroke(KeyEvent.VK_F1,0);
topComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ks,"HELP");
topComponent.getActionMap().put("HELP",
new
AbstractAction()
{
public void actionPerformed(ActionEvent evt)
{
// do something here, display a dialog or whatever
}
});
About
this Tutorial
This tutorial is from The
Swing Wiki which is published under the
GNU Free Documentation License 1.2.
|