Tutoriel

Dans ce tutoriel, nous créons une solution simple pour afficher le planning d’une flotte d’avions. Pour installer FlexGanttFX, veuillez suivre les instructions de la section Installation.

Airbus A380

Modèle de vue

Commençons par créer un modèle de vue pour le diagramme de Gantt. Nos objets sont Fleet, Aircraft, Crew et Flight. Les instances de Flight seront affichées sous forme de barre horizontale dans la zone graphique du diagramme de Gantt, tandis que les trois premiers objets seront affichés dans les lignes de la zone de table arborescente. Fleet, Aircraft et Crew partagent une superclasse commune appelée ModelObject, une extension de Row. La classe Row sert à définir une structure de données hiérarchique à l’aide de trois arguments de type : le premier indique le type de la ligne parente, le deuxième le type des lignes enfants, et le troisième le type des activités qui seront affichées sur le côté droit du diagramme de Gantt.

Objet de modèle

    P extends Row<?,?,?>, // Type of parent row
    C extends Row<?,?,?>, // Type of child rows
    A extends Activity> extends Row<P, C, A> { }

Nous pouvons maintenant passer ModelObject comme argument de type lors de la création d’une instance du contrôle GanttChart. Cela indique au contrôle que toutes les lignes auront ce supertype commun.

GanttChart<ModelObject<?,?,?> gantt = new GanttChart<>()

La classe de modèle Aircraft peut être implémentée comme dans le fragment de code suivant, en supposant qu’une flotte se compose de plusieurs avions, que chaque avion dispose d’un équipage et que les vols sont affectés aux avions et aux équipages.

public class Aircraft extends ModelObject<Fleet, Crew, Flight> {
    public Aircraft(String name) {
        super(name);
    }
}

La classe Flight étend MutableActivityBase et pourrait ressembler à ceci :

public class Flight extends MutableActivityBase<FlightData> {
    public Flight(FlightData data) {
        super(data.getFlightName()); // the activity name
        setStartTime(data.getFlightDepartureTime()); // times as java.time.Instant
        setEndTime(data.getFlightArrivalTime());
        setUserObject(data); // a user object according to the type argument above
    }
}

Cette classe définit un vol comme une activité modifiable, ce qui signifie que l’utilisateur peut modifier le vol. Nous voyons également que l’activité obtient ses informations depuis un objet métier de type FlightData. La prise en charge d’un objet utilisateur nous permet de créer un lien entre le modèle métier et le modèle de vue. Il ne reste plus qu’à ajouter les activités (les vols) aux lignes (les avions). Pour cela, il suffit d’appeler la méthode Row.addActivity(Layer, Activity).

Référentiels d’activités

Les lignes ne stockent pas elles-mêmes les activités ; elles délèguent plutôt toutes les fonctionnalités liées aux activités à un référentiel de type ActivityRepository. Le référentiel par défaut est de type IntervalTreeRepository. Les applications peuvent implémenter leurs propres référentiels et les enregistrer en appelant Row.setRepository().

Couches

Les couches servent à créer des groupes d’activités afin de pouvoir les afficher ou les masquer ensemble. Dans notre exemple, nous voulons regrouper les vols selon leur type de service (cargo, charter, formation, etc.).

Layer cargoLayer = new Layer("Cargo");
Layer trainingLayer = new Layer("Training");
Layer charterLayer = new Layer("Charter");

// make layers known to Gantt
gantt.getLayers().addAll(cargoLayer, trainingLayer, charterLayer); 

Le diagramme de Gantt sait maintenant quelles couches il doit afficher, et nous pouvons créer le lien entre les couches et les activités. Cela se fait lorsque nous ajoutons les activités aux lignes (ici : ajouter les vols aux avions).

Flight flight1 = new Flight(); // a cargo flight
Flight flight2 = new Flight(); // a training flight
Flight flight3 = new Flight(); // a charter flight

aircraft1.addActivity(cargoLayer, flight1);
aircraft1.addActivity(trainingLayer, flight2);
aircraft2.addActivity(charterLayer, flight3);

Dans l’exemple de code suivant, nous combinons toutes les étapes précédentes.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import com.flexganttfx.model.Activity;
import com.flexganttfx.model.Layer;
import com.flexganttfx.model.Row;
import com.flexganttfx.model.activity.MutableActivityBase;
import com.flexganttfx.view.GanttChart;

public class MyFirstGanttChart extends Application {

    /*
     * Common superclass of Fleet, Aircraft, and Crew.
     */
    class ModelObject<
        P extends Row<?,?,?>, // Type of parent row
        C extends Row<?,?,?>, // Type of child rows
        A extends Activity> extends Row<P, C, A> { }

    class Fleet extends ModelObject<Row<?,?,?>, Aircraft, Activity> { }

    class Aircraft extends ModelObject<Fleet, Crew, Flight> { }

    class Flight extends MutableActivityBase<Object> { }

