Creating a timeline image with JavaFX
To create some timeline images for my book, I created this little JavaFX application to be able to easily update the content and recreate the image. Of course you can do the same in an image editor, but hey I’m a programmer and lazy, so I want a program to do the job for me ;-)
History of programming languages
Other timelines generated with this code below…
Source code
The sources are available on GitHub inside the repository with all the book example code.
The datasets used to generate the timelines, are defined in an enum in the format startYear, endYear, entries(year, label):
LANGUAGE_BIRTHDATES(1990, 2024, Map.ofEntries( entry(1990, "Ruby\n "), entry(1991, "Python\n "), entry(1995, "Java, JavaScript, PHP, Qt"), entry(2000, "C#"), entry(2009, "Go"), entry(2014, "Swift"))),
Drawing the timeline itself is achieved with Lines and a label added to a Pane:
public class JavaTimeline extends Pane { public JavaTimeline(int width, int height, int offset, DataSet dataSet) { // Set the background color this.setStyle("-fx-background-color: #ffffff"); // Draw the main line from left to right int axisLength = width - (2 * offset); Line horizontalAxis = new Line(offset, offset * 3, axisLength, offset * 3); horizontalAxis.setStroke(Color.DARKGREY); horizontalAxis.setStrokeWidth(5); this.getChildren().add(horizontalAxis); // Draw the year lines int yearsToDraw = dataSet.getEndYear() - dataSet.getStartYear() + 1; double distanceBetweenYears = axisLength / yearsToDraw; for (int i = 0; i < yearsToDraw; i++) { int currentYear = dataSet.getStartYear() + i; double yearLineX = (offset * 3) + (i * distanceBetweenYears); Line yearLine = new Line(yearLineX, offset * 2, yearLineX, offset * 4); yearLine.setStroke(Color.DARKGREY); yearLine.setStrokeWidth(currentYear % 10 == 0 ? 4 : 2); this.getChildren().add(yearLine); // Add a label for every 10 year if (currentYear % 10 == 0) { Label yearLabel = new Label(String.valueOf(currentYear)); yearLabel.setLayoutX(yearLineX - 20); yearLabel.setLayoutY(offset * 5); yearLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 18px;"); this.getChildren().add(yearLabel); } // Add a special notation String notation = dataSet.getEntries().get(currentYear); if (notation != null && !notation.isEmpty()) { Line notationLine = new Line(yearLineX, offset * 5, yearLineX, offset * 9); notationLine.setStroke(Color.RED); notationLine.setStrokeWidth(3); this.getChildren().add(notationLine); int notationLabelWidth = height - (offset * 9); Label notationLabel = new Label(String.valueOf(dataSet.getStartYear() + i) + " - " + notation); notationLabel.setLayoutX(yearLineX); notationLabel.setLayoutY((offset * 9)); notationLabel.setPrefWidth(notationLabelWidth); notationLabel.setPrefHeight(40); notationLabel.setStyle("-fx-font-size: 14px; -fx-text-alignment: left;"); notationLabel.getTransforms().add(new Rotate(70, 0, 20 / 2, 0, Rotate.Z_AXIS)); this.getChildren().add(notationLabel); } } } }