GUIs

advertisement
CS102 – GUI
Based on :
David Davenport’s slides
Spring 2002
References:
http://java.sun.com/docs/books/tutorial/uiswing
http://www.aduni.org/courses/java
Core Java Volume 1
http://www.javaworld.com/javaworld/jw-04-1998/jw-04-howto.html
AWT
abstract window toolkit
 AWT components were provided by
the JDK 1.0 and 1.1 platforms.
 delegates into native windowing
system
 therefore, looks different in different
platforms

AWT
Since platforms vary, AWT had to
choose a lowest common
denominator
 write once, debug everywhere …

JFC & Swing






JFC is short for JavaTM Foundation Classes, which encompass a group of features to
help people build graphical user interfaces (GUIs). The JFC was first announced at
the 1997 JavaOne developer conference and has the following features:
The Swing Components

Include everything from buttons to split panes to tables. You can see mugshots
of all the components in
http://java.sun.com/docs/books/tutorial/uiswing/components/components.html
.
Pluggable Look and Feel Support

Gives any program that uses Swing components a choice of looks and feels. For
example, the same program can use either the JavaTM look and feel or the
Windows look and feel. We expect many more look-and-feel packages to
become available from various sources.
Accessibility API

Enables assistive technologies such as screen readers and Braille displays to
get information from the user interface.
Java 2DTM API (Java 2 Platform only)

Enables developers to easily incorporate high-quality 2D graphics, text, and
images in applications and in applets.
Drag and Drop Support (Java 2 Platform only)

Provides the ability to drag and drop between a Java application and a native
application.
Swing





is implemented without making use of
native window elements
components are painted by java on blank
windows.
more flexibility, not the lowest common
denominator
same look, same bugs in all platforms
The Swing components will continue to be
enhanced in the future, AWT is dying
Swing, other differences





Swing buttons and labels can display images
instead of, or in addition to, text.
You can easily add or change the borders drawn
around most Swing components. For example,
it's easy to put a box around the outside of a
container or label.
You can change the behavior or appearance of a
Swing component by either invoking methods on
it or creating a subclass of it.
Swing components don't have to be rectangular.
Buttons, for example, can be round.
Assistive technologies such as screen readers
can easily get information from Swing
components. For example, a tool can easily get
the text that's displayed on a button or label.
Look & Feel
Java look and feel
CDE/Motif look and feel
Windows look and feel
Swing’s Handicaps?
performance
 familiarity

Programming forms

Procedural programming


code is executed in sequential order.
statement
statement
statement
--------------statement
Event-driven programming

code is executed upon activation of events.
Do
method 1
then
method 3
method 1
method 2
method 3
--------------method n
Do
method 2
then
method 1
then
method 3
1
2
3
n
procedural programming




Up to now, our control flow model has been
pretty straight-forward.
Execution starts at main() and executes
statement sequentially branching with if,for,and
while statement, and occasional method calls.
When we need user input we call read() on the
console stream which waits (blocks) until the
user types something, then returns.
One problem with this model is: How do we wait
for and respond to input from more than one
source (eg keyboard and mouse). If we block on
either waiting for input, we may miss input from
the other.
Event-driven programming
the system waits for user input
events, and these events trigger
program methods
 Event-based programming
addresses the two problems:

How to wait on multiple input sources
(input events)
 How to decide what to do on each type
of input event

General form of eventdriven programming



The operating system and window system process
input event from devices (mouse movement, mouse
clicks, key presses etc)
The window system decides which window, and
hence which frame and program, each input event
belongs to.
A data structure describing the event is produced and
placed on an 'event queue'. Events are added to one
end of the queue by the window system . Events are
removed from the queue and processed by the
program. These event data structures hold
information including:
 Which of the program's windows this event
