An Analog Clock in JavaFX

JavaFX is great fun but there are so many ways of doing things. You can use FXML, you can use images to build things piecewise or you can write code for a live, scalable design.  The latter is the case here where I show how I created an analog clock using Java code.

Analog Clock with desktop in background
The clock is designed with the clockwork in a separate class and the face in another.

This text shows some example JavaFX code in Java slightly above introductory level. You should have your development set up and done at least a first program.

Many examples that I found in JavaFX books has been quite horrifying as the style seems to be to write very long methods, all of sudden. You will find another style here.  I believe code can be readable by following two principles:

  1. Write short methods, typically a few lines.
  2. Use names that are specific.
You’ll be the judge whether I come through to you with my coding style. Comments are encouraged – to this blog, not in the code!. Also, bear with me if you think that the clock problem has been solved too many times. This is about code philosophy in the context of JavaFX.
Let’s start with the face of our clock first, beginning with the entry point of any JavaFX application in Java, the start method:
public void start(final Stage stage) throws Exception {
        final Parent root = GroupBuilder.create()
                .children(
                        outerRim(),
                        minuteHand(),
                        hourHand(),
                        secondsHand(),
                        tickMarks(),
                        centerPoint()
                )
                .build();
        setUpMouseForScaleAndMove(stage, root);
        Scene scene = makeATransparentScene(root);
        makeATransparentStage(stage, scene);
    }

That is the complete logic for the program! Well, in a sense. We create a root group that has the children enumerated. There are the expected ones such as the hands and the tick marks. Then we set up so the user can change size of the clock and move it. This is necessary since we wish to do without window decorations. Finally it is all put on a scene and staged.

Fine. But how about the details? Let’s start with the outer rim. That took me quite awhile, I can tell you. I had no understanding of the RadialGradient that I wished to use.

So I made a small program to develop it where I could experiment with the parameters.  When I was happy with the result, this is how the code looked like:

    private Node outerRim() {
        return CircleBuilder.create()
                .fill(
                        RadialGradientBuilder.create()
                                .stops(
                                        new Stop(0.8, Color.WHITE),
                                        new Stop(0.9, Color.BLACK),
                                        new Stop(0.95, Color.WHITE),
                                        new Stop(1.0, Color.BLACK)
                                )
                                .cycleMethod(CycleMethod.NO_CYCLE)
                                .centerX(0.5)
                                .centerY(0.5)
                                .radius(0.5)
                                .proportional(true)
                                .build()
                )
                .radius(unit)
                .centerX(unit)
                .centerY(unit)
                .build();
    }

Notice the “unit” constant, it is a reference stick that I use to size everything, set to 100. Having such helps me from thinking in pixels and instead thinking in terms of resizeability. By using 100 and not 1 avoids the confusion when the parameters really are proportional, as for the RadialGradiantBuilder above.

As you can see, I am fond of the builder pattern of JavaFX. Typically for JavaFX, you have the option of using traditional Swing-style building of components which would look more like this:

    private Node outerRim() {
        Stop stops[] = {
                new Stop(0.8, Color.WHITE),
                new Stop(0.9, Color.BLACK),
                new Stop(0.95, Color.WHITE),
                new Stop(1.0, Color.BLACK)
        };
        RadialGradient gradient = new RadialGradient(0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE, stops);

        Circle result = new Circle(unit, gradient);
        result.setCenterX(unit);
        result.setCenterY(unit);
        return result;
    }

I think readability suffers, especially the RadialGradient constructor is cryptic. Compare with the builder style earlier, not only are the parameters named, the structure becomes more clear as well.

Anyhow, here is a picture of the result:

Looking at the next thing, the minute and hour hands, we will see that these were made using a Path. They are so similar that I extracted to a general method, “hand”, which contains the design. The Color parameter is a tell-tale about me considering them in different colours.

    private Node hourHand() {
        return hand(unit * 0.4, Color.BLACK, 180);
    }

    private Node minuteHand() {
        return hand(unit * 0.2, Color.BLACK, 30);
    }


    private Node hand(double stretchRelativeToRim, Color color, int startAngle) {
        return PathBuilder.create()
                .fill(color)
                .stroke(Color.TRANSPARENT)
                .elements(
                        new MoveTo(unit, unit),
                        new LineTo(unit * 0.9, unit * 0.9),
                        new LineTo(unit, stretchRelativeToRim),
                        new LineTo(unit * 1.1, unit * 0.9),
                        new LineTo(unit, unit)
                )
                .transforms(
                        RotateBuilder.create()
                                .pivotX(unit)
                                .pivotY(unit)
                                .angle(startAngle)
                                .build()
                )
                .build();
    }

