概述
Dateline 是一个控件,用于在一行或多行单元格中显示实际日期(周一、周二、周三……)。Dateline 支持时区感知,会跟踪当前选中的时间区间以及当前悬停的时间区间。当可见时间范围发生变化时(例如向左或向右滚动后),它还会触发事件。

刻度分辨率
Dateline 可以显示一到五行。每一行称为一个“Dateline 刻度”,每个刻度显示一个“分辨率”。分辨率由时间单位(例如天、周、月)、格式化模式和数量组成。数量用于指定“5 分钟”“15 分钟”等分辨率。可以通过调用 getScaleResolutions() 获取 Dateline 当前显示的完整分辨率列表。
系统层 GridLinesLayer 提供了该方法的一个使用示例。它会调用此方法,使用分辨率来计算垂直网格线的位置。为此,Resolution 类提供了 truncate() 方法用于跳到某个单位的起点(例如一天的开始),以及 increment() 方法用于跳到下一个单位(例如下一天)。有关 Resolution 的更多信息,请参阅 Dateline 模型文档。
主要时间单位
例如,一个包含三个刻度的 Dateline 可以显示“月”“周”和“日”这三种分辨率。最小的“日”分辨率显示在 Dateline 底部。该分辨率使用的时间单位 ChronoUnit.DAYS 也称为“主要时间单位”。该单位的当前值存储在只读属性 primaryTemporalUnit 中。查询活动存储库中的活动时会使用该属性值。这样,存储库就可以决定调用结果的粒度,或者决定某些活动是否完全不显示。
WeekendCalendar 类是主要时间单位的一个良好用例。它实现了 Calendar,而 Calendar 是 ActivityRepository 的扩展。WeekendCalendar 的用途是返回给定时间区间中的周末日期(星期六、星期日)。当它被调用时,如果主要时间单位过大或过小,就不会返回任何内容。如果用户当前查看的是分钟或十年,返回周末信息就没有意义。
时区
Dateline 需要知道它正在为哪个时区显示日期(例如 EST 或 GMT)。因此,它提供了 zoneIdProperty() 属性。该属性可写,并可通过 setZoneId() 设置。可以通过调用 setZoneId(true) 让该属性的值在控件中可见。
选择模型
Dateline 控件允许用户在按住快捷修饰键(Windows / Linux 上为 CTRL,Mac 上为 Option)的同时点击主鼠标按钮,对时间区间进行单选或多选。是否支持单选或多选取决于 selectionModeProperty() 的值。
只有当前在任意行/刻度中可见的区间才能被选中。因此,如果 Dateline 当前显示周和日,用户就只能选择整周或整天。可以通过调用 getSelectedTimeIntervals() 获取已选区间列表。
悬停时间区间
当鼠标指针悬停在 Dateline 上时,也意味着它悬停在某个时间区间上。根据给定鼠标位置所在 Dateline 行/刻度显示的分辨率,该区间可能是一整周,也可能是单独一天。无论是哪一种,该区间都会存储在只读属性 hoverTimeIntervalProperty() 中。
事件
当应用程序需要响应当前可见时间范围的任何变化时,可以监听 Dateline 触发的滚动事件。可以通过向 setOnVisibleRangeChanged() 方法传入事件监听器,或调用 addEventListener(DatelineScrollingEvent.ANY, myListener) 来实现。
单元格工厂
Dateline 控件能够显示不同类型的时间单位。默认支持 ChronoUnit(周一、周二、周三……)和 SimpleUnit(1、2、3、4……)。每种单位类型都有自己的视觉表示。为适配这一点,Dateline 控件会将 Dateline 单元格的创建委托给一个可插拔工厂,该工厂已预先映射到特定的时间单位类型。
setCellFactory(SimpleUnit.class, unit -> new SimpleUnitDatelineCell());
setCellFactory(ChronoUnit.class, unit -> new ChronoUnitDatelineCell());
Dateline 模型
Dateline 模型向 Dateline 控件提供各种信息,使其能够正确布局自身。
- 分辨率 – 分辨率定义要显示哪个时间单位(例如小时)以及如何格式化它。它还包含该分辨率是否可以显示在顶部、底部或中间刻度中的信息。每个模型通常会定义一长串此类分辨率。定义的分辨率越多,Dateline 控件在缩放时就越灵活。
- 时区 – Dateline 控件允许用户在不同时区之间切换。模型定义哪些时区可用。
- 刻度数量 – Dateline 控件由一组 Dateline 刻度组成(顶部、底部以及若干中间刻度)。模型可用于定义用户可选择查看的当前可见刻度数、最小刻度数和最大刻度数。
- 时间单位 – Dateline 控件在为当前单位创建刻度失败或成功后,会回调模型以查找“下一个”时间单位。
Dateline 模型是一个类型化模型。FlexGanttFX 随附两个特化实现:ChronoUnitDatelineModel 和 SimpleUnitDatelineModel。
Chrono Unit Dateline 模型
ChronoUnitDatelineModel 类是针对 JDK 8 中 ChronoUnit 时间单位的特化实现。它需要类型为 ChronoUnitResolution 的刻度分辨率。下面的清单是该模型的实现,并展示了如何定义和添加分辨率,以及如何使用分辨率从一个时间单位前进到下一个时间单位。
package com.flexganttfx.model.dateline;
import java.time.temporal.ChronoUnit;
import static com.flexganttfx.model.dateline.Resolution.Position.*;
import static java.time.temporal.ChronoUnit.*;
/**
* The chrono unit dateline model is a specialization of the dateline model that works
* in combination with the {@link ChronoUnit}. The chrono unit basically represents standard
* calendar units ranging from milliseconds to thousands of years.
*
* @since 1.0
*/
public final class ChronoUnitDatelineModel extends DatelineModel<ChronoUnit> {
/**
* Constructs a new dateline model with a long list of predefined
* resolutions of type {@link ChronoUnitResolution}.
*
* @since 1.0
*/
public ChronoUnitDatelineModel() {
addResolution(new ChronoUnitResolution(MILLIS, "EEEE, dd. MMMM yyyy, HH:mm:ss:SSS", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MILLIS, "EEEE, dd.MM.yy, HH:mm:ss:SSS", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MILLIS, "E, dd.MM.yy, HH:mm:ss:SSS", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MILLIS, "dd.MM.yy, HH:mm:ss:SSS", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MILLIS, "dd.MM, HH:mm:ss:SSS", 1, TOP));
addResolution(new ChronoUnitResolution(MILLIS, "SSS", 1, BOTTOM));
addResolution(new ChronoUnitResolution(MILLIS, "SSS", 5, BOTTOM));
addResolution(new ChronoUnitResolution(MILLIS, "SSS", 10, BOTTOM));
addResolution(new ChronoUnitResolution(MILLIS, "SSS", 15, BOTTOM));
addResolution(new ChronoUnitResolution(SECONDS, "EEEE, dd. MMMM yyyy, HH:mm:ss", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(SECONDS, "EEEE, dd.MM.yy, HH:mm:ss", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(SECONDS, "E, dd.MM.yy, HH:mm:ss", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(SECONDS, "dd.MM.yy, HH:mm:ss", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(SECONDS, "dd.MM, HH:mm:ss", 1, MIDDLE));
addResolution(new ChronoUnitResolution(SECONDS, "HH:mm:ss", 1, MIDDLE));
addResolution(new ChronoUnitResolution(SECONDS, "ss", 1, BOTTOM));
addResolution(new ChronoUnitResolution(SECONDS, "ss", 5, BOTTOM));
addResolution(new ChronoUnitResolution(SECONDS, "ss", 10, BOTTOM));
addResolution(new ChronoUnitResolution(SECONDS, "ss", 15, BOTTOM));
addResolution(new ChronoUnitResolution(MINUTES, "EEEE, dd. MMMM yyyy, HH:mm", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MINUTES, "EEEE, dd.MM.yy, HH:mm", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MINUTES, "E, dd.MM.yy, HH:mm", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MINUTES, "dd.MM.yy, HH:mm", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MINUTES, "dd.MM, HH:mm", 1, TOP));
addResolution(new ChronoUnitResolution(MINUTES, "HH:mm", 1, MIDDLE));
addResolution(new ChronoUnitResolution(MINUTES, "mm", 1, BOTTOM));
addResolution(new ChronoUnitResolution(MINUTES, "mm", 5, BOTTOM));
addResolution(new ChronoUnitResolution(MINUTES, "mm", 10, BOTTOM));
addResolution(new ChronoUnitResolution(MINUTES, "mm", 15, BOTTOM));
addResolution(new ChronoUnitResolution(HOURS, "EEEE, dd. MMMM yyyy, HH:mm", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(HOURS, "EEEE, dd.MM.yy, HH:mm", 1, TOP, BOTTOM, ONLY));
addResolution(new ChronoUnitResolution(HOURS, "E, dd.MM.yy, HH:mm", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(HOURS, "dd.MM.yy, HH:mm", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(HOURS, "dd.MM, HH:mm", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(HOURS, "H:mm", 1, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(HOURS, "H:mm", 3, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(HOURS, "H:mm", 6, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(DAYS, "EEEE d. MMMM yyyy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(DAYS, "EEEE d. MMMM yy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(DAYS, "E, d. MMMM yy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(DAYS, "E, d. MMMM", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(DAYS, "E, dd.MM.yy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(DAYS, "EEEE dd", 1, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(DAYS, "E dd", 1, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(DAYS, "dd.MM", 1, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(DAYS, "dd", 1, BOTTOM));
addResolution(new ChronoUnitResolution(WEEKS, "'W' w, EEEE d. MMMM yy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(WEEKS, "'W' w, d. MMMM yy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(WEEKS, "'W' w, d. MMMM", 1));
addResolution(new ChronoUnitResolution(WEEKS, "'W' w, E, dd.MM.yy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(WEEKS, "'W' w, dd.MM.yy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(WEEKS, "'W' w, dd.MM", 1, BOTTOM));
addResolution(new ChronoUnitResolution(WEEKS, "'W' w", 1, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(MONTHS, "MMMM yyyy", 1, TOP, ONLY));
addResolution(new ChronoUnitResolution(MONTHS, "MMMM", 1, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(MONTHS, "MMM", 1, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(MONTHS, "M", 1, MIDDLE, BOTTOM));
addResolution(new ChronoUnitResolution(YEARS, "yyyy", 1));
addResolution(new ChronoUnitResolution(DECADES, "yyyy", 1));
addResolution(new ChronoUnitResolution(CENTURIES, "yyyy", 1));
addResolution(new ChronoUnitResolution(MILLENNIA, "yyyy", 1));
}
@Override
public final ChronoUnit nextTemporalUnit(ChronoUnit unit) {
switch (unit) {
case NANOS:
return MICROS;
case MICROS:
return MILLIS;
case MILLIS:
return SECONDS;
case SECONDS:
return MINUTES;
case MINUTES:
return HOURS;
case HOURS:
return DAYS;
case DAYS:
return WEEKS;
case WEEKS:
return MONTHS;
case MONTHS:
return YEARS;
case YEARS:
return DECADES;
case DECADES:
return CENTURIES;
case CENTURIES:
return MILLENNIA;
default:
/*
* We are ignoring HALF DAYS.
*/
return null;
}
}
}
Simple Unit Dateline 模型
SimpleUnitDatelineModel 类是针对 FlexGanttFX 随附的 SimpleUnit 时间单位的特化实现。它需要类型为 SimpleUnitResolution 的刻度分辨率。下面的模型类实现展示了为什么该单位被称为“简单”。
package com.flexganttfx.model.dateline;
import com.flexganttfx.model.util.SimpleUnit;
public final class SimpleUnitDatelineModel extends DatelineModel<SimpleUnit> {
public SimpleUnitDatelineModel() {
for (SimpleUnit unit : SimpleUnit.values()) {
addResolution(new SimpleUnitResolution(unit, "", 1));
}
}
@Override
public SimpleUnit nextTemporalUnit(SimpleUnit unit) {
int ordinal = unit.ordinal();
if (ordinal < SimpleUnit.values().length - 1) {
return SimpleUnit.values()[ordinal + 1];
}
return null;
}
}
时区
Dateline 模型管理一个时区 ID 列表,UI 使用该列表为每个 ID 创建菜单项。这样用户就可以轻松在它们之间切换。默认列表在 DatelineModel 类中按如下方式设置:
/**
* Constructs a new model and populates the list of available zone IDs.
*/
protected DatelineModel() {
addZoneId("Europe/Berlin");
addZoneId("America/New_York");
addZoneId("Australia/Darwin");
addZoneId("Australia/Sydney");
addZoneId("America/Argentina/Buenos_Aires");
addZoneId("Africa/Cairo");
addZoneId("America/Anchorage");
addZoneId("America/Sao_Paulo");
addZoneId("Asia/Dhaka");
addZoneId("Africa/Harare");
addZoneId("America/St_Johns");
addZoneId("America/Chicago");
addZoneId("Asia/Shanghai");
addZoneId("Africa/Addis_Ababa")
addZoneId("Europe/Paris");
addZoneId("America/Indiana/Indianapolis");
addZoneId("Asia/Kolkata");
addZoneId("Asia/Tokyo");
addZoneId("Pacific/Apia");
addZoneId("Asia/Yerevan");
addZoneId("Pacific/Auckland");
addZoneId("Asia/Karachi");
addZoneId("America/Phoenix");
addZoneId("America/Puerto_Rico");
addZoneId("America/Los_Angeles");
addZoneId("Pacific/Guadalcanal");
addZoneId("Asia/Ho_Chi_Minh");
}