One picture is worth ten thousand words.
Designing graphical application is an excellent example of the use of inheritance, abstract classes, and interfaces.
Developing graphical user interfaces isn't similar to anything else in software engineering. There are no simple, bulletproof guidelines that lead to any sort of enlightenment. Kurt Godel (the great mathematician of all time) proved that it is impossible to create a closed system that does not depend on some knowledge outside of that system. If we paraphrase the great Godel, this means that we are not able to create the perfect GUI.
Every program that presents a Swing GUI contains at least one top-level
Swing CONTAINER. For most programs, the top-level Swing containers are instances
of JFrame, JDialog, or (for applets) JApplet.
Each JFrame
object implements a single main window,
and each JDialog
implements a
secondary window, usually a temporary window to receive an additional
information from the user (JOptionPane is an example of JDialog). Each JApplet
object implements an applet's display area within a browser window. A top-level
Swing container provides the support for painting and event handling.
A container class is used to hold other COMPONENTS. The role of containers is to determine how the components are organized and displayed. The component classes are JButton, JLabel, JTextField, JTextArea, JRadioButton, JComboBox, JList, JMenuBar and few more.
In addition to this there are the HELPER classes, like Font, Color, Graphics and more.
Frames:
A frame, implemented as an instance of the JFrame class, is a free standing window that has decorations such as a border, a title, and buttons for closing and iconifying the window. Applications with a GUI typically use at least one frame. Here is a simple example of working with frames
import javax.swing.*;
public class SimpleFrameDemo
{
public static void main(String[] args)
{
// we call a constructor to set a title for the Frame
JFrame frame = new JFrame("Simple Frame");
//set the size of the frame
frame.setSize(400, 300);
// the frame is not displayed until you set this to true
frame.setVisible(true);
// this closes the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
The screen of a computer is a grid of little squares called pixels. The color of each pixel can be set individually, and drawing on the screen just means setting the colors of individual pixels. A position of a pixel on the screen is specified by a pair of integer coordinates, (x,y). The upper left corner has coordinates (0,0). The x coordinate increases from left to right, and the y coordinate increases from top to bottom.
By default, a frame is displayed in the upper-left corner of the screen. You can move a frame to any location. Though you must know the screen size. This can be obtained by using Toolkit class:
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
Then the Dimension class field
variables width
and height
will reveal the actual screen size. Finally
frame.setLocation(screen.width-400, 0);
will move the frame to the upper-right corner.
See the code example SimpleFrameDemo.java
Panels:
In order to draw, you need something to draw on. Panels are used to group various components. Panels can contain another panels as well.
|-------------------------------|
| Frame |
| __________ ___________ |
| |Panel | | | |
| | | | Panel | |
| |________| |__________| |
| |
| ____________________________ |
| |Panel | |
| | | |
| |__________________________| |
|_______________________________|
To draw on a panel you need to
override the paintComponent()
method. A panel cannot be displayed on its own and must be added to an existing
container. Every top-level container indirectly contains an intermediate
container known as a content pane.
See the code example: SimplePanelDemo.java
Designing a layout:
Here is the relationship among
components:
|-------------------------------------
| (JFrame) |
| (JTextField) |
| (JLabel) _______ |
| Enter an integer: | | |
| ------- |
| |
| (JTextArea) (JPanel) |
| --------------- |
| | | |
| | (JButton) | |
| | | |
| --------------- |
|____________________________________|
A JLabel
object can display either text, an image, or both. You can specify where in the
label's display area the label's contents are aligned by setting the vertical and
horizontal alignment. By default, labels are vertically centered in their
display area. Text-only labels are leading edge aligned, by default; image-only
labels are horizontally centered, by default.
Here is the code that adds the button and label to the panel, and the panel to the frame:
frame = new JFrame(...);
button = new JButton(...);
label = new JLabel(...);
panel = new JPanel();
panel.add(button);
panel.add(label);
frame.getContentPane().add(panel, BorderLayout.CENTER);
Layout
Manager:
The components
in a container must be "laid out," which means setting their sizes
and positions. It's possible to program the layout yourself, but ordinarily
layout is done by a layout manager.
A layout manager is an object associated with a container that implements some
policy for laying out the components in that container. Different types of
layout manager implement different policies.
FlowLayout
- puts
components in a left-to-right line, sized at their preferred size.
GridLayout
- makes
components equal in size and displays them in the requested number of rows and
columns.
BorderLayout
- puts
components in five areas: north, south, east, west, and center.
BoxLayout
- arranges components either from top to bottom or from left
to right.
Arrangement
of components in a BorderLayout:
----------------------
| North |
|---------------------
| | | |
| | | |
|West| Center | East |
| | | |
| | | |
|---------------------
| South |
|____________________|
By
default, a panel's layout manager is an instance of FlowLayout
, which places
the panel's contents in a row. You can easily make a panel use any other layout
manager by invoking the setLayout
method or specifying a layout manager when creating the panel. The syntax to
set a layout manager is
container.setLayout(new GridLayout(2,3));
See the code example: PanelDemo.java.
Example: Calculator
-----------------
| display area |
|---------------|
| 7 | 8 | 9 | / |
|---------------|
| 4 | 5 | 6 | * |
|---------------|
| 1 | 2 | 3 | - |
|---------------|
| 0 | . | = | + |
|---------------|
| Made in CMU |
|_______________|
Here is a prototype:
// choose a proper layout
setLayout(new BorderLayout());
// north part of the layout
display = new JTextField("0");
display.setEditable(false);
add(display, BorderLayout.NORTH);
// center part of the layout
panel = new JPanel();
panel.setLayout(new GridLayout(4, 4));
// add 16 buttons
JButton button = new JButton(label);
//I explain this statement in the next paragraph
button.addActionListener(this);
panel.add(button);
add(panel, BorderLayout.CENTER);
// south part of the layout
add(new JLabel("Made at CMU"), BorderLayout.SOUTH);
Event
Handling:
Java
graphics programming is event driven. This means that parts of application are
executed when an event (event source)-
button click, mouse move and others - is activated. The program then choose
either to ignore the event or to respond to it.
If a
class wants to respond to a user event, it must implement the interface that
deals with the event. These interfaces are called event listeners. Each listener handles a specific kind of event.
The following event listeners are available (there are more of them!):
KeyListener
- keyboard event that occurs when a user types on a keyboard.
ActionListener
- action event that occurs when a user takes an action such as a click on a button.
MouseListener
- mouse event that occurs by a mouse click.
ItemListener
- item event that occurs when an item such as a checkbox has been changed.
ComponentListener
- window event that occurs when a window moves or resizes.
MouseMotionListener
- mouse event that occurs when a mouse moves.
WindowListener
- window event that occurs when user closes a window.
Let us
outline a general scenario by looking at the most simple event - how JButton
handles the mouse click. Every event
handler requires three pieces of code.
1. To
detect that a user clicks on a button, your class must be declared as an action
listener
public class Demo extends JApplet implements ActionListener
{
...
}
2.
You need to
register the event source
JButton x = new JButton();
x.addActionListener(instanceOfYourClass);
When the user clicks on the button, the button fires an action event:
ActionEvent
JButton ---------> action listener (actionPerformed)
3.
In the last step
you are to implement the method
actionPerformed
public void actionPerformed(ActionEvent e)
{
//what do you do when a user clicks this button?
}
See the code example: EventDemo.java.
Most
listener interfaces contain more than once method. For example, the MouseListener
interface has five methods: mousePressed
, mouseReleased
, mouseEntered
,
mouseExited
, and mouseClicked
. You do not need to implement
all these methods, but only those which you need. The others can have empty
bodies.
See the
code example: FlyingBalloon4XP.java.
Thread Concepts:
A thread is a single sequential flow of
control within a program. Sequential programs have a beginning, an execution
sequence, and an end. At any given time during the runtime of the program there
is a single point of execution. A thread is similar to the sequential programs:
it has a beginning, a sequence, and an end, and at any given time during the
runtime of the thread, there is a single point of execution. However, a thread
itself is not a program. It runs within a
program, and it cannot run on its own.
Examples of multithreaded application:
From the
programmer's point of view, a thread is a java object. It is created (like any
other java object) from a Java class with new
and an appropriate constructor. It has members and methods. It can be passed as
a parameter, put in an array, etc. Each thread object has a run()
method. The run method gives a thread something to do.
Creating a thread.
There are two techniques for creating a new thread of execution and providing a run method for it:
· Exdending the class Thread, and overriding its run() method.
1. Declare a class to be a subclass ofThread
2. Override the run method
3. Create runnable object
4. Start the thread
See SimpleThread1.java and the equivalent implementation SimpleThread2.java
· Implementing the Runnable interface
1. Declare a class that implements theRunnable
interface.
2. Implement the run method
3. Create runnable object
4. Start the thread
See SimpleThread3.java
Why do we need the second
approach? Why cannot we always extend the Thread
class? Hint: Java has a single inheritance.