    @Override
    public void start(Stage stage) throws Exception {
        // Our root object.
        Fleet fleet = new Fleet();
        fleet.setExpanded(true);

        // Create the control.      
        GanttChart<ModelObject<?,?,?>> gantt = new GanttChart<>(fleet);

        // Layers based on service type.
        Layer cargoLayer = new Layer("Cargo");
        Layer trainingLayer = new Layer("Training");
        Layer charterLayer = new Layer("Charter");
        gantt.getLayers().addAll(cargoLayer, trainingLayer, charterLayer);

        // Create the aircrafts.
        Aircraft aircraft1 = new Aircraft();
        Aircraft aircraft2 = new Aircraft();

        // Add the aircrafts to the fleet.
        fleet.getChildren().addAll(aircraft1, aircraft2);

        // Create the flights
        Flight flight1 = new Flight(); // a cargo flight
        Flight flight2 = new Flight(); // a training flight
        Flight flight3 = new Flight(); // a charter flight
        aircraft1.addActivity(cargoLayer, flight1);
        aircraft1.addActivity(trainingLayer, flight2);
        aircraft2.addActivity(charterLayer, flight3);

        Scene scene = new Scene(gantt);
        stage.setTitle("Fleet Schedule");
        stage.setScene(scene);
        stage.centerOnScreen();
        stage.sizeToScene();
        stage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

L’image ci-dessous montre ce que nous verrons lorsque nous exécuterons ce code.

Résultat intermédiaire

Moteurs de rendu d’activité

Ce résultat n’est pas mauvais pour seulement quelques lignes de code ; cependant, le rendu des vols n’est pas du tout attrayant. Nous pouvons personnaliser leur apparence en enregistrant un autre ActivityRenderer pour le type d’activité Flight. Cela se fait en appelant la méthode GraphicsBase.setActivityRenderer(), où la vue graphique est le contrôle situé sur le côté droit du diagramme de Gantt. Elle est responsable du rendu de toutes les activités. Nous pouvons ajouter les lignes suivantes à notre exemple précédent.

GraphicsView <ModelObject<?, ?, ?>> graphics = gantt.getGraphics();
graphics.setActivityRenderer(Flight.class,
                    GanttLayout.class,
                    new ActivityBarRenderer<>(graphics, "FlightRenderer"));

Cela remplace le moteur de rendu d’activité par défaut par un moteur qui dessine une barre de hauteur fixe. Ce qui est intéressant dans ce code, c’est que nous ne passons pas seulement le type d’activité et l’instance du moteur de rendu, mais aussi un type de disposition. Nous ne voulons pas passer trop de temps sur les dispositions dans ce guide de démarrage rapide, mais disons simplement que FlexGanttFX peut afficher les activités de plusieurs façons différentes (sous forme de barres temporelles, d’entrées de graphique ou d’entrées d’agenda). Notre exemple ressemble maintenant à ceci :

Rendu

Nous pouvons maintenant ajouter un GanttChartToolBar et un GanttChartStatusBar à l’exemple. Cela nous permet d’effectuer des actions sur le diagramme et de vérifier que les couches ont été ajoutées correctement. La bibliothèque « extras » de FlexGanttFX contient des contrôles supplémentaires tels que GanttChartStatusBar et GanttChartToolBar. En utilisant le BorderPane standard, il est facile de combiner le diagramme de Gantt et ces deux contrôles pour créer un contrôle complexe unique.

BorderPane borderPane = new BorderPane();
borderPane.setTop(new GanttChartToolBar(gantt));
borderPane.setCenter(gantt);
borderPane.setBottom(new GanttChartStatusBar(gantt));

Scene scene = new Scene(borderPane);

Notre exemple ressemble maintenant à ceci après avoir cliqué sur le bouton des couches dans la barre d’outils.

Rendu

Écoute des changements

Maintenant que nos données sont visualisées, nous voulons évidemment interagir avec elles et être informés des changements que nous effectuons. Par défaut, nos activités sont modifiables. Cela signifie que nous pouvons les faire glisser horizontalement ou verticalement. Pour recevoir des événements, il suffit d’enregistrer un gestionnaire ActivityEvent auprès du contrôle de vue graphique en appelant GraphicsBase.setOnActivityChanged().

Pour les besoins de ce tutoriel, nous allons simplement enregistrer un gestionnaire qui affichera l’événement dans la console. Cela peut être réalisé ainsi :

graphics.setOnActivityChanged(evt -> System.out.println(evt));

Lorsque nous relançons notre application, nous voyons maintenant la sortie suivante :

event type: DRAG_STARTED, time interval: 2014-04-17T21:45:00Z - 2014-04-22T21:30:00Z,
    value (chart value / percentage complete): 0.0,
    activity "null from 2014-04-18T12:15:00Z until 2014-04-23T12:00:00Z,
    user object = null",
    row = "Default",
    layer = "Training"

event type: DRAG_ONGOING, time interval: 2014-04-17T21:45:00Z - 2014-04-22T21:30:00Z,
    value (chart value / percentage complete): 0.0,
    activity "null from 2014-04-18T12:15:00Z until 2014-04-23T12:00:00Z,
    user object = null",
    row = "Default",
    layer = "Training"

event type: DRAG_FINISHED, time interval: 2014-04-17T21:45:00Z - 2014-04-22T21:30:00Z,
    value (chart value / percentage complete): 0.0,
    activity "null from 2014-04-18T03:00:00Z until 2014-04-23T02:45:00Z,
    user object = null",
    row = "Default",
    layer = "Training"

Notez les trois types d’événements différents DRAG_STARTED, DRAG_ONGOING et DRAG_FINISHED. Le premier est déclenché lorsque l’utilisateur commence un glisser-déposer, le deuxième pendant que le glisser-déposer est en cours, et le troisième lorsque le glisser-déposer est terminé. Ce modèle s’observe dans JavaFX lui-même et a également été implémenté dans tout FlexGanttFX. Consultez les différents types d’événements définis dans la classe ActivityEvent pour découvrir la quantité d’informations que vous pouvez recevoir lorsque l’utilisateur effectue des opérations d’édition.