Überblick

Die folgende Tabelle listet die wichtigsten Modellklassen auf, mit denen ein Gantt-Diagramm mit Daten befüllt wird.

Klasse Beschreibung
Activity Aktivitäten repräsentieren Objekte, die unterhalb der Timeline in der Grafikansicht des Gantt-Diagramm-Steuerelements angezeigt werden. Aktivitäten können auf einer Zeile einem bestimmten Layer hinzugefügt werden.
ActivityRef Eine Aktivitätsreferenz dient dazu, die Position einer Aktivität präzise zu identifizieren. Diese Position setzt sich aus der Zeile, dem Layer und der Aktivität selbst zusammen.
ActivityLink Ein Aktivitäts-Link kann verwendet werden, um eine Abhängigkeit zwischen zwei Aktivitäten auszudrücken.
ActivityRepository Activity-Repositories werden von Zeilen verwendet, um Aktivitäten zu speichern und nachzuschlagen.
Row Ein (Modell-)Zeilenobjekt speichert die Aktivitäten, die sich auf einer (visuellen) Zeile des Gantt-Diagramms befinden.
Layer Layer werden verwendet, um Aktivitäten zu gruppieren.
LinesManager Ein Lines-Manager steuert das Layout der (inneren) Linien innerhalb einer Zeile.
Layout Jede Zeile und jede innere Linie einer Zeile ist einem Layout zugeordnet. Das Layout beeinflusst verschiedene Aspekte beim Rendern und Bearbeiten von Aktivitäten. Darüber hinaus nutzen auch mehrere System-Layer, die zum Zeichnen des Zeilenhintergrunds verwendet werden, die Layout-Informationen.
Calendar Ein Kalender erweitert ein Activity-Repository um einen Namen und eine Sichtbarkeitseigenschaft.

Activity

Aktivitäten repräsentieren Objekte, die unterhalb der Timeline in der Grafikansicht des Gantt-Diagramm-Steuerelements angezeigt werden. Aktivitäten können auf einer Zeile einem bestimmten Layer durch Aufruf von Row.addActivity(Layer, Activity) hinzugefügt werden.

Aktivitätstypen

Dies sind die verschiedenen Aktivitätstypen, die einer Zeile hinzugefügt werden können.

  • Activity - Basisimplementierung: ActivityBaseBase - die einfachste Form einer Aktivität.

    • id (String)
    • name (String)
    • startTime (Instant)
    • endTime (Instant)
  • ChartActivity - Basisimplementierung: ChartActivityBaseBase - Aktivitäten dieses Typs können in einem Chart-Layout angezeigt werden.

  • id (String)
  • name (String)
  • startTime (Instant)
  • endTime (Instant)
  • chartValue (double)

  • CompletableActivity - Basisimplementierung: CompletableActivityBase - Aktivitäten dieses Typs führen einen Prozentwert (Fertigstellung) mit.

  • id (String)
  • name (String)
  • startTime (Instant)
  • endTime (Instant)
  • percentageComplete (double)

  • HighLowChartActivity - Basisimplementierung: HighLowChartActivityBase - Aktivitäten dieses Typs können in einem Chart-Layout angezeigt werden.

  • id (String)
  • name (String)
  • startTime (Instant)
  • endTime (Instant)
  • high (double)
  • low (double)

  • MutableActivity - Basisimplementierung: MutableActivityBase - die einfachste Form einer veränderbaren Aktivität. Der Benutzer kann Start- und Endzeit dieser Aktivitäten ändern.

  • id (String)
  • name (String)
  • startTime (Instant)
  • endTime (Instant)

  • MutableChartActivity - Basisimplementierung: MutableChartActivityBase - diese Aktivitäten können in einem Chart-Layout angezeigt werden. Der Benutzer kann Startzeit, Endzeit und Chart-Wert dieser Aktivitäten ändern.

  • id (String)
  • name (String)
  • startTime (Instant)
  • endTime (Instant)
  • chartValue (double)

  • MutableCompletableActivity - Basisimplementierung: MutableCompletableActivityBase - diese Aktivitäten führen einen Prozentwert (Fertigstellung) mit. Der Benutzer kann Startzeit, Endzeit und den Wert für percentageComplete dieser Aktivitäten ändern.

  • id (String)
  • name (String)
  • startTime (Instant)
  • endTime (Instant)
  • percentageComplete (double)

  • MutableHighLowChartActivity - Basisimplementierung: MutableHighLowChartActivityBase - diese Aktivitäten können in einem Chart-Layout angezeigt werden. Der Benutzer kann Startzeit, Endzeit sowie den high- und low-Wert dieser Aktivitäten ändern.

  • id (String)
  • name (String)
  • startTime (Instant)
  • endTime (Instant)
  • high (double)
  • low (double)

