v 1.1.0 ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ||| ||| !!! MERCI DE LIRE ATTENTIVEMENT CE DOCUMENT AVANT DE COMMENCER LE LABORATOIRE !!! ||| ||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| Le fichier markdown README.MD est un incontournable. N'importe quel projet doit en comporter un pour donner les informations importantes aux nouveaux collaborateurs. L'IDE recommandée pour ce projet est INTELLIJ IDEA 2016 ou supérieur. ECLIPSE peut être utilisé, mais il offre beaucoup moins de fonctionnalités. C'est un peu comme comparer une Tesla S à une deux chevaux, les deux nous permettent d'avancer, mais pas à la même vitesse. -- dans INTELLIJ IDEA, cliquez sur l'icône en haut à droite de l'éditeur pour afficher la version HTML de ce document (show preview only). Vous devez avec le plugin « Markdown support » d'installé. -- vous pouvez sinon consulter la version HTML à cette adresse : https://goo.gl/yeKZ7f
Présentation de l'organisation des sources et de l'architecture du programme.
package | description |
---|---|
ca.usherbrooke.ift215.studyz |
Coeur de l'application, c'est ici que vous allez implémenter les écrans. |
ca.usherbrooke.ift215.studyz.control |
Définition des contrôleurs propres à chaque composant implémenté. |
ca.usherbrooke.ift215.studyz.model |
Description des données métier. |
ca.usherbrooke.ift215.studyz.view |
Description de l'arborescence des vues propre à chaque composant, dans des fichier FXML. |
ca.usherbrooke.ift215.fx |
Mini-librairie offrant un cadre architectural à l'application. |
ca.usherbrooke.ift215.fx.act |
Utilitaire pour gérer des événements haut-niveau, appelés intents. |
La racine de l'arbre est le composant Studyz
, qui hérite de BorderPane
.
Lorsque l'étudiant n'est pas authentifié, le composant Authenticator
est affiché.
Lorsque l'étudiant est authentifié, c'est le composant PagesContainer
qui est affiché.
Vous n'avez pas à implémenter ce comportement.
|-- top : Header |-- StudentProfileReadView | | (ou) Studyz | (et) |-- StudentProfilePage -- |-- StudentProfileEditView | |-- Authenticator | | (ou) |-- center : | (ou) | |-- StudentProfilePassEditView |-- PagesContainer -- | (ou) | |-- StudentCoursesReadView |-- StudentCoursesPage -- | (ou) |-- StudentCoursesEditView
On remarquera qu'au troisième niveau on a des "pages", et au quatrième des "vues". Ce vocabulaire est propre à cette architecture et n'est pas générique.
Je vous recommande cependant de l'adopter, c'est une représentation qui vient du web et que vous devriez comprendre intuitivement.
Une page est un composant qui occupe une majeure partie de la scène, et dont chaque vue porte sur un même objet.
L'utilisateur peut basculer d'une page à l'autre à l'aide du menu en tête de scène.
On a donc différentes vues pour une même page.
Dans le fichier pom.xml
sont définies les librairies dont dépend le projet.
espace de nom | documentation générale | description |
---|---|---|
de.jensd.fontawesomefx |
fontawesomefx | Librairie permettant l'affichage d'icônes issues de polices d'icônes parmi les plus populaires du web (fontawesome, materialdesign, materialicons, ...). |
org.fxmisc.easybind |
easybind | Librairie simple et puissante qui améliore l'api Fluent de Javafx (Bindings) à l'aide de lambdas. |
org.controlsfx |
controlsfx | Librairie de composants javafx, riche et très bien documentée. |
Ci dessous, des cas d'utilisation pour chaque librairie dont vous aurez besoin.
Cette librairie est nécessaire pour les laboratoire 4 & 5
Elle est importée automatiquement par Maven
<Button text="ANNULER" onAction="#handleCancel">
<graphic>
<MaterialDesignIconView glyphName="CLOSE_CIRCLE" size="24"/>
</graphic>
</Button>
Pour voir les icônes disponibles avec MaterialDesignIconView
, se rendre à cette adresse.
Cette librairie est nécessaire pour les laboratoire 4 & 5
Elle est importée automatiquement par Maven
MonadicObservableValue<Teacher> teacherMonadicValue = EasyBind.monadic(teacherProperty);
salaryLabel.textProperty().bind(teacherMonadicValue.selectProperty(Teacher::salaryProperty));
On attache la propriété de texte du salaryLabel
de type Label
à la propriété 'salary' de l'enseignant.
Deux grands avantages :
- Cette liaison est 'null safe' : si la valeur enveloppée par teacherProperty vaut
null
, leString
enveloppé par la propriétée de texte duLabel
vaudranull
, sans qu'une exception soit levée. - Cette liaison est 'type safe' face à son équivalent avec
Bindings
, confère cet exemple.
MonadicObservableValue<Teacher> teacherMonadicValue = EasyBind.monadic(teacherProperty);
uniqueIdentifierLabel.textProperty().bind(teacherMonadicValue.map(Teacher::getUniqueIdentifier))
On attache la propriété de texte du uniqueIdentifierLabel
de type Label
à la valeur 'uniqueIdentifier' de l'enseignant. (ligne 3)
Cet exemple ressemble à s'y méprendre au précédent, à la différence près que le champs de l'enseignant n'est pas une propriété (StringProperty
ou ObjectProperty<Srting>
), mais un simple String
.
Cette section est notamment utile pour le laboratoire 3
L'apparence d'un composant peut être contrôlée par sa classe de style.
En FXML, c'est l'attribut styleClass
qui permet de le définir.
C'est une très bonne pratique que de séparer l'apparence des composants et leur arborescence.
Dans ce laboratoire, vous n'avez pas à implémenter l'apparence des composants, mais simplement de leur donner la bonne
classe de style qui est déjà définie dans src/ca/usherbrooke/ift215/studyz/view/base.css
.
Voici un exemple avec la barre de boutons qui apparaît en bas de chaque vue :
<FlowPane styleClass="intents" alignment="CENTER">
<ButtonBar>
<buttons>
<Button text="CUISINER UN COOKIE"
onAction="#handleCookCookie">
<graphic>
<MaterialDesignIconView glyphName="COOKIE" size="24"/>
</graphic>
</Button>
</buttons>
</ButtonBar>
</FlowPane>
On applique la classe de style intents
pour que l'apparence de la barre de boutons soit la même entre chaque vue.
REMARQUE : Pour appliquer plusieurs styles, il faut les séparer par des virgules.
Cette section est notamment utile pour les laboratoire 4 & 5
Lorsqu'un utilisateur clique sur un bouton, il s'attend à voir la vue s'adapter pour rendre compte de son intent.
En JavaFX, on va définir une méthode dans le contrôleur, par exemple :
// MyComponent.java
public void handleOpenMyTasks() {
this.pagesHandler.openTasks();
}
Cette méthode a besoin d'une référence au composant qui va ouvrir la page de taches, ici pagesHandler
.
On va référencer cette méthode dans le composant Button
de la vue :
<!-- MyComponent.fxml -->
<Button onAction="#handleOpenMyTasks" />
Problème : on a besoin de la référence du composant sur lequel appliquer l'intent
(changer de page, afficher un dialogue, ...etc), ici pagesHandler
.
À mesure que l'application se complexifie, chaque composant comporte un grand nombre de
références à d'autres composants. C'est ce qu'on appelle un couplage fort et c'est
le signe d'une mauvaise qualité de code.
Il existe même une expression consacrée en programmation, le code spaghetti !
Un moteur d'événements haut-niveau - c'est à dire proche du langage naturel - permet la diffusions d'actions de l'utilisateur en vue d'un but. Deux actions distinctes (clic sur un bouton, appuis sur une touche) peuvent avoir le même résultat, comme le chargement d'une interface.
Exemple : L'utilisateur clique sur CTRL + MAJ + R
dans Intellij Idea pour remplacer un mot dans
l'ensemble du dossier sélectionné, ou bien fait Edit → Find → Replace in Path.
L'issue - l'ouverture d'une boîte de dialogue - est la même, mais l'action diffère. C'est pourquoi on va
parler pour la suite d'intention (intent). C'est un niveau d'abstraction supplémentaire
qui permet de simplifier l'architecture globale de l'application.
Le package ca.usherbrooke.ift215.fx.act
expose une API pour gérer facilement les actions de haut-niveau,
appelées "intentions" de l'utilisateur.
Pour Studyz, les intentions sont définies dans l'énumération ca.usherbrooke.ift215.studyz.StudyzIntent
.
Reprenons l'exemple précédent, mais cette fois-ci utilisons le moteur d'événements pour ouvrir la page :
// MyComponent.java
public void handleOpenMyTasks() {
this.intentEngine.dispatch(StudyzIntent.READ_TASKS);
}
Mon composant n'a maintenant besoin que d'une seule référence à un IntentEngine
, plus de spaghetti !
Voici à quoi ressemble le code du composant qui va afficher les tâches :
// MyParentComponent.java
public MyComponentParent(IntentEngine intentEngine) {
// à chaque fois que l'événement READ_TASKS est propagé, displayTasks sera appelé.
intentEngine.subscribe(SomeIntent.READ_TASKS, this::displayTasks);
}
public void displayTasks() {
// du code interne au composant pour afficher les tâches
}
Dans le fichier FXML, rien ne change :
<!-- MyComponent.fxml -->
<Button onAction="#handleOpenMyTasks" />
Il existe des mécanismes plus élaborés, populaires dans certains frameworks du web comme redux, qui ne sont pas à la portée de ce cours.