I imagine several other designs of the hands of an analog clock! So let’s head on to the seconds hand, a simple line.

    private Node secondsHand() {
        return LineBuilder.create()
                .startX(unit)
                .endX(unit)
                .startY(unit * 1.1)
                .endY(unit * 0.2)
                .transforms(
                        RotateBuilder.create()
                                .pivotX(unit)
                                .pivotY(unit)
                                .angle(360 / 12 * 9)
                                .build()
                )
                .build();
    }

The seconds are indicated by a simple line that starts of 10% before the midpoint and crosses it to stop 20% before the edge. As you can see for all three hands, we add a transformation that rotates the hand around the midpoint.

On to the tick marks then. Simple, we use short lines that are slightly longer at 3, 6, 9 and 12 o’clock. Note that I am explicit on how the angle should be calculated when using the constant expression 360 / 12. Readability, again, everyone understands that 360 is the degrees of the full circle and we are dividing it into 12 parts. We rely on the compiler for such easy optimizations as calculating a constant expression.

   private Node tickMarks() {
        Group tickMarkGroup = new Group();
        for (int n = 0; n < 12; n++) {
            tickMarkGroup.getChildren().add(tickMark(n));
        }
        return tickMarkGroup;
    }

    private Node tickMark(int n) {
        return LineBuilder.create()
                .startX(unit)
                .endX(unit)
                .startY(unit * 0.12)
                .endY(unit * (n % 3 == 0 ? 0.3 : 0.2))
                .transforms(
                        RotateBuilder.create()
                                .pivotX(unit)
                                .pivotY(unit)
                                .angle(360 / 12 * n)
                                .build()
                )
                .strokeWidth(2)
                .build();
    }

The last graphical component is the midpoint. It is just a small circle.

    private Node centerPoint() {
        return CircleBuilder.create()
                .fill(Color.BLACK)
                .radius(0.05 * unit)
                .centerX(unit)
                .centerY(unit)
                .build();
    }

What remains then? We have a round, analog clock with hands and so on. It has the look of it! Well, we would like our users to be able to resize and move it to their liking at the same time as we wish to have an undecorated window, as you saw in the first image of this post. Window decorations are ugly on a little gadget like this one.

The mouse handling is shown below. When the user scrolls, typically with the mouse wheel, the clock scales. When the user drags, typically using a left-click-move mouse gesture, the clock moves accordingly. Now, right away, I admit being lazy, the move code positions the clock at the midpoint of the cursor.

    private void setUpMouseForScaleAndMove(final Stage stage, final Parent root) {
        root.onMouseDraggedProperty().set(moveWhenDragging(stage));
        root.onScrollProperty().set(scaleWhenScrolling(stage, root));
    }

    private EventHandler<MouseEvent> moveWhenDragging(final Stage stage) {
        return new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                stage.setX(mouseEvent.getScreenX() - stage.getWidth() / 2);
                stage.setY(mouseEvent.getScreenY() - stage.getHeight() / 2);
            }
        };
    }

    private EventHandler<ScrollEvent> scaleWhenScrolling(final Stage stage, final Parent root) {
        return new EventHandler<ScrollEvent>() {
            @Override
            public void handle(ScrollEvent scrollEvent) {
                double scroll = scrollEvent.getDeltaY();
                root.setScaleX(root.getScaleX() + scroll / 100);
                root.setScaleY(root.getScaleY() + scroll / 100);
                root.setTranslateX(root.getTranslateX() + scroll);
                root.setTranslateY(root.getTranslateY() + scroll);
                stage.sizeToScene();
            }
        };
    }

You see how easy it is to have your JavaFX application move its top window across the screen. Just change the X and Y of the stage.