Chart-Aktivität

Eine Chart-Aktivität basiert auf einer zusätzlichen Schnittstelle für Aktivitäten. Sie muss von Aktivitäten implementiert werden, die in einem ChartLayout dargestellt werden sollen. Diese Schnittstelle ergänzt die Aktivität um einen Chart-Wert. Die folgende Abbildung zeigt ein Beispiel für ein Chart-Layout, das pro Tag eine Chart-Aktivität anordnet.

Capacity-Layout

Completable-Aktivität

Eine Completable-Aktivität ist eine Aktivität, die einen „percentageComplete“-Wert zwischen 0 und 100 % mitführt. Completable-Aktivitäten werden mit einem speziellen Renderer für Aktivitätsbalken gezeichnet. Dieser Renderer zeichnet den Hintergrund der Aktivität auf Basis des percentageComplete-Werts. Die folgende Abbildung zeigt ein Beispiel.

Completable

High-Low-Chart-Aktivität

Eine High-Low-Chart-Aktivität besitzt zwei zusätzliche Attribute: high und low. Diese Werte werden vom ChartLayout verwendet, um die Aktivität passend zu positionieren. Ein typisches Einsatzbeispiel für High-Low-Aktivitäten sind Candlestick-Charts, etwa für Open-/High-/Low-/Close-Kurse von Aktien.

Aktivitätsreferenz

Eine Aktivitätsreferenz dient dazu, die „Position“ einer Aktivität präzise zu identifizieren. Eine Position ist die Kombination aus Zeile, Layer und der Aktivität selbst. Da sich dieselbe Aktivität gleichzeitig auf mehreren Zeilen und/oder mehreren Layern befinden kann, ist es häufig notwendig, mit einer Aktivitätsreferenz statt nur mit der Aktivität zu arbeiten. Eine Aktivitätsreferenz kann man sich als „Pfad“ zur Aktivität vorstellen.

Ein Aktivitäts-Link kann verwendet werden, um jede Art von Abhängigkeit zwischen zwei Aktivitäten zu modellieren. In Projektplanungsanwendungen würde ein Link eine Vorgänger-/Nachfolger-Beziehung zwischen zwei Aufgaben ausdrücken. Zum Beispiel: „Aufgabe A muss abgeschlossen sein, bevor Aufgabe B beginnen kann“. In anderen Domänen kann ein Link einfach ausdrücken, dass zwei oder mehr Aktivitäten gemeinsam geplant werden müssen und dass beim Verschieben einer Aktivität auch alle anderen verschoben werden müssen. Die folgende Abbildung zeigt ein Beispiel für einen solchen Link.

Ein Link kann dem Gantt-Diagramm hinzugefügt werden, indem GanttChart.getLinks().add(myLink);

Link

Activity-Repository

Activity-Repositories werden von Zeilen verwendet, um Aktivitäten zu speichern und nachzuschlagen. Jede Zeile besitzt standardmäßig ein IntervalTreeActivityRepository. Dieses Standard-Repository kann durch eine eigene Implementierung ersetzt werden, zum Beispiel wenn Ihre Anwendung eine Lazy-Loading-Strategie benötigt.

Abfragen

Die wichtigste Funktionalität jedes Repositorys ist die Möglichkeit, Aktivitäten innerhalb eines gegebenen Zeitintervalls abzufragen. Zu diesem Zweck definiert die Schnittstelle ActivityRepository die Methode getActivities() mit diesen Parametern:

Typ Name Beschreibung
Layer layer Immer wenn der Benutzer nach links oder rechts scrollt, fragt die Zeile das Repository mehrfach ab – einmal pro Layer.
Instant startTime Die Startzeit des Zeitintervalls, für das die Zeile Aktivitäten abfragt.
Instant endTime Die Endzeit des Zeitintervalls, für das die Zeile Aktivitäten abfragt.
TemporalUnit unit Der aktuelle Wert der primären Zeiteinheit, die derzeit von der Dateline angezeigt wird. Dies ist die Einheit, die am unteren Rand der Dateline dargestellt wird, z. B. Tage. Mit diesem Parameter lässt sich steuern, wie fein granular das Ergebnis ist. Wenn bekannt ist, dass der Benutzer aktuell Monate betrachtet, kann es sinnvoll sein, tägliche Aktivitäten zu aggregieren.
ZomeId zoneId Die Zeitzone, die von der Zeile angezeigt wird.

