Übersicht
Das Timeline-Steuerelement ist ein Container für die Dateline und die Eventline. Es wird oberhalb des Grafik-Steuerelements angezeigt und stellt mehrere Methoden zum Scrollen und Zoomen bereit, die beide mit oder ohne Animation ausgeführt werden können. Die Timeline verfolgt außerdem die aktuelle Zeit (siehe TimeTracker).

Navigation
Die Timeline dient zur Navigation durch die Zeit. Sie bietet Methoden, um zur aktuellen Zeit oder einer bestimmten Zeit zu springen. Es kann angefordert werden, eine bestimmte Zeiteinheit anzuzeigen („Tage anzeigen") oder einen Zeitbereich.
showNow(), showNow(boolean center)– ändert die Startzeit des Timeline-Modells so, dass die aktuelle Zeit (wie im TimelineModel gespeichert) entweder am linken Rand der Dateline oder genau in der Mitte angezeigt wird.showTime(Instant time), showTime(Instant time, boolean center)– ändert die Startzeit des Timeline-Modells so, dass die angegebene Zeit entweder am linken Rand der Dateline oder genau in der Mitte angezeigt wird.showRange(Instant start, Instant end), showRange(Instant start, Duration duration), showRange(TimeInterval range)– ändert die Startzeit und den „Millisekunden-pro-Pixel"-Wert des Timeline-Modells so, dass der angegebene Zeitbereich vollständig in der Dateline sichtbar wird.showTemporalUnit(TemporalUnit unit, double width)– ändert die Startzeit und den „Millisekunden-pro-Pixel"-Wert des Timeline-Modells so, dass die angegebene Zeiteinheit in der Dateline verwendet wird. Jede Zelle in der Dateline ist so breit wie die angegebene Breite.
Es ist zu beachten, dass die Timeline in Zusammenarbeit mit der Dateline diese Anforderungen nur nach bestem Bemühen erfüllen kann, da sie von der Verfügbarkeit von Dateline-Auflösungen im Dateline-Modell abhängen.
Die oben genannten Methoden können mit oder ohne Animation ausgeführt werden. Diese Animation wird über zwei Eigenschaften gesteuert: moveAnimated und moveDuration. Die entsprechenden Getter- und Setter-Methoden für diese Eigenschaften sind in der Timeline verfügbar.
Zoomen
Die Timeline ist für alles rund um das Zoomen zuständig. Der Benutzer kann die + / --Tasten drücken, um die Zoom-Stufe um einen bestimmten Zoom-Faktor zu erhöhen, oder durch Ziehen der Maus und gleichzeitiges Halten der SHIFT-Taste ein Zeitintervall per „Lasso" auswählen. Das Ergebnis ist ein ausgewähltes Zeitintervall, das in der schreibgeschützten Eigenschaft selectedTimeInterval gespeichert wird.
Die Timeline beobachtet Änderungen an dieser Eigenschaft und versucht automatisch, das ausgewählte Zeitintervall über die gesamte verfügbare Breite anzuzeigen, was letztendlich einen Zoom-In-Vorgang auslöst.
zoomIn(), zoomOut()– veranlasst die Timeline, das Timeline-Modell so zu ändern, dass der resultierende sichtbare Zeitbereich der aktuelle Zeitbereich multipliziert bzw. dividiert durch den aktuellen Zoom-Faktor ist.zoom(double factor, boolean zoomIn, Instant frozenTime)– führt einen Zoom-Vorgang mit dem angegebenen Zoom-Faktor durch (entweder hinein oder heraus). Die Timeline versucht, die angegebene „eingefrorene" Zeit an ihrer aktuellen Position zu halten. Dieses Verhalten ist sehr nützlich für einen Pinch-basierten Zoom, bei dem die Benutzeroberfläche „in" eine bestimmte Zeit hineinzoomt.setZoomLassoEnabled(boolean), boolean isZoomLassoEnabled();– steuert die Verfügbarkeit des Zoom-Lassos.
Genau wie die Bewegungsoperationen können auch Zoom-Operationen animiert oder nicht animiert ausgeführt werden. Dazu stehen die Eigenschaften zoomAnimated und zoomDuration zur Verfügung.
Eine weitere Eigenschaft dient der Feinabstimmung des Zoom-Verhaltens, da einige Anwendungen beim Zoomen entweder die Startzeit, die Endzeit oder die Mittelzeit beibehalten möchten. Dafür kann die Anwendung die Eigenschaft zoomMode setzen. Mögliche Werte dieses Enums sind KEEP_START_TIME, KEEP_END_TIME oder CENTER.
Scrollen
Die Timeline unterstützt das Scrollen nach links und rechts in zwei verschiedenen Geschwindigkeiten.
scrollLeft(),scrollLeftFast()– ändert die Startzeit-Eigenschaft des Timeline-Modells so, dass die Dateline mit einer früheren Zeit beginnt.scrollRight(),scrollRightFast()– ändert die Startzeit-Eigenschaft des Timeline-Modells so, dass die Dateline mit einer späteren Zeit beginnt.
Diese Methoden können vom Benutzer über die +- und --Tasten aufgerufen werden. Das Scrollen ist schnell, wenn der Benutzer gleichzeitig SHIFT drückt.
TimeTracker
Das Timeline-Steuerelement ist für die Zeitverfolgung zuständig. Das bedeutet, es aktualisiert die Eigenschaft now des zugrunde liegenden Timeline-Modells. Die Timeline implementiert Methoden zum Starten und Stoppen der Zeitverfolgung, die eigentliche Aktualisierung von now wird jedoch an eine TimeTracker-Klasse delegiert.
startTimeTracking(),stopTimeTracking()– startet und stoppt die Zeitverfolgung. Diese Methoden rufen die entsprechenden Methoden der TimeTracker-Klasse auf.timeTrackerProperty(),setTimeTracker(TimeTracker tracker),TimeTracker getTimeTracker()– die TimeTracker-Eigenschaft sowie ihre Getter- und Setter-Methoden. Der Standard-TimeTracker (verwendet die Systemzeit) kann durch einen benutzerdefinierten ersetzt werden.
Sichtbares Zeitintervall
Zwei schreibgeschützte Eigenschaften verfolgen die früheste und späteste von der Timeline angezeigte Zeit. Sie heißen
visibleStartTime und visibleEndTime. Die Methoden getVisibleStartTime(), getVisibleEndTime() und getVisibleDuration()
dienen zur Arbeit mit diesen Eigenschaften.
Timeline-Modell
Das Timeline-Steuerelement verwendet ein Modell vom Typ TimelineModel. Dieses Modell stellt die wichtigsten Parameter für die
Timeline und die Dateline bereit, damit diese korrekt funktionieren. Das Timeline-Modell kann für verschiedene temporale Einheiten typisiert werden.
FlexGanttFX wird mit einem ChronoUnitTimelineModel und einem SimpleUnitTimelineModel ausgeliefert.
Startzeit & Millisekunden pro Pixel
Die beiden wichtigsten Eigenschaften des TimelineModel sind startTime und millisPerPixel (MPP). Die Startzeit bestimmt die erste sichtbare Zeit im Gantt-Diagramm, während die aktuelle Breite der Timeline in Kombination mit dem MPP-Wert die letzte sichtbare Zeit und damit den sichtbaren Zeitbereich bestimmt. Ein Erhöhen des MPP-Werts bewirkt, dass die Timeline einen größeren Zeitbereich anzeigt, während eine Reduzierung einen kürzeren Zeitbereich ergibt. Die in der Klasse Timeline enthaltenen Methoden zum Anzeigen einer Zeit, Scrollen zu einer Zeit und Zoomen in einen Bereich spielen mit diesen beiden Variablen, um ihren Zweck zu erfüllen. Die folgende Tabelle listet die Methoden zu diesen Eigenschaften auf:
ObjectProperty<Instant> startTimeProperty();setStartTime(Instant time);Instant getStartTime();– speichert, setzt und gibt die aktuelle Startzeit zurück, die erste sichtbare Zeit im Gantt-Diagramm. Die frühest mögliche Startzeit kann über die EigenschafthorizonStartTimeeingeschränkt werden.DoubleProperty millisPerPixel();setMillisPerPixel(double mpp);double getMillisPerPixel();– speichert, setzt und gibt den Millisekunden-pro-Pixel-Wert (MPP) zurück. Der Standardwert von MPP ist 24 * 60 * 60 * 1000 / 30, was bedeutet, dass Tage eine Breite von 30 Pixeln haben.
Now-Zeit / Now-Position
Gantt-Diagramme erfordern häufig die Markierung der „aktuellen" Zeit. Diese kann entweder die Systemzeit
(java.time.Instant.now()) oder ein von der Anwendung gesteuerter beliebiger Wert sein. Letzteres ist häufig bei Software der Fall,
die eine Simulation ausführt und das Gantt-Diagramm zur Verfolgung der Simulationszeit verwendet. Zur Unterstützung dieser Anwendungsfälle
definiert das Timeline-Modell eine Eigenschaft namens now.
Der Wert von now wird normalerweise von einem TimeTracker aktualisiert, der über die Timeline gesteuert werden kann.
ObjectProperty<Instant> nowProperty();void setNow(Instant now);Instant getNow();– speichert, setzt und gibt die aktuelle Zeit zurück.ReadOnlyDoubleProperty nowLocation();double getNowLocation();– speichert und gibt die Position der aktuellen Zeit zurück. Die Now-Position wird vom Modell basierend auf der Startzeit und dem MPP-Wert berechnet. Diese Eigenschaft ist schreibgeschützt, da die Now-Position immer vom Wert der Now-Zeit abhängt. Die Position kann nur durch Ändern vonnowselbst geändert werden.
Zeit- & Koordinatenberechnungen
Der primäre Zweck des Timeline-Modells ist die Umrechnung von Zeit in eine Position und umgekehrt. Dafür bietet das Modell mehrere Methoden:
double calculateLocationForTime(Instant);– gibt die x-Koordinate für die angegebene Zeit zurück.Instant calculateTimeForLocation(double);– gibt die Zeit für die angegebene x-Koordinate zurück.
Der Horizont
Planungsanwendungen arbeiten oft mit einem Horizont, der durch eine früheste und späteste Zeit definiert ist. Diese Zeiten können auf dem geladenen Datensatz (Min-/Max-Berechnung der Start- und Endzeiten der Aktivitäten) oder dem Planungshorizont
(Q1, Q2, Q3, Q4) basieren. Das Setzen der Werte von horizonStartTime und horizonEndTime stellt sicher, dass der Benutzer nicht zu einer Zeit außerhalb des Horizonts scrollen kann.
Höchste & niedrigste temporale Einheit
Nicht alle Anwendungen benötigen alle verfügbaren Einheiten einer temporalen Einheit. java.time.temporal.ChronoUnit definiert beispielsweise Einheiten von Nanosekunden bis Jahrtausenden. Die Eigenschaften highestTemporalUnit und lowestTemporalUnit ermöglichen es der Anwendung, den Einheitenbereich auf etwas Sinnvolleres einzuschränken, z. B. Stunden bis Monate.
Time-Tracker
Ein Time-Tracker kann verwendet werden, um die Eigenschaft now des TimelineModel zu aktualisieren. In den meisten Fällen entspricht die Zeit „now" der Systemzeit, bei Simulationssoftware kann dies jedoch anders sein. Um den Time-Tracker zu verwenden, erstellen Sie einfach eine Instanz von TimeTracker und binden Sie deren timeProperty() an die nowProperty() des Timeline-Modells.
Beispiel
Im Folgenden finden Sie den vollständigen Code der Standard-TimeTracker-Klasse.
/**
* Copyright (C) 2014 - 2016 Dirk Lemmermann Software & Consulting (dlsc.com)
*
* This file is part of FlexGanttFX.
*/
package com.flexganttfx.view.timeline;
import java.time.Instant;
import java.util.logging.Level;
import com.flexganttfx.core.LoggingDomain;
import com.flexganttfx.model.timeline.TimelineModel;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
/**
* A time tracker can be used to update the property
* {@link TimelineModel#nowProperty()}. In most cases the time "now" will be
* equivalent to the system time but in simulations this might not be the case.
* The time tracker can be used in combination with the {@link TimelineModel} by
* binding the {@link TimelineModel#nowProperty()} to the
* {@link TimeTracker#timeProperty()}.
*
* @since 1.0
*/
public class TimeTracker extends Thread {
private boolean running = true;
private long delay = 1000;
private boolean stopped;
/**
* Constructs a new tracker.
*
* @since 1.0
*/
public TimeTracker() {
setName("Time Tracker");
setDaemon(true);
}
private final ReadOnlyObjectWrapper<Instant> time = new ReadOnlyObjectWrapper<>(
this, "time", Instant.now());
public final ReadOnlyObjectProperty<Instant> timeProperty() {
return time.getReadOnlyProperty();
}
public final Instant getTime() {
return time.get();
}
/**
* Returns the delay in milliseconds between updates of
* {@link TimelineModel#nowProperty()}. The default is 1000 millis.
*
* @return the default delay between update calls
* @since 1.0
*/
public final long getDelay() {
return delay;
}
/**
* Sets the delay between updates of {@link TimelineModel#nowProperty()}.
* The default is 1000 millis.
*
* @param millis
* the new delay
* @throws IllegalArgumentException
* if the delay is zero or smaller
* @since 1.0
*/
public final void setDelay(long millis) {
if (millis <= 0) {
throw new IllegalArgumentException(
"delay must be larger than zero but was" + millis); //$NON-NLS-1$
}
this.delay = millis;
}
/**
* Starts the tracking of the time.
*
* @since 1.0
*/
public final void startTracking() {
if (stopped) {
throw new IllegalStateException(
"Time tracker has already been stopped and can not be started again.");
} else {
running = true;
start();
}
}
@Override
public void run() {
while (running) {
Platform.runLater(() -> time.set(getNow()));
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
LoggingDomain.CONFIG.log(Level.WARNING,
"problem in update thread", e); //$NON-NLS-1$
}
}
}
/**
* Stops the tracking of the time.
*
* @since 1.0
*/
public final void stopTracking() {
stopped = true;
running = false;
}
/**
* Override to return the instant that will be set as "now" on the timeline
* model. The default implementation uses {@link Instant#now()}.
*
* @see TimelineModel#setNow(Instant)
*
* @return the "now" instant
*/
protected Instant getNow() {
return Instant.now();
}
}