Beruflich Dokumente
Kultur Dokumente
JavaFX
6 - Architecture MVC
Gestion des vnements
Jacques BAPST
jacques.bapst@hefr.ch
Architecture MVC
Structure d'une application
IHM-1 FX06
Jacques BAPST
Jacques BAPST
Jacques BAPST
Model
View
Le contrleur peut
consulter et mettre
jour le modle en fonction
des vnements.
Controller
Flux de donnes
vnements
IHM-1 FX06
Jacques BAPST
Interface
utilisateur
Rsultats
Vue
Stockage
Traitements
Dcisions
Action
interprte
Utilisateur
IHM-1 FX06
Mise jour
de l'interface
Oprations
(fonctions)
Contrleur
Jacques BAPST
Modle
MVC Modle
Le modle (Model) est responsable de la gestion des donnes qui
caractrisent l'tat du systme et son volution.
Dans certaines situations (simples) le modle peut contenir luimme les donnes mais, la plupart du temps, il agit comme un
intermdiaire (proxy) et gre l'accs aux donnes qui sont stockes
dans une base de donnes, un serveur d'informations, le cloud,
Le modle est souvent dfini par une ou plusieurs interfaces Java qui
permettent de s'abstraire de la faon dont les donnes (les objets
mtier) sont rellement stockes (notion de DAO Data Access Object).
Il offre galement les mthodes et fonctions permettant de grer,
transformer et manipuler ces donnes.
Les informations gres par le modle doivent tre indpendantes
de la manire dont elles seront affiches. Le modle doit pouvoir
exister indpendamment de la reprsentation visuelle des donnes.
IHM-1 FX06
Jacques BAPST
MVC Vue
La vue (View) est charge de la reprsentation visuelle des
informations en faisant appel des crans, des fentres, des
composants, des conteneurs (layout), des botes de dialogue, etc.
Plusieurs vues diffrentes peuvent tre bases sur le mme modle
(plusieurs reprsentations possibles d'un mme jeu de donnes).
La vue intercepte certaines actions de l'utilisateur et les transmet au
contrleur pour qu'il les traite (souris, clavier, gestes, ).
La mise jour de la vue peut tre dclenche par un contrleur ou
par un vnement signalant un changement intervenu dans les
donnes du modle par exemple (mode asynchrone).
La reprsentation visuelle des informations affiches peut dpendre
du Look-and-Feel adopt (ou impos) et peut varier d'une
plateforme l'autre. L'utilisateur peut parfois modifier lui mme le
thme de prsentation des informations.
IHM-1 FX06
Jacques BAPST
MVC Contrleur
Le contrleur (Controller) est charg de ragir aux diffrentes
actions de l'utilisateur ou d'autres vnements qui peuvent
survenir.
Le contrleur dfinit le comportement de l'application et sa logique
(comment elle ragit aux sollicitations, business logic).
Dans les applications simples, le contrleur gre la synchronisation
entre la vue et le modle (rle de chef d'orchestre).
Le contrleur est inform des vnements qui doivent tre traits et
sait d'o ils proviennent.
La plupart des actions tant interceptes (ou en lien) avec la vue, il
existe un couplage assez fort entre la vue et le contrleur.
Le contrleur communique gnralement avec le modle et avec la
vue. C'est le sens des transferts et le mode de communication qui
caractrisent diffrentes variantes de l'architecture MVC.
IHM-1 FX06
Jacques BAPST
Jacques BAPST
10
C o mp l me nt
MVP
: Model - View - Presenter
MVVM : Model - View - View-Model
Source: tomyrhymond.wordpress.com/2011/09/16/mvc-mvp-and-mvvm
IHM-1 FX06
Jacques BAPST
11
IHM-1 FX06
Jacques BAPST
12
Jacques BAPST
13
IHM-1 FX06
Jacques BAPST
14
// Thread
________________________________________________________________________________
Callback1()
code1;
// Button clicked
Callback2()
code2;
// Key pressed
Callback3()
code3
// Window resized
. . .
. . .
// Pinch gesture
}
IHM-1 FX06
Jacques BAPST
15
vnement [1]
Un vnement (event) constitue une notification qui signale que
quelque chose s'est pass (un fait, un acte digne d'intrt).
Un vnement peut tre provoqu par :
Une action de l'utilisateur
Un clic avec la souris
La pression sur une touche du clavier
Le dplacement d'une fentre
Un geste sur un cran tactile
...
IHM-1 FX06
Jacques BAPST
16
vnement [2]
En JavaFX les vnements sont reprsents par des objets de la
classe Event ou, plus gnralement, d'une de ses sous-classes.
De nombreux vnements sont prdfinis (MouseEvent, KeyEvent,
DragEvent, ScrollEvent, ) mais il est galement possible de crer
ses propres vnements en crant des sous-classes de Event.
Chaque objet de type "vnement" comprend (au moins) les
informations suivantes :
Le type de l'vnement (EventType consultable avec getEventType())
Le type permet de classifier les vnements l'intrieur d'une mme classe
(par exemple, la classe KeyEvent englobe KEY_PRESSED, KEY_RELEASED, KEY_TYPED)
La source de l'vnement (Object consultable avec getSource())
Objet qui est l'origine de l'vnement selon la position dans la chane de
traitement des vnements (event dispatch chain).
La cible de l'vnement (EventTarget consultable avec getTarget())
Composant cible de l'vnement (indpendamment de la position dans la
chane de traitement des vnements (event dispatch chain)
IHM-1 FX06
Jacques BAPST
17
Types d'vnements
Chaque vnement est d'un certain type (objet de type EventType).
Chaque type d'vnement possde un nom (getName()) et un type
parent (getSuperType()).
Les types d'vnement forment donc une hirarchie.
Par exemple si on presse une touche le nom de l'vnement est
KEY_PRESSED et le type parent est KeyEvent.ANY.
A la racine, on a
Event.ANY
(= EventType.ROOT)
Jacques BAPST
18
IHM-1 FX06
Jacques BAPST
19
Label
IHM-1 FX06
TextArea
HBox
Button
Button
Button
Insert
Delete
Quit
Jacques BAPST
20
Scene
BorderPane
Label
IHM-1 FX06
TextArea
HBox
Button
Button
Button
Insert
Delete
Quit
Jacques BAPST
21
Stage
Scene
BorderPane
Label
IHM-1 FX06
TextArea
HBox
Button
Button
Button
Insert
Delete
Quit
Jacques BAPST
22
Jacques BAPST
23
IHM-1 FX06
setOnAction(Handler)
setOnKeyTyped(Handler)
Jacques BAPST
24
Jacques BAPST
25
Jacques BAPST
26
BorderPane
HBox
Label
TextArea
Button
Button
Button
root
btnPanel
lblTitle
txaMsg
btnInsert
btnDelete
btnQuit
=
=
=
=
=
=
=
new
new
new
new
new
new
new
BorderPane();
HBox(10);
Label("Event Handling");
TextArea();
Button("Insert");
Button("Delete");
Button("Quit");
primaryStage.setTitle("Event Handling");
root.setPadding(new Insets(10));
//--- Title
lblTitle.setFont(Font.font("System", FontWeight.BOLD, 20));
lblTitle.setTextFill(Color.DARKGREEN);
BorderPane.setAlignment(lblTitle, Pos.CENTER);
BorderPane.setMargin(lblTitle, new Insets(0, 0, 10, 0));
root.setTop(lblTitle);
//--- Text-Area
txaMsg.setWrapText(true);
txaMsg.setPrefColumnCount(15);
txaMsg.setPrefRowCount(10);
root.setCenter(txaMsg);
IHM-1 FX05
Jacques BAPST
27
IHM-1 FX05
Jacques BAPST
28
IHM-1 FX06
Jacques BAPST
29
IHM-1 FX06
Jacques BAPST
30
Jacques BAPST
31
{
Platform.exit();
});
IHM-1 FX06
Jacques BAPST
32
Classe 'contrleur'
Dans la variante MVC synchrone, il est frquent que la classe du
contrleur reoive dans son constructeur les rfrences du modle
et de la vue.
public class ButtonController implements EventHandler<ActionEvent> {
Rfrences du
modle et de la vue.
Jacques BAPST
33
vnement
KeyEvent
Dans classe
Node, Scene
MouseEvent
Node, Scene
MouseDragEvent
Node, Scene
DragEvent
Node, Scene
ScrollEvent
Node, Scene
RotateEvent
Node, Scene
SwipeEvent
TouchEvent
ZoomEvent
ContextMenuEvent
Node, Scene
Node, Scene
Node, Scene
Node, Scene
Jacques BAPST
34
ListView.
d'une table ou
TableColumn.
EditEvent
CellEditEvent
TreeView.
EditEvent
Erreur survenue dans le media-player
MediaErrorEvent
Menu est affich (droul) ou masqu (enroul) Event
Fentre popup masque
Event
Onglet slectionn ou ferm
Event
Fentre affiche, ferme, masque
WindowEvent
IHM-1 FX06
Jacques BAPST
Dans classe
Node, Scene
ButtonBase
ComboBoxBase
ContextMenu
MenuItem
TextField
ListView
TableColumn
TreeView
MediaView
Menu
PopupWindow
Tab
Window
35
Fantas'TIP [1]
Exemple d'application pour illustrer diffrentes manires de raliser
le dcoupage du code en exploitant plusieurs des techniques qui
sont disposition (classes EventHandler, expressions lambda, binding de
proprits, ), tout en respectant les principes de l'architecture MVC.
L'application Fantas'TIP est un utilitaire qui calcule le pourboire
prvoir et le montant par personne, en fonction du montant de la
note, du pourcentage octroy et du nombre de convives.
IHM-1 FX06
Jacques BAPST
36
Fantas'TIP [2]
Quatre variantes de cette application ont t cres :
Variante 1 : Avec un contrleur ralis sous forme de classe 'ordinaire'
Variante 2 : Avec un contrleur ralis sous forme d'expression lambda
Variante 3 : Avec un contrleur ralis sous forme de liaisons de hautniveau (high-level binding) entre les donnes d'entre
(saisies par l'utilisateur) et les donnes de sortie (calcules)
Variante 4 : Avec un contrleur ralis sous forme de liaisons de basniveau (low-level binding) entre les donnes d'entre
(saisies par l'utilisateur) et les donnes de sortie (calcules)
Jacques BAPST
37
Fantas'TIP [3]
Variante 1 : modle de l'application
Interface du modle
Classe implmentant cette interface
public interface IFantasTipModel {
void
setBill(double amount);
void
setTipPercent(int percent);
void
setNbPeople(int nbPeople);
double getTipPerPerson();
double getTotalPerPerson();
}
public class FantasTipModel implements IFantasTipModel {
private double
private int
private int
bill
tipPercent
nbPeople
= 0;
= 0;
= 1;
//---------------------------------------------------------------------------public FantasTipModel() {
}
//---------------------------------------------------------------------------@Override
public void setBill(double amount) {
if (amount < 0) throw new IllegalArgumentException("Amount < 0");
this.bill = amount;
}
IHM-1 FX06
Jacques BAPST
38
Fantas'TIP [4]
//---------------------------------------------------------------------------@Override
public void setTipPercent(int percent) {
if (percent < 0) throw new IllegalArgumentException("Percent < 0");
this.tipPercent = percent;
}
//---------------------------------------------------------------------------@Override
public void setNbPeople(int nbPeople) {
if (nbPeople <= 0) throw new IllegalArgumentException("Nb people <= 0");
this.nbPeople = nbPeople;
}
//---------------------------------------------------------------------------@Override
public double getTipPerPerson() {
return bill * tipPercent / 100.0 / nbPeople;
}
//---------------------------------------------------------------------------@Override
public double getTotalPerPerson() {
return bill/nbPeople + getTipPerPerson();
}
}
IHM-1 FX06
Jacques BAPST
39
Fantas'TIP [5]
Variante 1 : classe contrleur (du bouton 'Calculate')
public class FantasTipController implements EventHandler<ActionEvent> {
private IFantasTipModel model;
private FantasTipView
view;
//---------------------------------------------------------------------------// Constructor receives model and view references
//---------------------------------------------------------------------------public FantasTipController(IFantasTipModel model, FantasTipView
view) {
this.model = model;
this.view = view;
}
//---------------------------------------------------------------------------// Method executed when 'Calculate' button is pressed
//---------------------------------------------------------------------------@Override
public void handle(ActionEvent event) {
//--- Get values from view (check for errors) and update model data
try {
double bill
= view.getBillValue();
int
tipPercent = view.getTipPercentValue();
int
nbPeople
= view.getNbPeopleValue();
model.setBill(bill);
model.setTipPercent(tipPercent);
model.setNbPeople(nbPeople);
}
catch (IllegalStateException e) {
return;
}
IHM-1 FX06
Jacques BAPST
40
Fantas'TIP [6]
Variante 1 : cration d'une instance du contrleur et association au
bouton Calculate.
Ces activits sont effectues dans le code de la vue (la mthode prive
createController() est appele dans la mthode start()).
. . .
//---------------------------------------------------------------------------// Create button controller instance and associate it to button
//---------------------------------------------------------------------------private void createController() {
controller = new FantasTipController(model, this);
btnCalc.setOnAction(controller);
}
. . .
IHM-1 FX06
Jacques BAPST
41
Fantas'TIP [7]
Variante 2 : le contrleur est cr sous forme d'expression lambda.
L'expression lambda est crite dans le code de la vue (la mthode
createController() est appele dans la mthode start()).
. . .
//---------------------------------------------------------------------------// Create button controller with lambda expression
//---------------------------------------------------------------------------private void createController() {
btnCalc.setOnAction(event -> {
try {
double bill
= getBillValue();
int
tipPercent = getTipPercentValue();
int
nbPeople
= getNbPeopleValue();
model.setBill(bill);
model.setTipPercent(tipPercent);
model.setNbPeople(nbPeople);
}
catch (IllegalStateException e) {
return;
}
Mme si le code du
contrleur est crit
dans le fichier source
de la vue, l'expression
lambda constitue bien
un contrleur qui sera
reprsent par une
instance d'une classe
anonyme qui
implmente l'interface
EventHandler.
IHM-1 FX06
Jacques BAPST
42
Fantas'TIP [8]
Dans les variantes 3 et 4, les vues possdent des liaisons (par
binding) entre les donnes d'entres et les donnes de sortie.
L'interface n'a donc plus besoin de bouton pour dclencher le calcul.
Les donnes de sortie sont automatiquement mises jour lorsqu'on
change les donnes d'entre (durant la saisie des champs texte).
IHM-1 FX06
Jacques BAPST
43
Fantas'TIP [9]
Variante 3 : le contrleur est constitu par les liaisons (bindings)
cres entre les proprits des composants d'entre et ceux de
sortie.
Dans cette variante, on cre des liaisons dites de haut-niveau (highlevel bindings) car les oprations sont effectues par des invocations
de mthodes en cascade. Cet enchanement d'appels est rendu
possible par l'utilisation d'un modle de conception appel fluent
interface pattern ou fluent API.
Les oprations disponibles sont limites mais suffisantes pour les calculs
ncessaires dans l'application propose.
Ex : result = a.multiply(b).add(c.multiply(d));
44
Fantas'TIP [10]
Variante 3 : mthode createViewModelBindings() appele dans la
mthode start() pour crer les liaisons entre les proprits.
. . .
IHM-1 FX06
Jacques BAPST
45
Fantas'TIP [11]
Variante 3 : mthode dsConverter() utilise pour convertir une
proprit de type double en String et inversement.
. . .
IHM-1 FX06
Jacques BAPST
46
Fantas'TIP [12]
Variante 4 : le contrleur est constitu par les liaisons (bindings) de
bas-niveau (low-level bindings) entre les proprits des composants
d'entre et ceux de sortie.
Pour crer des liaisons de bas-niveau, on redfinit les mthodes
computeValue() de liaisons existantes (on cre des sous-classes de
IntegerBinding, DoubleBinding, StringBinding, etc.).
On dispose dans ce cas de tout le potentiel des instructions et des
librairies disposition pour effectuer les calculs qui lient les proprits
(aussi complexes soient-ils).
Ne pas oublier de dfinir toutes les proprits dont dpend la liaison en
invoquant la mthode parente super.bind(p1, p2, )
IHM-1 FX06
Jacques BAPST
47
Fantas'TIP [13]
Variante 4 : mthode dblTipPerPersonBinding() qui retourne une
spcialisation de DoubleBinding (qui calcule le pourboire par convive).
. . .
IHM-1 FX06
Jacques BAPST
48
Fantas'TIP [14]
Variante 4 : mthode strTipPerPersonBinding() qui retourne une
spcialisation de StringBinding (qui convertit et formate la valeur).
. . .
IHM-1 FX06
Jacques BAPST
49
Fantas'TIP [15]
Variante 4 : mthode createViewModelBindings() qui effectue
l'ensemble des liaisons entre les diffrentes proprits.
. . .
IHM-1 FX06
Jacques BAPST
50