Frühester / spätester verwendeter Zeitpunkt

Jede Repository-Implementierung muss den frühesten und spätesten verwendeten Zeitpunkt liefern können (früheste Startzeit / späteste Endzeit aller im Repository gespeicherten Aktivitäten). Dadurch kann die UI Steuerelemente für eine einfache Navigation bereitstellen: „frühesten anzeigen“, „spätesten anzeigen“. Zu diesem Zweck müssen Repositories die getEarliestTimeUsed() und getLatestTimeUsed() Methoden implementieren.

Aktivitäten aktualisieren

Aktivitäten müssen aus ihrem Repository entfernt werden (ActivityRef.detachFromRow()), bevor sie geändert werden, und nach der Änderung wieder hinzugefügt werden (ActivityRef.attachToRow()). Nur so lässt sich sicherstellen, dass die zugrunde liegende Datenstruktur eines Repositorys stets mit den Aktivitäten synchron bleibt. Beispiel: Die Interval-Tree-Datenstruktur funktioniert nur korrekt, wenn sich alle Knoten an ihrer richtigen Position befinden. Dies kann nur garantiert werden, wenn die Knoten vor der Änderung aus dem Baum entfernt werden (andernfalls findet der Baum sie nicht) und anschließend mit ihrem neuen Wert wieder eingefügt werden.

Ereignisbehandlung

Activity-Repositories unterstützen Listener, sodass sich die UI aktualisieren kann, wenn sich der Inhalt des Repositorys ändert. Interessierte Komponenten können Handler registrieren, indem sie addEventHandler() aufrufen, oder Handler entfernen, indem sie removeEventHandler() aufrufen. Die Ereignisklasse heißt RepositoryEvent und verfügt über drei Ereignistypen:

  • ACTIVITY_ADDED - Eine Aktivität wurde dem Repository hinzugefügt.
  • ACTIVITY_REMOVED - Eine Aktivität wurde aus dem Repository entfernt.
  • REPOSITORY_CHANGED - Der Zustand des Repositorys hat sich geändert.

Jeder dieser Ereignistypen löst normalerweise ein Neuzeichnen der Zeile aus, zu der das Repository gehört.

IntervalTreeActivityRepository

Das InteralTreeActivityRepository ist ein Activity-Repository, das eine oder mehrere binäre Interval-Tree-Datenstrukturen zum Speichern von Aktivitäten verwendet.

Binärer Interval Tree

ListActivityRepository

Das ListActivityRepository ist ein Activity-Repository, das eine oder mehrere Listendatenstrukturen zum Speichern von Aktivitäten verwendet. Dieses Repository kann so konfiguriert werden, dass seine Abfragemethode unterschiedliche Typen von Ergebnis-Iteratoren zurückgibt. Die möglichen Werte sind definiert in ListActivityRepository.IteratorType.

  • BINARY_ITERATOR - gibt einen Iterator zurück, der eine binäre Suche ausführt, um die erste für ein gegebenes Zeitintervall zu zeichnende Aktivität zu finden. Anschließend iteriert er über alle folgenden Aktivitäten, bis er eine Aktivität findet, die nach dem gegebenen Zeitintervall beginnt.
  • LINEAR_ITERATOR - gibt einen Iterator zurück, der eine lineare Suche ausführt, um die erste für ein gegebenes Zeitintervall zu zeichnende Aktivität zu finden. Anschließend iteriert er über alle folgenden Aktivitäten, bis er eine Aktivität findet, die nach dem gegebenen Zeitintervall beginnt.
  • SIMPLE_ITERATOR - gibt einen Iterator zurück, der keinerlei Suche ausführt, sondern sofort Aktivitäten zurückliefert – unabhängig davon, ob sie sich aktuell im sichtbaren Zeitintervall des Gantt-Diagramms befinden oder nicht.

