CS535 Object-Oriented Programming & Design
Fall Semester, 1996
Doc 23 AWT Buttons, Model-View
[To Lecture Notes Index]
San Diego State University -- This page last updated Nov 14, 1996
Contents of Doc 23 AWT Buttons, Model-View
- References
- Very Short Overview of AWT Parts
- Buttons
- Handling Button Events
- Custom Buttons
- Separating the GUI from the Program
- Model-View
- Observable and Observer
Core Java, Chapter 7
Graphic Java, Geary and McClellan, Chapter 4
Design Patterns: Elements of Reusable Object-Oriented Software, Gamma,
Helm, Johnson, Vlissides, pages 293-304
Components
Components are basic UI things
Buttons,
Checkboxes, Choices, Lists, Menus, and Text Fields
- When a user activates one of these controls -- by clicking a button for
example -- it posts an event (ACTION_EVENT). An object that contains the
control can react to the event by implementing the action() or handleEvent()
method.
Canvases and Text Areas
- Ways of Getting User Input
Scrollbars and Labels
Component Classes
Containers: Windows and Panels
Containers contain components and containers
Window
- Dialog, FileDialog, and Frame are subclasses
-
- Provide windows to contain components
Panels
- Group components within an area of an existing window
Container Classes
Component
action(Event,Object) | lostFocus(Event,Object) | |
addNotify() | minimumSize() | |
bounds() | mouseDown(Event,int,int) | |
checkImage(Image,ImageObserver) | mouseDrag(Event,int,int) | |
createImage(ImageProducer) | mouseEnter(Event,int,int) | |
createImage(int,int) | mouseExit(Event,int,int) | |
deliverEvent(Event) | mouseMove(Event,int,int) | |
disable() | mouseUp(Event,int,int) | |
enable() | move(int,int) | |
enable(boolean) | nextFocus() | |
getBackground() | paint(Graphics) | |
getColorModel() | paintAll(Graphics) | |
getFont() | paramString() | |
getFontMetrics(Font) | postEvent(Event) | |
getForeground() | preferredSize() | |
getGraphics() | prepareImage(Image,ImageObserver) | |
getParent() | print(Graphics) | |
getPeer() | printAll(Graphics) | |
getToolkit() | removeNotify() | |
gotFocus(Event,Object) | repaint() | |
handleEvent(Event) | repaint(int,int,int,int) | |
hide() | repaint(long) | |
imageUpdate(Image,int, ...) | repaint(long,int,int,int,int) | |
inside(int,int) | requestFocus() | |
invalidate() | reshape(int,int,int,int) | |
isEnabled() | resize(Dimension) | |
isShowing() | resize(int,int) | |
isValid() | setBackground(Color) | |
isVisible() | setFont(Font) | |
keyDown(Event,int) | setForeground(Color) | |
keyUp(Event,int) | show() | |
layout() | show(boolean) | |
list() | size() | |
list(PrintStream) | toString() | |
list(PrintStream,int) | update(Graphics) | |
locate(int,int) | validate() | |
location() | |
Adding a Button to a Window
import java.awt.*;
class ButtonExample extends Frame
{
public ButtonExample( int widthInPixels, int heightInPixels )
{
setTitle( "Button Example" );
resize( widthInPixels, heightInPixels );
setLayout( new FlowLayout() );
add( new Button( "Red" ) );
add( new Button( "Blue" ) );
show();
}
}
class TestButton
{
public static void main( String args[] )
{
new ButtonExample(200, 50);
}
}
Button Methods
getLabel() | setLabel(String) |
Layouts
Need to specify where items will appear in a window that changes size
Must work on different platforms:
- PC, Mac, Unix, PDA, toasters, TVs
Layouts provide a flexible way to place items in a window without specifing
coordinates
The default layout for a frame is BorderLayout
The action method is sent when button has been pressed
import java.awt.*;
class ButtonExample extends Frame
{
public ButtonExample( int widthInPixels, int heightInPixels )
{
setTitle( "Button Example" );
resize( widthInPixels, heightInPixels );
setLayout( new FlowLayout() );
add( new Button( "Red" ) );
add( new Button( "Blue" ) );
show();
}
public boolean action( Event processNow, Object argument )
{
if ( buttonPressed.equals( "Red" ) )
setBackground( Color.red );
else if ( buttonPressed.equals( "Blue" ) )
setBackground( Color.blue );
else return false;
repaint(); // Show effect of color change
return true;
}
public static void main( String args[] ){
ButtonExample window = new ButtonExample(200, 50);
}
}
Identifying Buttons by LabelJust Say No
class ButtonExample extends Frame
{
Button red = new Button( "Red" );
Button blue = new Button( "Blue" );
public ButtonExample( int widthInPixels, int heightInPixels )
{
setTitle( "Button Example" );
resize( widthInPixels, heightInPixels );
setLayout( new FlowLayout() );
add( red );
add( blue );
show();
}
public boolean action( Event processNow, Object argument )
{
if ( processNow.target == red )
setBackground( Color.red );
else if ( processNow.target == blue )
setBackground( Color.blue );
else return false;
repaint(); // Show effect of color change
return true;
}
}
Paint vs. Repaint
repaint calls paint plus more
Don't call paint directly
import java.awt.*;
class PaintRepaint extends Frame{
int drawPoint = 10;
Button move = new Button( "Move" );
public PaintRepaint( int widthInPixels, int heightInPixels ) {
setTitle( "PaintRepaint Example" );
resize( widthInPixels, heightInPixels );
setLayout( new FlowLayout() );
add( move );
show();
}
public void paint( Graphics display ){
display.drawString( "Hello World", drawPoint, drawPoint );
}
public boolean action( Event processNow, Object argument ) {
if ( processNow.target == move )
drawPoint = drawPoint + 15;
else return false;
repaint(); // Show effect moving the text
return true;
}
public static void main( String args[] ) {
new PaintRepaint(250, 150);
}
}
Output
Starting Position
After Several Click on Move
Problem
Big case statement!!
For each button we need an if statement
public boolean action( Event processNow, Object argument )
{
if ( processNow.target == red )
setBackground( Color.red );
else if ( processNow.target == blue )
setBackground( Color.blue );
else if ( processNow.target == ok )
//code here
else if ( processNow.target == quit )
//code here
else if ( processNow.target == pause )
//code here
else if ( processNow.target == start )
//code here
else if ( processNow.target == green )
etc.
A Solution
Let button handle the event
Button events are first sent to the button
Can use action or handleEvent
class ColorButton extends Button
{
Frame windowToColor;
Color paintColor;
public ColorButton( String buttonName, Color colorToUse,
Frame toColor )
{
super( buttonName );
paintColor = colorToUse;
windowToColor = toColor;
}
public boolean action( Event processNow, Object argument )
{
windowToColor.setBackground( paintColor );
windowToColor.repaint();
return true;
}
}
Note: argument will contain the label of the button!
Using Custom Button Class
import java.awt.*;
class ButtonExample extends Frame
{
public ButtonExample( int widthInPixels, int heightInPixels )
{
setTitle( "Button Example" );
resize( widthInPixels, heightInPixels );
setLayout( new FlowLayout() );
add( new ColorButton( "Red", Color.red, this ) );
add( new ColorButton( "Blue" , Color.blue, this) );
add( new ColorButton( "Green" , Color.green, this) );
add( new ColorButton( "Yellow" , Color.yellow, this) );
add( new ColorButton( "Dark Gray" , Color.darkGray, this) );
show();
}
}
class TestButton
{
public static void main( String args[] )
{
new ButtonExample(200, 200);
new ButtonExample(200, 200);
}
}
Output
A Simple Example
Problem: create a simple counter that:
- can increase
-
- can decrease
-
- can be reset to zero
Display the counter in a window and allow the user to increase or decrease the
counter via buttons.
A Poor Solution - The Window is the Counter!
class BadCounterExample extends Frame {
Button increase = new Button( "Increase" );
Button decrease = new Button( "Decrease" );
Button reset = new Button( "Reset" );
int count = 0;
public BadCounterExample( int width, int height ) {
setTitle( "Button Example" );
resize( width, height );
setLayout( new FlowLayout() );
add( increase );
add( decrease );
add( reset );
show();
}
public boolean action( Event processNow, Object argument ) {
if ( processNow.target == increase ) count++;
else if ( processNow.target == decrease ) count--;
else if ( processNow.target == reset ) count = 0;
else return false;
repaint();
return true;
}
public void paint( Graphics display ) {
display.drawString( "The count is " + count , 50, 50 );
}
public static void main( String args[] ){
new BadCounterExample( 200, 100 );
}
}
Output After clicking Increase 6 times
What is Wrong with the about program?It works!
Where is the code reuse?
Can it help in the following Situation?
* 3 different ways to display a counter
* Some counters have more than one display
* Some counters are grouped together
A Model is a system of objects that perform a task
A View:
Displays a representation of the model by asking model for values to
display
Handles all interaction with user about the model
Keeping the model and view separate
- Give flexibility in combining new views to a model
-
- Allows multiple views on the same model
Counter Model
class Counter
{
private int count = 0;
public void increase()
{
count++;
}
public void decrease()
{
count--;
}
public void reset()
{
count = 0;
}
public int value()
{
return count;
}
public String toString()
{
return String.valueOf( count );
}
}
A View for the Model
class CounterExample extends Frame
{
Button increase = new Button( "Increase" );
Button decrease = new Button( "Decrease" );
Counter count;
public CounterExample( int width, int height, Counter outside )
{
setTitle( "Model View Button" );
resize( width, height );
setLayout( new FlowLayout() );
add( increase );
add( decrease );
count = outside ;
show();
}
public boolean action( Event processNow, Object buttonPressed )
{
if ( processNow.target == increase )
count.increase();
else if ( processNow.target == decrease )
count.decrease();
else
return false;
repaint();
return true;
}
public void paint( Graphics display )
{
display.drawString( "The count is " + count , 50, 50 );
}
}
Driver ProgramTwo Views Two Models
class ButtonTest
{
public static void main( String args[] )
{
Counter test = new Counter();
new CounterExample( 200, 100, test );
test = new Counter();
new CounterExample( 200, 100, test );
}
}
How about Two Views, One Model?
class ButtonTest
{
public static void main( String args[] )
{
Counter test = new Counter();
new CounterExample( 200, 100, test );
new CounterExample( 200, 100, test );
}
}
What Went Wrong?
When view A changes the counter how does view B know to update?
Who's responsibility is it to notify B of the change?
(Dependents)(Publish-Subscribe)
Class java.util.Observable
- Observable object may have any number of Observers
-
- Whenever the Observable instance changes,
- it notifies all of its observers
-
- Notification is done by calling the update() method on all observers.
Interface java.util.Observer
- When implemented, this interface allows all classes to be observable by
instances of class Observer
java.util.Observable Methods
addObserver(Observer)
- Adds an observer to the observer list.
clearChanged()
- Clears an observable change.
countObservers()
- Counts the number of observers.
deleteObserver(Observer)
- Deletes an observer from the observer list.
deleteObservers()
- Deletes observers from the observer list.
hasChanged()
- Returns a true boolean if an observable change has occurred.
notifyObservers()
- Notifies all observers if an observable change occurs.
notifyObservers(Object)
- Notifies all observers of the specified observable change which occurred.
setChanged()
- Sets a flag to note an observable change.
Interface java.util.Observer
update
- Called when observers in the observable list need to be updated
Simple Example
class SimpleCounter extends Observable{
public int count = 0;
public void increase() {
count++;
setChanged();
notifyObservers();
}
}
class SimpleObserver implements Observer {
String id;
public SimpleObserver( String id ) {
this.id = id ;
}
public void update( Observable sender, Object message ) {
System.out.println( "From " + id + " New value " +
((SimpleCounter) sender).count );
}
}
class ObserveTest {
public static void main( String args[] ) {
SimpleCounter test = new SimpleCounter();
SimpleObserver a = new SimpleObserver( "a" );
test.addObserver( a);
test.addObserver( new SimpleObserver( "b" ) );
test.increase();
test.addObserver( new SimpleObserver( "c" ) );
test.increase();
}
}
Running the Example
class ObserveTest {
public static void main( String args[] ) {
SimpleCounter test = new SimpleCounter();
SimpleObserver a = new SimpleObserver( "a" );
test.addObserver( a);
test.addObserver( new SimpleObserver( "b" ) );
test.increase();
test.addObserver( new SimpleObserver( "c" ) );
test.increase();
}
}
Output
From b New value 1
From a New value 1
From c New value 2
From b New value 2
From a New value 2