Programmazione
ad Eventi (Parte I)
Programmazione
ad Eventi (Parte II)
Motivazioni
La
libreria SWING
La gerarchia di classi
Il modello MVC (Model View Controller)
Il layout manager
Costruire le interfacce utenti in java
Gli ambienti IDE (Eclipse, JBuilder, Netbeans)
Implementiamo una chat…
(interazione sincrona e asincrona)
paradigma ad eventi
Programmare la GUI
Il
La
libreria AWT (modelli basati su ereditarietà e
su delega)
Interazione sincrona
Leggi - elabora - scrivi
Inizio (main classico)
Esecuzione sequenziale di un insieme di
istruzioni
In punti prestabiliti il controllo viene passato
all’utente
Input
public static void main(String[] argv) {
…
leggi
…
elabora
…
scrivi
…
}
Elabora
Output
Fine (ritorno al sistema operativo)
Comunicazione sincrona
Chiamata a funzione
Identificazione del chiamato
Invocazione della funzione
Passaggio dei parametri (opzionale)
Esecuzione della funzione
Ritorno dei valori (opzionale)
Il chiamante aspetta la terminazione
del chiamato
Comunicazione asincrona
Vorrei disporre di un meccanismo asincrono
Il chiamante invia la comunicazione e
continua la propria esecuzione
Il chiamato si attiva quando riceve la
comunicazione, esegue il codice e poi
termina
Non c’e’ il trasfermiento del controllo
(L’esecuzione del chiamante e del chiamato
sono disaccoppiate)
1
Paradigma ad eventi
Progetto programmi che reagiscono a
stimoli esterni: Gli eventi
realtà
Applicazione
Il programma è una collezione di funzioni
attivabili su richiesta
Non devo prevedere a priori la sequenza
delle azioni
L’applicazione è puramente reattiva
Non è possibile identificare staticamente un
flusso di controllo unitario
Occorrenza dell’ evento B
Occorrenza dell’ evento A
Non esiste il main classico
Non c’e’ un unico algoritmo
Non c’e’ un inizio ed una fine
Programmazione ad eventi
(caratteristiche)
Programmazione ad eventi
(architettura)
Il main esiste ma si limita a inizializzare
l’applicazione istanziando gli osservatori e
gli handler
Possono coesistere piu flussi di
esecuzione, uno per ogni evento (c’è
bisogno della concorrenza)
GUI schema di
funzionamento
Osservatore
evento A
Handler
evento A
Osservatore
evento B
Handler
evento A e B
Handler
evento B
Esempi di applicazioni
Sistemi operativi
Applicazioni di rete
Interrupt, driver di dispositivo
client server, scambio messaggi, peer
to peer
Applicazione interattive con
interfaccia grafica a finestre
Programmazione ad eventi con
un linguaggio procedurale
Widget (osservatore)
button
Callback (handler)
Void pressed() {
…
}
Application loop
Esempio il C in ambiente Xwindow e win32
I widget sono delle strutture dati
Le callback sono delle funzioni in C
Ogni callback è associata al rispettivo
widget attraverso la chiamata ad una
opportuna funzione di libreria
Ho bisogno di un loop infinito per essere
reattivo agli eventi
While (true) { … }
2
Esempio Xwindows
Esempio win32
/*-----------------------------------------------------------HELLOWIN.C -- Displays "Hello, Windows 98!" in client area
(c) Charles Petzold, 1998
Changes made by Naomi Novik, 2001
------------------------------------------------------------*/
#include
#include
#include
#include
<stdio.h>
<X11/Intrinsic.h>
<X11/StringDefs.h>
<X11/Xaw/Command.h>
#include <windows.h>
// Use a 24-point font
#define FONT_SIZE 24
// have to declare the windows procedure before the entry point
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
void push(Widget w, XtPointer client_data, XtPointer event_data) {
printf("Button pushed!\n");
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, i nt iCmdShow)
{
static TCHAR s zAppName[ ] = TEXT ("HelloWin") ;
HWND
h wnd ;
MSG
m sg ;
WNDCLASS
w ndclass ;
void main(int argc, char *argv[]) {
XtAppContext ac;
Widget top, button;
top = XtVaAppInitialize(&ac, "XButton", NULL, 0, &argc, argv, NULL,
NULL);
button = XtVaCreateManagedWidget("Press here", commandWidgetClass,
top, NULL);
// We need to define the window class, which is responsible for
// identifying the window procedure that will be used to handle
// messages to this window
// can just stick with this for now -- indicates that window should
// be repainted whenever horizontal (HREDRAW) or vertical (VREDRAW)
// size changes
wndclass.style
= CS_HREDRAW | CS_VREDRAW ;
XtAddCallback(button, XtNcallback, push, NULL);
XtRealizeWidget(top);
XtAppMainLoop(ac);
// *** defines the procedure that handles the messages ***/
wndclass.lpfnWndProc
= WndProc ;
wndclass.cbClsExtra
wndclass.cbWndExtra
wndclass.hInstance
wndclass.hIcon
wndclass.hCursor
wndclass.hbrBackground
wndclass.lpszMenuName
}
Esempio wi32 (cont…)
// The windows procedure handles all of the messages received by the
// window
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC
hdc ;
PAINTSTRUCT ps ;
RECT
rect ;
int fontHeight;
HFONT
f ont;
if (!RegisterClass (&wndclass))
{
// If the preprocessor definition UNICODE is defined, then
// RegisterClass will fail unless the operating system
// has Unicode support (Win NT, Win2K)
MessageBox (NULL, TEXT ("This program requires Windows NT or Windows 2000!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
Loop infinito
0 ;
0 ;
hInstance ;
LoadIcon (NULL, IDI_APPLICATION) ;
LoadCursor (NULL, IDC_ARROW) ;
(HBRUSH) GetStockObject (WHITE_BRUSH) ;
NULL ;
Esempio win32 (cont…)
// must give the window class a name -- note declaration of szAppName
// above.
wndclass.lpszClassName = szAppName ;
// Can create multiple windows based on a single window class -- this
// creates one
hwnd = CreateWindow (szAppName,
// window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW,
// window style
CW_USEDEFAULT,
// initial x position
CW_USEDEFAULT,
// initial y position
CW_USEDEFAULT,
// initial x size
CW_USEDEFAULT,
// initial y size
NULL,
// parent window handle
NULL,
// window menu handle
hInstance,
// program instance handle
NULL) ;
// creation parameters
=
=
=
=
=
=
=
switch (message)
{
// message received on window creation
case WM_CREATE:
PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
callback
return 0 ;
// message received whenever window needs to be repainted
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
handler
// create the font we're going to use
// look up CreateFont documentation to see what all the args do
// Height
// the first argument to M ulDiv is the desired point size,
// and note that this will work only if we're in the MM_TEXT
// mapping mode.
fontHeight = -MulDiv(FONT_SIZE, GetDeviceCaps(hdc, LOGPIXELSY), 72);
// when displaying, pass on preferences from args to W inMain
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
font = C reateFont(fontHeight, 0, 0, 0, FW_DONTCARE,
FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
VARIABLE_PITCH | FF_ROMAN,
TEXT("Arial") );
// here's the message loop that dispatches Windows messages
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
SelectObject(hdc, font);
}
GetClientRect (hwnd, &rect) ;
Esempio win32 (cont…)
DrawText (hdc, TEXT ("Hello, World!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
Programmazione ad eventi
in un linguaggio OO
Esempio Java
Due approcci possibili
EndPaint (hwnd, &p s) ;
return 0 ;
// message received whenever window is destroyed
case WM_DESTROY:
// inserts a WM_QUIT message into queue -- causes GetMessage
// in the message loop above to return 0
PostQuitMessage (0) ;
return 0 ;
}
// MUST return default, otherwise things like quitting won't work
return DefWindowProc (h wnd, message, wParam, lParam) ;
}
Ereditarietà AWT 1.0
Delegazione AWT 1.1
3
Approccio per ereditarietà
(1)
Ogni widget è un oggetto (i.e., una istanza)
Esiste una classe Java per ogni tipo di
widget
Approccio per ereditarietà
(2)
class Button {…}
Le classi che rappresentano i diversi
widget sono organizzate in una
opportuna gerarchia di ereditarietà
class Button extends Component {
…
}
Gli eventi sono descritti come oggetti di un’unica classe e
differenziati attraverso un opportuno attributo id
Approccio per ereditarietà
(3)
I widget (istanze) sono organizzati in una
gerarchia di aggregazione
class Container extends Component {
…
public Component add(Component c){…}
public void remove(Component c) {…}
}
Tale gerarchia determina la posizione dei vari widget
Le callback sono espresse come metodi delle classi dei widget
class Button extends Component {
…
public boolean action(Event, Object) {}
}
AWT 1.0
Per creare un widget di un tipo T con
associata una certa callback bisogna:
creare una sottoclasse T1 di T
ridefinire il metodo action inserendovi
il codice della callback
istanziare T1, creando il widget vero
e proprio
Gli eventi non gestiti sono propagati
lungo la gerarchia di aggregazione
AWT 1.0 dettaglio
AWT 1.0 dettaglio
Il metodo handleEvent, se non ridefinito,
invoca il metodo appropriato (action, keyUp
ecc.)
Se questi ritorna true il metodo handleEvent
ritorna true
altrimenti invoca il metodo handleEvent del
contenitore del widget (accessibile con il
metodo getParent())
I metodi di gestione di specifici eventi
(action, keyUp ecc.), se non ridefiniti,
ritornano false
4
Esempio AWT 1.0
Osservazioni
import java.awt.*;
public class JButton {
static public void main(String[] args) {
Frame top=new Frame("JButton");
Button b=new MyButton("Press here");
top.add(b);
top.pack();
top.show();
}
}
class MyButton extends Button {
public MyButton(String s) { super(s); }
public boolean action(Event e, Object o) {
System.out.println("Button pushed!");
return true;
}
class MyFrame extends Frame {
B utton b;
p ublic M yFrame(String s) {
s uper(s) ;
b =new Button("Press here");
a dd(b) ;
p ack();
}
public b oolean handleEvent(Event evt) {
i f(evt.target==this && e vt.id==Event.WINDOW_DESTROY) {
d ispose();
S ystem.exit(1);
r eturn true;
} else i f(evt.target==b && e vt.id==Event.ACTION_EVENT) {
S ystem.out.println("Button pushed!");
r eturn true;
} else return s uper.handleEvent(evt);
}
Librerie di classi
General purpose
Non contengono logica applicativa
Esempi: (Vector, List, Queue…)
Framework
Per evitare la creazione di due classi
(MyFrame e MyButton) si può affidare
anche la gestione degli eventi relativi al
bottone al frame (propagazione degli eventi)
Difetti di AWT 1.0
}
Librerie vs framework
In particolare la finestra non si chiude (e
l’applicazione non termina) anche se si
preme sull’opportuno bottone
Una possibile soluzione è creare una
classe MyFrame che gestisca tali eventi e
usare questa classe per creare il frame
principale
}
Esempio rivisto
L’applicazione non gestisce altri eventi oltre
quello di pressione del bottone
Per ogni widget bisogna creare una classe diversa solo per
ridefinire il metodo di gestione degli eventi (es. 1)
Tali classi hanno spesso una sola istanza
oppure… bisogna concentrare la gestione degli eventi
relativi a tutti i widget contenuti in un container in un’unica
procedura (es. 2)
complessa,
poco leggibile,
difficile da modificare.
Non è Object Oriented! (e’ simile alla callback dell’esempio
in C)
Non aiuta una separazione chiara tra codice relativo
all’interfaccia e codice applicativo
Tra osservatore dell’evento e gestore dell’evento (event
handler)
Modello basato su delega
In generale è possibile passare da un
progetto basato sull’ereditarietà
(generalizzazione) ad uno basato sulla
delega (aggregazione) e viceversa
Indirizzati per un dominio specifico
Hanno numerose classi astratte
Inglobano in parte la logica di funzionamento
dell’applicazione
Ha un proprio ciclo di vita. Solitamente è il framework
a chiamare te invece che tu a chiamare il framework
Esempi (AWT 1.1, SWING)
5
Approccio per delega (1)
Ogni widget è un oggetto (i.e., una istanza)
Esiste una classe Java per ogni tipo di widget
class Button {…}
Le classi sono organizzate in una opportuna gerarchia
di ereditarietà
class Button extends Component {
…
}
I widget (istanze) sono organizzati in una gerarchia di
aggregazione (component e container)
L’evento diventa una classe e non più una chiamata a
procedura
Approccio per delega (2)
concetto di Listener (ascoltatore)
I widget rappresentano gli osservatori degli eventi
Questi delegano ad opportuni “ event listener” la
gestione degli eventi da loro originati
Gli event listener sono descritti attraverso opportune
interfacce
Un event handler è un qualsiasi oggetto la cui classe
implementi l’interfaccia di un event listener
Gli eventi non gestiti da un opportuno handler NON
sono propagati lungo la gerarchia di aggregazione
realtà
Applicazione
Osservatore
evento A
Handler
evento A
Events e Listners
Occorrenza dell’ evento B
Occorrenza dell’ evento A
ActionEvent, KeyEvent, ...
Event source: buttons, checkboxes, choices, frames
Event listener: una classe delegata a gestire certi eventi
Un listener deve:
Implementare una appropriata interfaccia;
Informare il sorgente che è interessato a gestire
gli eventi da lui generati.
Un listener può ascoltare diverse tipologie di eventi da
diverse sorgenti
Anche il sorgente può essere un Listner (basta
implementare l’interfaccia appropriata)
Listeners possono essere delle classi complete oppure
delle inner classes (metodo piu usato).
Gerarchia degli eventi Java
Handler
evento A e B
Osservatore
evento B
Handler
evento B
Esempi di eventi
Window Events (resize, move, …)
Buttons (pressed, relase, …)
Mouse Events (click, doubleclick, …)
Menus
Dialog Boxes
Text inputs, state changes, choice
boxes, scroll bars
11 listners (interface)
ActionListener
AdjustmentListener
ComponentListener
ContainerListener
FocusListener
ItemListener
KeyListener
MouseListener
MouseMotionListener
TextListener
WindowListener
Ognuno è specializzato
ad “ascoltare” determinati eventi
6
Mouse Events e Mouse
Listners
MouseListener
mouseClicked
mousePressed
mouseReleased
mouseEntered (enters a component)
mouseExited (exits a component)
MouseMotionListener
mouseMoved
mouseDragged
MouseEvent
int getX (), int getY, Point getPoint()
getClickCount()
Struttura dell’applicazione
Creazione di un widget di tipo T con associata una callback:
creare un oggetto della classe T (es Button)
Creare una classe T1 che implementi l’opportuna
interfaccia listener, inserendo il codice della callback
nell’implementazione del metodo definito in questa
interfaccia
istanziare T1, creando l’event handler vero e proprio
registrare tale event handler al widget (metodo addListener)
notifica
Event 1
Quando un mouse si muove sullo schermo vengono generati una
infinità di eventi che, spesso ignorati dall’applicazione
Tali eventi vengono messi in una coda e processati in FIFO
Listner 1
addListner
Widget 1
addListner
Listner 2
notifica
AWT 1.1: java.awt.*
AWT 1.1: java.awt.event.*
Esempio AWT 1.1
Piu ascoltatori
import java.awt.*;
import java.awt.event.*;
public class JButton2 {
static public void main(String[] args) {
Frame top=new Frame("JButton");
Button b=new Button("Press here");
Handler h = new Handler();
b.addActionListener(h);
top.add(b); top.pack(); top.show();
}
}
class Handler implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Button pushed!");
}
}
Widget. Sorgente degli eventi
Creazione dell’osservatore
registrazione dell’ osservatore
presso la sorgente degli eventi
Implementazione dell’osservatore
E’ possibile associare più event listener allo
stesso widget
E’ sufficiente aggiungere i vari event listener
tramite il metodo addXXXListener
L’ordine di chiamate dei vari event listener
non è fissato a priori (non determinismo)
Un oggetto può essere “ascoltatore” di più
eventi
E’ sufficiente che la classe al quale
appartiene implementi diverse interfacce di
tipo event listener
7
Adapter (1)
Adapter (2)
Alcuni event listener comprendono numerose funzioni
public abstract interface java.awt.event.WindowListener
extends java.util.EventListener {
Ridefinire tutte queste funzioni può essere
molto noioso
// Instance Methods
public
public
public
public
public
public
public
abstract
abstract
abstract
abstract
abstract
abstract
abstract
void
void
void
void
void
void
void
windowActivated (WindowEvent e);
windowClosed (WindowEvent e);
windowClosing (WindowEvent e);
windowDeactivated (WindowEvent e);
windowDeiconified (WindowEvent e);
windowIconified (WindowEvent e);
windowOpened (WindowEvent e);
}
Spesso siamo interessati ad una sola di
queste
Le classi astratte “adapter” risolvono
questo problema
Ne esiste una per ogni event listener
“complesso”
Implementano tutti i metodi del rispettivo
event listener come metodi vuoti (i.e., che
non fanno nulla)
Adapter (3)
import j ava.awt.*;
import j ava.awt.event. *;
public class JButton3 {
static public void m ain(String[ ] args) {
F rame top=new F rame("JButton");
B utton b=new Button("Press here");
H andler h = new Handler();
b .addActionListener(h) ; top.addWindowListener(h);
t op.add(b) ; t op.pack(); top.show( );
}
}
class Handler extends WindowAdapter implements ActionListener
p ublic void w indowClosing(WindowEvent e) {
e .getWindow().dispose( ); System.exit(1);
}
p ublic void a ctionPerformed(ActionEvent e) {
S ystem.out.println("Button pushed!");
}
}
8