Das SIMPLE_ITERATOR wird für Zeilen mit nur wenigen Aktivitäten verwendet. Dieser Iterator ist sehr nützlich, wenn sichergestellt werden soll, dass der nachlaufende Text einer Aktivität weiterhin angezeigt wird, selbst wenn die Aktivität bereits aus dem sichtbaren Bereich herausgescrollt wurde.

Simple-Iterator-Anwendungsfall

Row

Ein Zeilenobjekt speichert die Aktivitäten, die sich auf einer Zeile des Gantt-Diagramms befinden. Diese Aktivitäten werden nicht direkt in der Zeile gespeichert, sondern in einem Activity-Repository. Das Standard-Repository ist vom Typ IntervalTreeActivityRepository. Aktivitäten können auf Linien innerhalb der Zeile platziert werden. Die Zeile delegiert diese Arbeit an einen LinesManager. Der Standard-Manager ist vom Typ EqualLinesManager.

Typargumente & Hierarchie

Drei Typargumente sind erforderlich, um eine Zeile zu definieren. Das erste definiert den Typ der übergeordneten Zeile, das zweite definiert den Typ der untergeordneten Zeilen, das dritte definiert den Typ der Aktivitäten, die auf der Zeile angezeigt werden. Das folgende Beispiel definiert ein „Building“. Das Building ist Teil einer Factory. Das Building enthält Machines. In der Zeile, die das Building repräsentiert, werden Shifts angezeigt.

public class Building extends Row<Factory, Machine, Shift> {
}

Ein solches Modell würde es ermöglichen, im Gantt-Diagramm eine Hierarchie anzuzeigen, die beispielsweise so aussieht:

  • Factory
  • Building 1
  • Building 2
  • Building 3
    • Machine A
    • Machine B
    • Machine B
  • Building 4
    • Machine C
    • Machine D

Eigenschaften

Jede Zeile besitzt eine Reihe von beobachtbaren Eigenschaften.

Eigenschaft Typ Beschreibung
expanded Boolean Steuert, ob die Zeile ihre untergeordneten Zeilen anzeigt oder nicht.
height double Die aktuelle Höhe der Zeile.
minHeight double Die minimale Höhe der Zeile.
maxHeight double Die maximale Höhe der Zeile.
layout Layout Das von der Zeile verwendete Layout. Der Standardwert ist GanttLayout.
lineCount int Die Anzahl der inneren Linien, die innerhalb der Zeile angezeigt werden.
linesManager Lines-Manager Der Lines-Manager, der die Linien, ihre Position, ihre Höhe und die Platzierung der Aktivitäten steuert.
name String Der Name der Zeile, z. B. „Machine 1“, „Building 1“.
parent Row Die übergeordnete Zeile. Dies ist eine schreibgeschützte Eigenschaft, die intern verwaltet und aktualisiert wird, wenn eine Zeile zur Liste der untergeordneten Zeilen einer anderen Zeile hinzugefügt wird.
repository Activity-Repository Das von der Zeile verwendete Repository zum Speichern der Aktivitäten.
showing boolean Ein Flag, das signalisiert, dass die Zeile aktuell in der UI angezeigt wird. Diese Information kann zur Optimierung von Lazy-Loading-Strategien verwendet werden.
userObject Object Ein optionales Benutzerobjekt. Es dient als Brücke zum Geschäftsmodell.
zoneId ZoneId Die von der Zeile repräsentierte Zeitzone. Jede Zeile kann ihre eigene Zeitzone haben.

Layer

Layer dienen dazu, Aktivitäten zu gruppieren. Aktivitäten auf demselben Layer werden gleichzeitig gezeichnet (Z-Order). Ein Layer hat einen Namen und eine ID, kann ein- und ausgeschaltet werden, und seine Deckkraft kann geändert werden. Diese Änderungen wirken sich auf alle Aktivitäten auf diesem Layer aus. Die ID des Layers wird für Drag-&-Drop-Operationen von Aktivitäten zwischen verschiedenen Gantt-Diagrammen verwendet. Abgelegte Aktivitäten werden dem Layer mit derselben ID hinzugefügt. Der Layer-Name wird als Standard-ID für neu erstellte Layer verwendet. Die ID muss nur geändert werden, wenn derselbe Layer-Typ mit unterschiedlichen Namen in verschiedenen Gantt-Diagrammen verwendet wird.

Lines-Manager