belongs to.
 The type of event (mouse click, key press, etc.)
 Any event-specific data (mouse position, key
code, etc.)
Event loop
main(){
...set up application data structures...
...set up GUI....
// enter event loop
while(true){
Event e = get_event();
process_event(e); // event dispatch routine } }

the event loop is usually hidden from
programmer, he/she provides just a
process_event() method
Java Event Management
java system has more control over
event system, it directs events
based on their type
 you only override behavior you are
interested in.
 more details later…

AWT Applications - Frame

Frame is a container for components
Optional Menu
Three
containers in
Frame with
Border Layout
UI-components
inside
containers each
with own layout
Frame with
normal window
controls
AWT classes
AWTEvent
Font
FontMetrics
Object
Color
Container
Panel
Applet
Button
Window
Frame
Label
Dialog
TextField
TextComponent
TextArea
Graphics
Component
List
Choice
CheckBox
LayoutManager
CheckBoxGroup
Canvas
MenuComponent
Scrollbar
MenuItem
MenuBar
Menu
FileDialog
AWT & Swing classes
AWTEvent
Font
Classes in the
java.awt package
LayoutManager
Heavyweight
1
FontMetrics
Object
Color
Panel
Applet
JApplet
Window
Frame
JFrame
Dialog
JDialog
Graphics
Component
Container
*
JComponent
Swing Components in the
javax.swing package
Lightweight
Swing - JComponents
.
JCheckBoxMenuItem
AbstractButton
JMenuItem
JMenu
JButton
.JRadioButtonMenuItem
.JToggleButton
JCheckBox
JRadioButton
.JEditorPane
JComponent
.JTextComponent
.JTextField
.JPasswordField
.JTextArea
.JLabel
.JList
.JComboBox
.JMenuBar
.JPanel
.JOptionPane
.JScrollBar
.JScrollPane
.JPopupMenu
.JSeparator
.JSlider
.JTabbedPane
.JRootPane
.JPane
.JProgressBar
.JToolBar
.JSplitPane
.JTable
.JTree
.JColorChooser
.JInternalFrame
.JToolTip
.JLayeredPane
.JTableHeader
.JFileChooser
Understanding the GUI

UI-containers


{label}
Each UI-component

{Frame}

components
{textfield}
{button}
have list of
UI-components

is a class
with paint method
& lists of
Event listeners
Setting up the GUI

Extend Frame class

In constructor
• Create instances of containers
& add them to Frame
• Create instances of components
& add them to containers or Frame

Hint: Employ
layout managers
to arrange
components in
containers
Possibly override paint method
 UI-components
 Painting
added to components list
Frame
1.paints Frame borders
2.calls Frame paint method
3.calls paint method of each object in component list
A simple example
import javax.swing.*;
class FirstFrame extends JFrame {
public FirstFrame() {
setTitle("FirstFrame");
setSize(300, 200);
}
}
public class FirstTest {
public static void main(String[] args) {
JFrame frame = new FirstFrame();
frame.show();
}
}
Simple Example Remarks





we have two classes, one for the frame, the
other for the main method that creates the
frame
we could have put the main method inside
class FirstFrame
if we don’t set the size, the default is a
window of size 0x0
java runtime creates a new thread for our
frame
it doesn’t terminate, just hides
Hello World Program
import javax.swing.*;
public class HelloWorldSwing {
public static void main(String[] args) {
JFrame frame = new
JFrame("HelloWorld");
JLabel label = new JLabel("Hello World");
frame.getContentPane().add(label);
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.setBounds(200, 200, 200, 50);
frame.setVisible(true);
}
}
Containers




Swing provides three generally useful top-level
container classes: JFrame, JDialog, and JApplet.
To appear onscreen, every GUI component must
be part of a containment hierarchy. Each
containment hierarchy has a top-level container
as its root.
Each top-level container has a content pane
that, generally speaking, contains the visible
components in that top-level container's GUI.
You can optionally add a menu bar to a top-level
container. The menu bar is positioned within the
top-level container, but outside the content
pane.
Container Tree
• a frame, or main window
(JFrame)
• a panel, sometimes called a
pane (JPanel)
•a button (JButton)
•a label (JLabel)
Containers ..



The frame is a top-level container. It exists mainly
to provide a place for other Swing components to
paint themselves.
The panel is an intermediate container. Its only
purpose is to simplify the positioning of the button
and label. Other intermediate Swing containers,
such as scroll panes (JScrollPane) and tabbed
panes (JTabbedPane), typically play a more visible,
interactive role in a program's GUI.
The button and label are atomic components -components that exist not to hold random Swing
components, but as self-sufficient entities that
present bits of information to the user. Often,
atomic components also get input from the user.
Layouts
Using Layout Managers




By default, every container has a layout
manager.
All JPanel objects use a FlowLayout by default,
whereas content panes (the main containers in
JApplet, JDialog, and JFrame objects) use
BorderLayout by default.
You can change the layout as:
JPanel pane = new JPanel();
pane.setLayout(new BorderLayout());
When you add components to a panel or a
content pane, the arguments you specify to the
add method depend on the layout manager that
the panel or content pane is using.
Border Layout


A border layout lays out a container, arranging and
resizing its components to fit in five regions: north,
south, east, west, and center.
Each region may contain no more than one
component, and is identified by a corresponding
constant: NORTH, SOUTH, EAST, WEST, and CENTER.
When adding a component to a container with a
border layout, use one of these five constants, for
example:
Panel p = new Panel();
p.setLayout(new BorderLayout());
p.add(new Button("Okay"), BorderLayout.SOUTH);
Border Layout Example
Container contentPane = getContentPane();
//Use the content pane's default BorderLayout.
//contentPane.setLayout(new BorderLayout());
contentPane.add(new JButton("Button 1 (NORTH)"),
BorderLayout.NORTH);
contentPane.add(new JButton("2 (CENTER)"),
BorderLayout.CENTER);
contentPane.add(new JButton("Button 3 (WEST)"),
BorderLayout.WEST);
contentPane.add(new JButton("Long-Named Button 4
(SOUTH)"), BorderLayout.SOUTH);
contentPane.add(new JButton("Button 5 (EAST)"),
BorderLayout.EAST);
Border Layout Output
Border Layout cont.


We specified the component as the first argument to
the add method. For example:
add(component, BorderLayout.CENTER) //preferred
However, you might see code in other programs that
specifies the component second. For example, the
following are alternate ways of writing the preceding
code:
add(BorderLayout.CENTER, component)
add("Center", component) //valid but error prone
By default, a BorderLayout puts no gap between the
components it manages. You can specify gaps (in
pixels) using the following constructor:
BorderLayout(int horizontalGap, int verticalGap) You
can also use the following methods to set the
horizontal and vertical gaps, respectively: void
setHgap(int) void setVgap(int)
BorderLayout(20, 20)
Border Layout Resizing
First North and South are drawn
with their normal sizes, then east &
west, finally all the rest of the space
is reserved for center.
 center is the default location
 by default anything in center fill
resize to fill the space

Border Layout Resized
Flow Layout



FlowLayout puts components in a row,
sized at their preferred size.
If the horizontal space in the container is
too small to put all the components in
one row, FlowLayout uses multiple rows.
Within each row, components are
centered (the default), left-aligned, or
right-aligned as specified when the
FlowLayout is created.
Flow Layout Example
Container contentPane = getContentPane();
contentPane.setLayout(new FlowLayout());
contentPane.add(new
contentPane.add(new
contentPane.add(new
contentPane.add(new
Button 4"));
contentPane.add(new
JButton("Button 1"));
JButton("2"));
JButton("Button 3"));
JButton("Long-Named
JButton("Button 5"));
Flow Layout Example Output
The FlowLayout API

The FlowLayout class has three constructors:





public FlowLayout()
public FlowLayout(int alignment)
public FlowLayout(int alignment, int horizontalGap, int
verticalGap)
The alignment argument must have the value
FlowLayout.LEFT, FlowLayout.CENTER, or
FlowLayout.RIGHT.
The horizontalGap and verticalGap arguments specify
the number of pixels to put between components. If
you don't specify a gap value, FlowLayout uses 5 for
the default gap value.
Grid Layout



A GridLayout places components in a grid
of cells.
Each component takes all the available
space within its cell, and each cell is
exactly the same size.
If you resize the GridLayout window, it
changes the cell size so that the cells are
as large as possible, given the space
available to the container.
Grid Layout Example
Container contentPane = getContentPane();
contentPane.setLayout(new GridLayout(0,2));
contentPane.add(new JButton("Button 1"));
contentPane.add(new JButton("2"));
contentPane.add(new JButton("Button 3"));
contentPane.add(new JButton("Long-Named Button 4"));
contentPane.add(new JButton("Button 5"));

The constructor tells the GridLayout class to create an
instance that has two columns and as many rows as
necessary.
Grid Output
The GridLayout API



The GridLayout class has two constructors:
public GridLayout(int rows, int columns)
public GridLayout(int rows, int columns,
int horizontalGap, int verticalGap)
At least one of the rows and columns arguments
must be nonzero.
The horizontalGap and verticalGap arguments to
the second constructor allow you to specify the
number of pixels between cells. If you don't
specify gaps, their values default to zero.
GridBagLayout





GridBagLayout is the most flexible and complex one
A GridBagLayout places components in a grid of rows
and columns, allowing specified components to span
multiple rows or columns.
Not all rows necessarily have the same height.
Similarly, not all columns necessarily have the same
width.
Essentially, GridBagLayout places components in
rectangles (cells) in a grid, and then uses the
components' preferred sizes to determine how big
the cells should be.
see http://www.interex.org/pubcontent/enterprise/jul00/08chew.html
for a nice description
Setting Up …
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
JPanel pane = new JPanel();
pane.setLayout(gridbag);
//For each component to be added to this container:
//...Create the component...
//...Set instance variables in the GridBagConstraints
instance...
gridbag.setConstraints(theComponent, c);
pane.add(theComponent);
GridBagConstraints







gridx, gridy

Specify the row and column at the upper left of the component.
gridwidth, gridheight

Specify the number of columns (for gridwidth) or rows (for gridheight) in the
component's display area.
fill

Used when the component's display area is larger than the component's requested size
to determine whether and how to resize the component. Valid values are NONE (the
default), HORIZONTAL (make the component wide enough to fill its display area
horizontally, but don't change its height), VERTICAL and BOTH.
ipadx, ipady

Specifies the internal padding: how much to add to the minimum size of the
component. The default value is zero. The width of the component will be at least its
minimum width plus ipadx*2 pixels, the height of the component will be at least its
minimum height plus ipady*2 pixels.
insets

Specifies the external padding of the component -- the minimum amount of space
between the component and the edges of its display area. By default, each component
has no external padding.
anchor

Used when the component is smaller than its display area to determine where (within
the area) to place the component. Valid values (defined as GridBagConstraints
constants) are CENTER (the default), NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH,
SOUTHWEST, WEST, and NORTHWEST.
weightx, weighty

Weights are used to determine how to distribute space among columns (weightx) and
among rows (weighty); this is important for specifying resizing behavior.
GridBagLayout Example
All
ipadx = 0 fill = HORIZONTAL
Button 1
ipady = 0 weightx = 0.5 weighty = 0.0 gridwidth = 1
anchor = CENTER insets = new Insets(0,0,0,0)
gridx = 0 gridy = 0
Button 2
weightx = 0.5 gridx = 1 gridy = 0
Button 3
weightx = 0.5 gridx = 2 gridy = 0
Button 4
ipady = 40 weightx = 0.0 gridwidth = 3
gridx = 0 gridy = 1
Button 5
ipady = 0 weightx = 0.0 weighty = 1.0
anchor = SOUTH insets = new Insets(10,0,0,0)
gridwidth = 2 gridx = 1 gridy = 2
Horizontal Fill

fill = HORIZONTAL expands components to
maximum horizontally, but normal vertically
fill=NONE
fill=HORIZONTAL
Weights


all the components expand
equally horizontally, since
their weightx are equal
only button 5’s area expand
vertically, since other’s
weighty’s are 0
Ipad & inset
ipad values grow the component
itself, whereas insets grow the area
while keeping the component same
size
 fill = HORIZONTAL makes buttons
wider than they should be, but only
button 4 with a nonzero ipady value
is higher than normal.

Why Layouts?
Can use Absolute Layout
 Layouts provide flexibility in
different environments with different
screen resolutions.
 Font sizes, component contents may
change (internationalization)

Using Netbeans IDE
Events & Event Handling
Event
cause
(mouse,
keyboard,
timer, …)

Event
Source
object
Event
Object
Event
listener
object
(executes
method)
Example…




User clicks on a button
Button is source of event object
Event object passed to associated listener object
Listener object executes associated method
to perform desired task (save file, quit program, …)
Setting up Event Handling

Create listener class




Using new or existing class, simply
Implement desired event listener interface
Putting code for desired action in its methods
In application


(e.g. Frame)
Create instance of listener class
Add as listener of source object
• can have any number of listeners for each event
• Source & listener can be same object!
Understanding Events

When button is pressed

{label}

{Frame}
components
WindowListeners
{textfield}
windowClosing
actionPerformed

ActionListeners
actionPerformed method
of every item in button’s
actionListeners list called
Similarly for textfield
When Frame close button
is pressed

windowClosing method of
every item in Frame’s
windowListeners list called
{MyListener}
{button}
ActionListeners
actionPerformed
Event Classes
EventObject
AWTEvent
ListSelectionEvent
ActionEvent
ContainerEvent
AdjustmentEvent
FocusEvent
ComponentEvent
InputEvent
ItemEvent
PaintEvent
TextEvent
WindowEvent
MouseEvent
KeyEvent
Event Examples
User clicks a button, presses Return while typing in a
text field, or chooses a menu item ActionListener
User closes a frame (main window)  WindowListener
User presses a mouse button while the cursor is over a
component  MouseListener
User moves the mouse over a component 
MouseMotionListener
Component becomes visible  ComponentListener
Component gets the keyboard focus  FocusListener
Table or list selection changes  ListSelectionListener
How to Implement an
Event Handler



In the declaration for the event handler class, specify
that the class either implements a listener interface or
extends a class that implements a listener interface.
For example:
public class MyClass implements ActionListener {
Register an instance of the event handler class as a
listener upon one or more components. For example:
someComponent.addActionListener(instanceOfMyClass)
Implement the methods in the listener interface. For
example:
public void actionPerformed(ActionEvent e) { ...//code
that reacts to the action... }
public class MC extends JFrame implements ActionListener {
private int noClicks = 0;
private JButton button = new JButton("click me !");
private JTextArea textArea = new JTextArea(5, 40);
private JLabel label = new JLabel("Here you can see the
number of button clicks") ;
private JProgressBar progressBar =
new JProgressBar(JProgressBar.HORIZONTAL);
public MC(String title) {
super(title);
Container cp = getContentPane();
cp.setLayout(new BoxLayout(cp, BoxLayout.Y_AXIS));
cp.add(button);
JScrollPane scrollPane = new JScrollPane(textArea);
cp.add(scrollPane);cp.add(label); cp.add(progressBar);
button.addActionListener(this);
}
Action Example Cont.
public void actionPerformed(ActionEvent e) {
noClicks++;
textArea.append("Button clicked " + noClicks +
" times so far \n");
label.setText("You clicked " + noClicks + " times");
progressBar.setValue(noClicks);
}
}
public static void main(String[] args) {
MyClass frame = new MyClass("Simple Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
Listener Class Flavors
public class MyClass implements MouseListener {
... someObject.addMouseListener(this); ...
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) {
...//Event handler implementation goes here...
}}
Adapter Classes
Java comes with a variety of adapter
classes that implement Listener
interfaces but have empty
implementations.
 One can extend an adapter class
and only override events that are of
interest

Adapter Example
An example of extending an adapter class
instead of directly implementing a listener
interface.
public class MyClass extends MouseAdapter {
... x.addMouseListener(this); ...

public void mouseClicked(MouseEvent e) {
//Event handler implementation
//
goes here...
}}
Using an Inner class …
What if your class has to extend another one?
public class MyClass extends Applet {
... x.addMouseListener(new MyAdapter());
...
class MyAdapter extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
...//Event handler implementation
// goes here...
}
}
}

Using an Anonymous
Inner class
public class MyClass extends Applet { ...
x.addMouseListener( new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
...//Event handler impl.
}
}); ... } }

an inner class can refer to instance
variables and methods just as if its code
is in the containing class
Example
class PanelJ2 extends JPanel {
public PanelJ2() {
JButton b = new JButton( "OK");
setLayout( new BorderLayout() );
setBackground( Color.red);
add( b, BorderLayout.SOUTH);
b.addActionListener( new ExampleActionListener());
}
}
// restores screen after window has been covered, minimized,
public void paintComponent( Graphics g) {
super.paintComponent( g);
g.drawRect( 50, 50, 200, 100);
g.drawString( "Hello", 125, 100 );
}
Action Listener
class ExampleActionListener implements
ActionListener {
public void actionPerformed( ActionEvent e)
{
System.out.println( "Button Pressed");
}
}
Putting 3 of PanelJ2 …
class MyJFrame extends JFrame {
}
public MyJFrame() {
Container c = getContentPane();
c.setLayout( new FlowLayout() );
c.add( new PanelJ2());
c.add( new PanelJ2());
c.add( new PanelJ2());
setTitle( "ExampleJ3 - Hi there");
setBounds( 100, 150, 300, 300);
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
Outcome …
Put them inside an applet
public class AppletExample extends Applet {
public void init() {
setLayout( new FlowLayout() );
add( new PanelJ2());
add( new PanelJ2());
add( new PanelJ2());
}
}
Applet outcome
Which button ?
Let’s say you have three buttons on
your application. How can you tell
which button was pressed?
 We need something distinct about
the event object to decide what to
do
 We can use getSource() method

ButtonPanel
class ButtonPanel extends JPanel implemnts ActionListener{
public ButtonPanel() {
yellowButton = new JButton("Yellow");
blueButton = new JButton("Blue");
redButton = new JButton("Red");
add(yellowButton);
add(blueButton);
add(redButton);
}
}
yellowButton.addActionListener(this);
blueButton.addActionListener(this);
redButton.addActionListener(this);
private JButton yellowButton, blueButton, redButton;
actionPerformed method
public void actionPerformed(ActionEvent evt){
Object source = evt.getSource();
Color color = getBackground();
if (source == yellowButton) color = Color.yellow;
else if (source == blueButton) color = Color.blue;
else if (source == redButton) color = Color.red;
setBackground(color);
repaint();
}
An alternative?
We used the same listener object for
all the buttons (ButtonPanel itself)
 Another approach would be to use
different listeners who knows about
what they should do when the event
happens

ColorAction inner class
private class ColorAction implements ActionListener
{
public ColorAction(Color c)
{
backgroundColor = c;
}
public void actionPerformed(ActionEvent event) {
setBackground(backgroundColor);
}
private Color backgroundColor;
}
New constructor …
public ButtonPanel() {
// create buttons
JButton yellowButton = new JButton("Yellow"); . . .
// add buttons to panel
add(yellowButton); add(blueButton);
add(redButton);
// create button actions
ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
}
// associate actions with buttons
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);
ColorAction, another aproach
class ColorAction implements ActionListener {
public ColorAction(Color c, JPanel p) {
backgroundColor = c;
buttonPanel = p;
}
public void actionPerformed(ActionEvent event) {
buttonPanel.setBackground(backgroundColor);
}
private Color backgroundColor;
private JPanel buttonPanel;
}
The program ..
Mouse Listener Example
public class MouseMove extends JFrame {
private Rectangle box;
private int x,y;
}
public static void main(String[] args) {
MouseMove m = new MouseMove();
}
public MouseMove() {
super("Mouse Demonstration");
Container cp=getContentPane();
JPanel mousePanel = new MousePanel();
mousePanel.setPreferredSize(new Dimension(300, 300));
cp.add(mousePanel);
pack();
setVisible(true);
}
class MousePanel extends JPanel {
int x,y;
public MousePanel() {
addMouseListener( new MouseAdapter() {
public void mousePressed(MouseEvent event) {
x=event.getX();
y=event.getY();
System.out.println(event);
repaint();
}
} );
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("You clicked here", x, y);
}
Design Tips

GUI code can get very messy

Do not put everything in one class
(as many Visual IDE’s do)
Quick & dirty = impossible to change!
 Employ design patterns, e.g. MVC


Think Design first...
MVC - Design Pattern
View
View
Multiple
Views
View
14:30
Half past two
notify
read
model
hours: 14
mins: 30
secs: 25
visual update
content update
controller
receives user
interface events
1 sec.
timer
Reset
MVC . . .



Model : The core of the application. This
maintains the state and data that the application
represents. When significant changes occur in
the model, it updates all of its views
Controller : The user interface presented to the
user to manipulate the application.
View : The user interface which displays
information about the model to the user. Any
object that needs information about the model
needs to be a registered view with the model.
Observer/Observable



Java contains a framework to implement a system
where a model is observed by a set of observers.
The Observable class represents an observable
object, or "data" in the model-view paradigm. It can
be subclassed to represent an object that the
application wants to have observed.
An observable object can have one or more
observers. An observer may be any object that
implements interface Observer. After an observable
instance changes, an application calling the
Observable's notifyObservers method causes all of its
observers to be notified of the change by a call to
their update method.
Observable class



void addObserver(Observer o)
Adds an observer to the set of observers for
this object, provided that it is not the same as some
observer already in the set.
protected void setChanged()
Marks this Observable object as having been
changed; the hasChanged method will now return
true.
void notifyObservers()
If this object has changed, as indicated by the
hasChanged method, then notify all of its observers
and then call the clearChanged method to indicate
that this object has no longer changed.
interface Observer
public void update(Observable o, Object arg)


This method is called whenever the observed
object is changed. An application calls an
Observable object's notifyObservers method to
have all the object's observers notified of the
change.
Parameters:
• o - the observable object.
• arg - an argument passed to the notifyObservers method.
Clock Example, Time Model
public class Time3 extends Observable {
private int hour, minute, second;
//setters, getters, constructors omitted
public void tick() {
second = ( second + 1 ) % 60;
if ( second == 0 ) {
minute = (minute + 1 ) % 60;
if ( minute == 0 ) hour = ( hour + 1 ) % 24;
}
setChanged();
notifyObservers();
}
Clock View (partial code)
public class ClockView extends JPanel implements Observer {
Time3 t;
public ClockView( Time3 time) {
t = time;
t.addObserver( this);
…
}
public void update( Observable o, Object arg) {
repaint();
}
public void paintComponent( Graphics g) {
super.paintComponent(g);
g.drawOval(2, 2, 95, 95);
double hoursAngle = 2 * Math.PI * ( t.getHour() - 3 ) / 12;
g.drawLine( 50, 50, 50 + (int) (25 * Math.cos( hoursAngle)),
50 + (int) (25 * Math.sin( hoursAngle)) );
}
Digital View
public class DigitalView extends JLabel implements Observer {
Time3 t;
public DigitalView( Time3 t) {
…
this.t = t;
t.addObserver( this);
}
}
public void update(Observable o, Object arg) {
setText( t.toUniversalString());
repaint();
}
Clock Panel (partial)
public class Clock extends JPanel {
public Clock() {
theTime = new Time3();
clockView1 = new ClockView( theTime);
clockView2 = new DigitalView( theTime);
add( clockView1, CENTER);
add( clockView2, SOUTH);
timer = new Timer( 1000, new TimerActionListener() );
timer.start();
}
Time3
theTime; Timer
timer;
ClockView clockView1; DigitalView clockView2;
}
private class TimerActionListener implements ActionListener {
public void actionPerformed ( ActionEvent e) {
theTime.tick();
}
}
An Application
public class Example1 extends JFrame {
public Example1() {
Container c = getContentPane();
c.setLayout( new FlowLayout() );
Clock tc1 = new Clock(); c.add( tc1);
Clock tc2 = new Clock(); c.add( tc2);
}
}
setBounds( 100, 150, 300, 250);
setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
setVisible(true);
Output …
MVC in Swing
ButtonModel model = button.getModel();
ButtonUI ui = button.getUI();
model and ui

The model
The behavior of the model is captured by the ButtonModel
interface. A button model instance encapsulates the internal
state of a single button and defines how the button behaves. Its
methods can be grouped into four categories -- those that:





Query internal state
Manipulate internal state
Add and remove event listeners
Fire events
The view and controller
The behavior of the view and controller are captured by the
ButtonUI interface. Classes that implement this interface are
responsible for both creating a button's visual representation
and handling user input provided via the keyboard and mouse.
Its methods can be grouped into three categories -- those that:



Paint
Return geometric information
Handle AWT events
interface ButtonModel



This model is used for check boxes and radio buttons, as well
as for normal buttons. For check boxes and radio buttons,
pressing the mouse selects the button. For normal buttons,
pressing the mouse "arms" the button. Releasing the mouse
over the button then initiates a button press, firing its action
event. Releasing the mouse elsewhere disarms the button.
In use, a UI will invoke setSelected(boolean) when a mouse
click occurs over a check box or radio button. It will invoke
setArmed(boolean) when the mouse is pressed over a regular
button and invoke setPressed(boolean) when the mouse is
released. If the mouse travels outside the button in the
meantime, setArmed(false) will tell the button not to fire when
it sees setPressed. (If the mouse travels back in, the button will
be rearmed.)
A button is triggered when it is both "armed" and "pressed".
Plugging in Custom model
Let’s say we have a button model that
just toggles the state, called
ToggleButtonModel.
 We can designate this new type of
model to our ordinary JButton like:
button.setModel(new
toggleButtonModel(button));

Changing UI class
Given a new UI class FancyButtonUI,
we can change the look of our
button as:
button.setUI(new
FancyButtonUI(button));

Results
MVC Example
class Circles extends JFrame implements ActionListener {
public Circles() {
radius = new JTextField( 5);
radius.addActionListener( this);
}
// convert given radius to circumference or vice versa
public void actionPerformed( ActionEvent e) {
DecimalFormat fmt = new DecimalFormat( "0.##");
}
}
if ( e.getSource() == radius) {
double newRadius = Double.parseDouble( radius.getText() );
double newCircumference = 2 * 3.142 * newRadius;
circumference.setText( fmt.format( newCircumference) );
}
else {
double newCircumference = Double.parseDouble( circumference.getText() );
double newRadius = newCircumference / 2 / 3.142;
radius.setText( fmt.format( newRadius) );
}
Separate Circle logic
class Circle {
double radius, circumference;
public Circle( double r){
setRadius( r);
}
public void setRadius( double r) {
radius = r;
circumference = 2 * Math.PI * r;
}
public double getRadius() {
return radius;
}
public void setCircumference( double c) {
circumference = c;
radius = circumference / 2 / 3.142;
}
public double getCircumference() {
return circumference;
}
}
Circle logic separated
class Circles extends JFrame implements ActionListener {
Circle aCircle;
public Circles( Circle circleObject) {
aCircle = circleObject;
}
}
// convert given radius to circumference or vice versa
public void actionPerformed( ActionEvent e) {
if ( e.getSource() == radius) {
double newRadius =
Double.parseDouble( radius.getText() );
aCircle.setRadius( newRadius);
circumference.setText( fmt.format(
aCircle.getCircumference() ) );
}
else …
}
Model aware of view
class Circle {
double radius, circumference;
Circles panel;
}
public Circle( double r){
setRadius( r);
}
public void setUI( Circles panel) {
this.panel = panel;
}
public void setRadius( double r) {
radius = r;
circumference = 2 * Math.PI * r;
if (panel != null)
panel.update();
}
public double getRadius() { return radius;}
public void setCircumference( double c) { …}
public double getCircumference() {return circumference;}
class Circles extends JFrame implements ActionListener {
Circle aCircle;
public Circles( Circle circleObject) {
aCircle = circleObject;
aCircle.setUI( this);
}
// convert given radius to circumference or vice versa
public void actionPerformed( ActionEvent e) {
}
}
if ( e.getSource() == radius) {
double newRadius = Double.parseDouble( radius.getText() );
aCircle.setRadius( newRadius);
}
else . . .
public void update() {
DecimalFormat fmt = new DecimalFormat( "0.##");
circumference.setText( fmt.format( aCircle.getCircumference() ) );
radius.setText( fmt.format( aCircle.getRadius() ));
}
Using Observer framework
class Circle extends Observable {
double radius, circumference;
public Circle( double r){
setRadius( r);
}
public void setRadius( double r) {
radius = r;
circumference = 2 * Math.PI * r;
setChanged();
notifyObservers();
}
public double getRadius() {return radius;}
public void setCircumference( double c) {…
}
public double getCircumference() {return circumference;}
}
Using Observable
class Circles extends JFrame implements ActionListener, Observer {
Circle aCircle;
public Circles( Circle circleObject) {
aCircle = circleObject;
aCircle.addObserver( this);
}
// convert given radius to circumference or vice versa
public void actionPerformed( ActionEvent e) {
}
}
if ( e.getSource() == radius) {
double newRadius = Double.parseDouble( radius.getText() );
aCircle.setRadius( newRadius);
}
else . . .
public void update( Observable o, Object arg) {
Circle oCircle = (Circle) o;
DecimalFormat fmt = new DecimalFormat( "0.##");
circumference.setText( fmt.format( oCircle.getCircumference() ) );
radius.setText( fmt.format( oCircle.getRadius() ));
}
Download