We still have a few things left before we can finish, the Scene and the Stage.

    private Scene makeATransparentScene(Parent root) {
        return SceneBuilder.create()
                .root(root)
                .fill(Color.TRANSPARENT)
                .build();
    }

    private void makeATransparentStage(Stage stage, Scene scene) {
        stage.setScene(scene);
        stage.initStyle(StageStyle.TRANSPARENT);
        stage.show();
    }

Ok, fine, a static clock that misses a crucial thing to make it a real clock – the clockwork! We create a Clockwork class that has some properties that we can bind to. The properties will be hour, minute and second.

public class Clockwork {

    public SimpleIntegerProperty hour = new SimpleIntegerProperty(0);
    public SimpleIntegerProperty minute = new SimpleIntegerProperty(0);
    public SimpleIntegerProperty second = new SimpleIntegerProperty(0);

    public Clockwork() {
        startTicking();
    }

    private void startTicking() {
        TimelineBuilder.create()
                .cycleCount(Timeline.INDEFINITE)
                .keyFrames(
                        new KeyFrame(Duration.seconds(1), updateTime())
                )
                .build()
                .play();
    }

    private EventHandler updateTime() {
        return new EventHandler() {
            @Override
            public void handle(Event event) {
                Calendar calendar = Calendar.getInstance();
                hour.set(calendar.get(Calendar.HOUR));
                minute.set(calendar.get(Calendar.MINUTE));
                second.set(calendar.get(Calendar.SECOND));
            }
        };
    }
}

The Timeline feature makes a ticking clock a breeze. Simply put, we just want our updateTime method to be called once a second. It will set the three mentioned properties which the clock will be bound to.

The last thing is to wire up our clock with the clockwork. Of course, the powerful bind mechanism of JavaFX is suitable for this. We need to bind the rotation angle of the hands to the clockwork properties. Here is the new version of the methods that create the hands:

    private Node hourHand() {
        Rotate rotate = rotationAroundCenter();
        rotate.angleProperty().bind(clockwork.hour.multiply(360 / 12));
        return hand(unit * 0.4, Color.BLACK, rotate);
    }

    private Node minuteHand() {
        Rotate rotate = rotationAroundCenter();
        rotate.angleProperty().bind(clockwork.minute.multiply(360 / 60));
        return hand(unit * 0.2, Color.BLACK, rotate);
    }

    private Node secondsHand() {
        Rotate rotate = rotationAroundCenter();
        rotate.angleProperty().bind(clockwork.second.multiply(360 / 60));
        return LineBuilder.create()
                .startX(unit)
                .endX(unit)
                .startY(unit * 1.1)
                .endY(unit * 0.2)
                .transforms(rotate)
                .build();
    }
    
    private Rotate rotationAroundCenter() {
        return RotateBuilder.create()
                .pivotX(unit)
                .pivotY(unit)
                .build();
    }


    private Node hand(double stretchRelativeToRim, Color color, Rotate rotate) {
        return PathBuilder.create()
                .fill(color)
                .stroke(Color.TRANSPARENT)
                .elements(
                        new MoveTo(unit, unit),
                        new LineTo(unit * 0.9, unit * 0.9),
                        new LineTo(unit, stretchRelativeToRim),
                        new LineTo(unit * 1.1, unit * 0.9),
                        new LineTo(unit, unit)
                )
                .transforms(rotate)
                .build();
    }

As you can see, the rotation transformation has been refactored to an extracted method. Note how we bind to a calculated value. For example, the minute hand will be rotated 360 / 60 degrees times the number of minutes.

rotate.angleProperty().bind(clockwork.minute.multiply(360 / 60));

In summary, I tried to show a style that favours readability. I also showed how the domain object – the java.util.Calendar can be encapsulated and have selected properties exposed by an adapter, Clockwork, so that presentation objects can bind to them. It is easy to imagine a digital clock using the same clockwork.

JavaFX is, starting from Java 7, part of the standard Java SE and becoming a serious contender when it comes to solving presentation problems. Not only can we do classical forms with a limited set of interactive gadgets, like the ones provided in HTML, we can also go further and create completely new ways of doing GUI.