Dieser Manager steuert das Layout von Linien innerhalb einer Zeile. Aktivitäten, die sich auf unterschiedlichen Linien befinden, überlappen sich nicht – es sei denn, die Linien selbst überlappen sich. Jede Linie kann eine eigene Höhe und Position innerhalb der Zeile haben. Außerdem kann jede Linie ihr eigenes Layout besitzen. Durch die Verwendung von Linien und Layouts ist es möglich, Aktivitäten, die zur selben Zeile gehören, auf unterschiedliche Weise darzustellen (siehe ChartLayout, AgendaLayout, GanttLayout).

Zeilenanzahl

Die tatsächliche Anzahl der Linien einer Zeile wird in der Eigenschaft lineCount der Zeile gespeichert. Rufen Sie einfach Row.setLineCount(int) auf, um den Wert zu ändern. Ist die Linienanzahl größer als null, fragt die Zeile ihren Lines-Manager, wo sich jede Linie befindet, wie hoch sie ist und welche Aktivität auf welcher Linie platziert wird. Außerdem wird der für jede Linie zu verwendende Layout-Typ vom Manager abgerufen.

Schnittstelle

Die folgende Tabelle listet die Schnittstellenmethoden eines LineManager.

Methode Beschreibung
double getLineHeight(int lineIndex, double rowHeight); Gibt die Höhe der Linie mit dem angegebenen Index zurück. Die Höhe kann zur Laufzeit auf Basis der angegebenen verfügbaren Zeilenhöhe berechnet werden.
int getLineIndex(A activity); Gibt den Linienindex für die angegebene Aktivität zurück. Diese Methode platziert Aktivitäten auf unterschiedlichen Linien.
Layout getLineLayout(int lineIndex); Gibt das Layout für die Linie mit dem angegebenen Linienindex zurück. Jede Linie kann ihr eigenes Layout haben.
double getLineLocation(int lineIndex, double rowHeight); Gibt die Position der Linie mit dem angegebenen Index zurück. Die Position kann zur Laufzeit auf Basis der angegebenen verfügbaren Zeilenhöhe berechnet werden.

Equal-Lines-Manager

Das EqualLinesManager kann verwendet werden, um Linienpositionen und Linienhöhen gleichmäßig auf einer Zeile zu verteilen. Jede Linie hat dieselbe Höhe, und die Linien überlappen sich nicht. Der Manager stellt dieses Verhalten bereit; es bleibt jedoch Aufgabe der Anwendung, die Aktivitäten auf unterschiedlichen Linien zu platzieren und das Layout für jede Linie festzulegen. Aus diesem Grund sind auch die Methoden getLineHeight() und getLineLocation() final, während die Methoden getLineLayout() und getLineIndex() nicht final sind und überschrieben werden können.

Auto-Lines-Manager

Das AutoLinesManager kann verwendet werden, um eine dynamische Anzahl von Linien basierend auf allen Aktivitäten in einem Repository zu erstellen. Dieser Lines-Manager erkennt Cluster sich überschneidender Aktivitäten (Start-/Endzeitintervalle) und stellt sicher, dass genügend Linien verfügbar sind, um die Aktivitäten ohne Überlappung zu platzieren. Nachfolgend finden Sie den vollständigen Quellcode dieser Manager-Klasse als Fallstudie. Beachten Sie, dass die Methode layout() des Managers von außen aufgerufen werden muss. Eine gute Möglichkeit dafür ist, auf ACTIVITY_CHANGE_FINISHED-Ereignisse oder auf noch feiner granulare START/END_TIME_CHANGE_FINISHED-Ereignisse zu hören.

/**
 * Copyright (C) 2026 Dirk Lemmermann Software & Consulting (dlsc.com)
 *
 * This file is part of FlexGanttFX.
 */
package com.flexganttfx.view.util;
import static java.util.Objects.requireNonNull;
import impl.com.flexganttfx.skin.util.Placement;
import impl.com.flexganttfx.skin.util.Resolver;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.flexganttfx.model.Activity;
import com.flexganttfx.model.ActivityRepository;
import com.flexganttfx.model.Layer;
import com.flexganttfx.model.LinesManager;
import com.flexganttfx.model.Row;
import com.flexganttfx.model.layout.EqualLinesManager;
import com.flexganttfx.view.graphics.GraphicsBase;

/**
 * A specialized {@link LinesManager} used for ensuring that activities will not
 * overlap each other. This manager will create as many inner lines as needed
 * and will calculate the placement of all activities on these lines.
 *
 * @param <R>
 *            the type of the row that will be managed
 * @param <A>
 *            the type of the activities that will be managed
 *
 * @since 1.2
 */
public class AutoLinesManager<R extends Row<?, ?, A>, A extends Activity>
        extends EqualLinesManager<R, A> {

    private Map<A, Placement<A>> placements;
    private GraphicsBase<R> graphics;

    /**
     * Constructs a new automatic lines manager. The constructor requires a
     * reference to the graphics view to look up various parameters that are
     * needed when the manager queries the activity repository of the row (e.g.
     * the currently displayed temporal unit and the list of layers).
     *
     * @param row
     *            the managed row
     * @param graphics
     *            the graphics view where the manager will be used
     *
     * @since 1.2
     */
    public AutoLinesManager(R row, GraphicsBase<R> graphics) {
        super(row);
        this.graphics = requireNonNull(graphics);
        layout();
    }

    /**
     * Returns the graphics view where the manager will be used.
     *
     * @return the graphics view
     * @since 1.2
     */
    public final GraphicsBase<R> getGraphics() {
        return graphics;
    }

    public final void layout() {
        R row = getRow();
        ActivityRepository<A> repository = row.getRepository();
        Instant st = repository.getEarliestTimeUsed();
        Instant et = repository.getLatestTimeUsed();
        if (st == null || et == null) {
            return;
        }
        List<A> allActivities = new ArrayList<>();
        for (Layer layer : graphics.getLayers()) {
            Iterator<A> activities = repository.getActivities(layer, st, et,
                    graphics.getTimeline().getDateline()
                            .getPrimaryTemporalUnit(), row.getZoneId());
            if (activities != null) {
                activities.forEachRemaining(activity -> allActivities
                        .add(activity));
            }
        }
        placements = Resolver.resolve(allActivities);
        if (placements != null && !placements.isEmpty()) {
            Placement<A> p = placements.values().iterator().next();
            row.setLineCount(p.getColumnCount());
        } else {
            row.setLineCount(0);
        }
    }

    @Override
    public int getLineIndex(A activity) {
        if (placements != null) {
            Placement<A> placement = placements.get(activity);
            if (placement != null) {
                return placement.getColumnIndex();
            }
        }
        return -1;
    }
}

Layout-Typen

Jede Zeile und jede innere Linie einer Zeile ist einem Layout zugeordnet. Das Layout beeinflusst verschiedene Aspekte beim Rendern und Bearbeiten von Aktivitäten. Darüber hinaus nutzen auch mehrere System-Layer, die den Zeilenhintergrund zeichnen, die Layout-Informationen. Das Layout kann durch Aufruf von Row.setLayout(Layout) gesetzt werden; bei Verwendung innerer Linien kann es über den Lines-Manager der Zeile zurückgegeben werden.

FlexGanttFX enthält drei Layout-Typen.

Layout Beschreibung
GanttLayout Ordnet Aktivitäten horizontal entlang der Timeline an. Die Positionen basieren auf den Start- und Endzeiten der Aktivitäten.
AgendaLayout Ordnet Aktivitäten vertikal entlang einer Skala für „lokale Zeit“ (0–24 Stunden) an. Dadurch sehen Aktivitäten wie Kalendereinträge aus.
ChartLayout Ordnet Aktivitäten als Chart-Werte an. Aktivitäten können die Schnittstelle ChartActivity oder HighLowChartActivity implementieren.

Padding

Alle Layout-Typen besitzen eine Padding-Eigenschaft. Sie wird verwendet, um am oberen und unteren Rand jeder Zeile beziehungsweise Linie einen visuellen Abstand zu erzeugen.

Padding

Gantt-Layout

Gantt-Layout

Agenda-Layout

Die Agenda-Layout-Klasse wird verwendet, um Aktivitäten ähnlich wie in einem regulären Kalender anzuordnen, bei dem eine vertikale Skala Stunden anzeigt. Aktivitäten repräsentieren Termine für einen bestimmten Tag. Aktivitäten, die in einem Agenda-Layout angezeigt werden, können mehrfach gerendert werden. Dies ist beispielsweise der Fall, wenn sich eine Aktivität über mehrere Tage erstreckt.

Agenda-Layout