12 responses on “An Analog Clock in JavaFX

  1. As a test of the readability of the code and exercise to the reader, find how to change the behaviour of the hour hand. Currently it is digital, it hops between the ticks which is not very analogue behaviour. Make it move gradually instead.

  2. Interesting post Per. Clock is an interesting application to compare styles on as it is one of the few applications which is implemented multiple times by different people. Here is my implementation for example, which follows the JavaFX demo “tradition” of placing most code in a single method. In my own experience I have found the single method style works best for small demos, but as programs get larger and more complex, multiple small methods and classes are clearly better. I think a couple of important topics with respect to JavaFX style are use of css and/or fxml vs coding directly in the Java API as well as procedural vs builder java code and would appreciate your thoughts on those topics.

    1. Yes there are other ways of doing it. Right after the clock project, I started a small game project where I use FXML to set up scenes. It sort of puts all the static graphics in one bucket and have hooks for the rest. I am not convinced that it is good yet. A traditional interface with complex forms should lend itself more to it.

      When it comes to CSS, I feel that it comes with some problems that are really from the web world. If I’d like to have a way of changing appearance in a single spot, it is easily done in Java but with HTML, you need CSS. With that said, their is of course the possibility of user customization that perhaps in some case could relay on CSS, but I think it remains to be seen.

      On coding style, yes, I have observed the same “tradition”, this post is an attack on that. 🙂 I can happily note that Carl Dea and Jim Weaver liked the post. 🙂

      As for how many lines of code a method should have, the rules for clean code applies always. Your code should always be readable. If it is a demo, will you not let your reader in and understand?

      A typical sign of not doing it right is when you write comments. Don’t write comments in your code, write code that reads well without comments!

      I read you code and I think it is virtually unreadable, with all respect. It is a long method sprinkled with constants that carries little or no meaning. If I am given such a code to change, be it fix a small bug only, I start to extract method ’til I drop. It is the fastest way of understanding code.

      Let’s start a new tradition with well written code – hope you agree with me!

  3. Late to the party, but I’m following the jewelsea’s request at the bottom of his gist.

    I’m doing my first program with FX, but I’m not new to GUI programming, and personally prefer the Per’s style of using many small methods all returning Nodes like outerRim() minuteHand() and so on. Plus, I find the FX API a great improvement over Swing because of Builders, which save typing and makes the code easier to read.

    However what I find difficult in GUI programming is finding names. There’s a famous joke that there are three hard things in computer science: naming things, cache invalidation and off-by-one errors. Naming things is at the first place, and with no surprise. Maybe this was thought in another context and doesn’t originally refers to method names (or local variables names like in jewelsea’s code), but I think the concept still applies. When the program grows and time passes by, there too many names not meaningful anymore. I really like the FXML approach of declaring handlers with strings like “#handleFoo” and I’d really appreciate a similar binding scheme but 100% Java, because writing (and reading) XML is frustrating.

    However there are cases (think of tables with custom factories) where you eventually end up with not-so-small methods (like 50 lines or so), because real world applications always need to show tabular data (in either TableView’s or ListView’s), and I’d really like the JavaFX engineers to come up with solutions to this to allow concise and expressive code.

    1. No problem you being late. I am happy that people still find old posts and comment on them.

      Yes, it is true that naming is one of the hardest problem in computer science. I observed that it is worth the while as not only becomes the code more readable, it also make me think about what I am actually trying to accomplish.

      Nowadays, renaming is not such a hassle as it used to be so that takes away some of the pain. I find that when my idea is blurry, the naming suffers but I can live with it since I know that I will have the discipline to rename things later on.

      Thanks for commenting. Don’t miss my colleague’s Oscar Lantz revisit of this problem! See pingback above.

  4. Hi, thank you for the example, but it appears that the code has since been deprecated in Java 8. It would be great to see another version with JavaFX 8 and maybe a download link,

    1. Ross! Thanks for reading my old post and comment.

      Our GitHub repo has code examples https://github.com/crispab/crisp-code-examples

      Among the JavaFX code examples is a new version with the clock split up so that the clockwork is in a separate class. The logic for a gadget that is always on top is in a utility class. The logics for the hands etc is rewritten for Java 8.

      Let me know what you think.

Leave a Reply to Per Lundholm Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.