Mit der Agenda-Layout-Klasse können Sie eine Start- und Endzeit festlegen. Damit wird das Zeitintervall eingeschränkt, das angezeigt wird und in dem die Agenda-Aktivitäten angeordnet werden. In den meisten Fällen ist es nicht sinnvoll, die gesamten 24 Stunden anzuzeigen, sondern nur die Arbeitszeit, z. B. von 8:00 bis 18:00 Uhr. Rufen Sie einfach AgendaLayout.setStartTime() oder setEndTime() auf, um den Zeitraum zu ändern.

Konfliktstrategie

Aktivitäten in einem Agenda-Layout können sich überschneiden. Über die Eigenschaft conflictStrategy legen Sie fest, wie mit solchen Situationen umgegangen wird. Die folgende Liste zeigt die möglichen Werte.

Strategie Beschreibung
OVERLAPPING Kollidierende Agenda-Einträge werden übereinander gezeichnet, wobei einer von ihnen um einige Pixel eingerückt wird. Die Einrückung kann über die Eigenschaft overlapOffset von AgendaLayout gesteuert werden.
PARALLEL Kollidierende Agenda-Einträge werden in unterschiedlichen Spalten innerhalb desselben Tages angezeigt.

Chart-Layout

Bei Verwendung der Klasse ChartLayout werden Aktivitäten als Chart-Balken angeordnet. Eine Reihe solcher Balken kann beispielsweise verwendet werden, um ein Kapazitätsprofil zu bilden. Aktivitäten vom Typ ChartActivity werden auf einer Nulllinie zwischen dem minimalen und dem maximalen Wert des Layouts platziert. Die Höhe der Chart-Aktivität basiert auf dem von ChartActivity.getChartValue() zurückgegebenen Wert. Aktivitäten vom Typ HighLowChartActivity erscheinen als schwebende Balken. Das Layout unterstützt außerdem die Definition von Neben- und Hauptlinien, die im Zeilenhintergrund gezeichnet werden.

Chart-Layout

Min.- & Max.-Wert – das Chart-Layout stellt zwei Eigenschaften bereit, die das tatsächliche Layout der Chart-Aktivitäten steuern: minValue und maxValue. Diese Werte müssen von der Anwendung verwaltet werden, nicht vom Framework. Sie können durch Aufruf von ChartLayout.setMinValue() oder ChartLayout.setMaxValue() gesetzt werden.

Haupt- & Neben-Ticks – jede Chart-Layout-Instanz besitzt eine Liste von Haupt- und Neben-Ticks. Diesen Listen können Werte hinzugefügt werden, um Wertlinien im Hintergrund der Zeile zu rendern. Beispiel: Der minValue ist 0 und der maxValue ist 100. Dann ist es sinnvoll, Haupt-Ticks für die Werte 50 und 100 zu definieren. Neben-Ticks könnten bei 10, 20, 30, 40, 60, 70, 80 und 90 liegen.

Calendar

Ein Kalender erweitert ein Activity-Repository um einen Namen und eine Sichtbarkeitseigenschaft. Kalender können dem gesamten Gantt-Diagramm oder einzelnen Zeilen innerhalb des Gantt-Diagramms hinzugefügt werden. Auch die Dateline verwendet sie.

Dateline.getCalendars();
GraphicsBase.getCalendars();
Row.getCalendars();

Kalender werden für den Hintergrund von Zeilen verwendet. Sie können beispielsweise Wochenenden oder Feiertage markieren. Kalenderinformationen werden immer schreibgeschützt angezeigt. Aktivitäten, die von Kalendern zurückgegeben werden, müssen vom Typ CalendarActivity sein. Sie können vom Benutzer nicht interaktiv bearbeitet werden.

Wochenendkalender

In FlexGanttFX ist bereits ein vordefinierter Kalendertyp enthalten. Er heißt WeekendCalendar und wird verwendet, um Wochenendtage zu markieren (z. B. Samstag und Sonntag).

Wochenende

Das folgende Listing zeigt den vollständigen Code dieser Kalenderklasse. Es kann als Grundlage für Ihre eigenen Kalender verwendet werden.

/**
 * Copyright (C) 2014 Dirk Lemmermann Software & Consulting (dlsc.com)
 * This file is part of FlexGanttFX.
 */
package com.flexganttfx.model.calendar;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.util.Objects.requireNonNull;
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;

import javafx.event.Event;

import com.flexganttfx.model.Layer;
import com.flexganttfx.model.repository.RepositoryEvent;
/**
 * A calendar specialized on returning activities that represent weekend days
 * (default: Saturday, Sunday). The days that are considered weekend days can be
 * configured by calling {@link #setWeekendDays(DayOfWeek...)}.
 *
 * @since 1.0
 */
public class WeekendCalendar extends CalendarBase<WeekendCalendarActivity> {

    private Instant lastStartTime = Instant.MIN;
    private Instant lastEndTime = Instant.MAX;
    private ZoneId lastZoneId;
    private List<WeekendCalendarActivity> entries;
    private EnumSet<DayOfWeek> weekendDays = EnumSet.of(DayOfWeek.SATURDAY,
            DayOfWeek.SUNDAY);
    /**
     * Constructs a new weekend calendar.
     *
     * @since 1.0
     */
    public WeekendCalendar() {
        super("Weekends");
    }

    /**
     * Sets the days of the week that are considered to be a weekend day. By
     * default {@link DayOfWeek#SATURDAY} and {@link DayOfWeek#SUNDAY} are
     * considered weekend days.
     *
     * @param days
     *            the days of the week that are to be considered weekend days
     * @since 1.0
     */
    public void setWeekendDays(DayOfWeek... days) {
        requireNonNull(days);
        weekendDays.clear();
        weekendDays.addAll(Arrays.asList(days));
        Event.fireEvent(this, new RepositoryEvent(this));
    }

    /**
     * Returns the days of the week that are to be considered weekend days. By
     * default {@link DayOfWeek#SATURDAY} and {@link DayOfWeek#SUNDAY} are
     * considered weekend days.
     *
     * @return the days of the week used as weekend days
     * @since 1.0
     */
    public DayOfWeek[] getWeekendDays() {
        return weekendDays.toArray(new DayOfWeek[weekendDays.size()]);
    }

    @Override
    public Iterator<WeekendCalendarActivity> getActivities(Layer layer,
            Instant startTime, Instant endTime, TemporalUnit temporalUnit,
            ZoneId zoneId) {

        if (!(temporalUnit instanceof ChronoUnit)) {
            /*
             * We only work for ChronoUnit.
             */
            return Collections.emptyListIterator();
        }
        if (startTime.equals(lastStartTime) && endTime.equals(lastEndTime)
                && zoneId.equals(lastZoneId)) {
            /*
             * We already answered this query for the given time interval. Let's
             * return the result from last time.
             */
            if (entries != null) {
                return entries.iterator();
            }
        } else {
            ChronoUnit unit = (ChronoUnit) temporalUnit;
            /*
             * The time interval has changed. Find the weekends within the new
             * interval, but only if the user is currently looking at days or
             * weeks.
             */
            if (isSupportedUnit(unit)) {
                /* Lazily create list structure. */
                if (entries == null) {
                    entries = new ArrayList<WeekendCalendarActivity>();
                } else {
                    entries.clear();
                }
                ZonedDateTime st = ZonedDateTime.ofInstant(startTime, zoneId);
                ZonedDateTime et = ZonedDateTime.ofInstant(endTime, zoneId);
                findWeekends(st, et);
                lastStartTime = startTime;
                lastEndTime = endTime;
                lastZoneId = zoneId;
                return entries.iterator();
            }
        }

        return Collections.emptyListIterator();
    }

    /**
     * Determines if weekends will be shown for the given temporal unit.
     * By default, we only show weekends for {@link ChronoUnit#DAYS} and
     * {@link ChronoUnit#WEEKS}. To support more units, override
     * this method in a subclass.
     *
     * @param unit
     *            the unit to check
     * @return true if weekend information will be shown in the Gantt chart
     * @since 1.0
     */
    protected boolean isSupportedUnit(TemporalUnit unit) {
        if (unit instanceof ChronoUnit) {
            ChronoUnit chronoUnit = (ChronoUnit) unit;
            switch (chronoUnit) {
            case DAYS:
            case WEEKS:
                return true;
            default:
                return false;
            }
        }
        return false;
    }

    private void findWeekends(ZonedDateTime st, ZonedDateTime et) {
        while (st.isBefore(et) || st.equals(et)) {
            if (weekendDays.contains(st.getDayOfWeek())) {
                st = st.truncatedTo(DAYS);
                entries.add(new WeekendCalendarActivity(st.getDayOfWeek()
                        .toString(), Instant.from(st), Instant.from(st
                        .plusDays(1)), st.getDayOfWeek()));
            }
            st = st.plusDays(1);
        }
    }
}