Clock demo
Let's apply some of the stuff we learned in this Chapter to the Clock demo we started in the previous chapter.
The digital clock is not as fun as an analog one, so let's add hands to it.
We need three hands—hours, minutes, and seconds. We'll use a simple line for the seconds hand and slightly more complex path shapes for the hour and minute ones. For example, here is the path for the minute hand:
Path minuteHand = new Path(
new MoveTo(0, 0),
new LineTo(15, -5),
new LineTo(100,0),
new LineTo(15,5),
new ClosePath());
minuteHand.setFill(Color.DARKGRAY);
This code gives us not the prettiest but a conveniently simple hand. We'll work on making it nicer in following chapters:
To show time, the hand has to rotate. We'll use the Rotate transformation. We need to rotate the hand around the leftmost point, not the center, so we set the pivot point to zero coordinates:
Rotate rotateMinutesHand = new Rotate();
rotateMinutesHand.setPivotX(0);
rotateMinutesHand.setPivotY(0);
minuteHand.getTransforms().add(rotateMinutesHand);
Now, we can control the time set by modifying the angle for this Rotate transformation.
Also, our hand is inside our layout manager, which tries to center it around Path central point as well. But, we want to have a center in the local coordinates (0,0). To achieve that, we will translate our hand left by half of its actual size:
minuteHand.setTranslateX( minuteHand.getBoundsInLocal().getWidth()/2 );
For a better understanding, take a look at the ScenicView screenshot for this code—the dashed box is layoutBounds and the colored rectangle is boundsInParent, which changes after every rotation:
Here is the full code:
public class ClockTwo extends Application {
private final Text txtTime = new Text();
private Rotate rotateSecondHand = new Rotate(0,0,0);
private Rotate rotateMinuteHand = new Rotate(0,0,0);
private Rotate rotateHourHand = new Rotate(0,0,0);
private Thread timer = new Thread(() -> {
SimpleDateFormat dt = new SimpleDateFormat("hh:mm:ss");
Date now = new Date();
String time = dt.format(now);
Platform.runLater(()-> {
// updating live UI object requires JavaFX App Thread
rotateSecondHand.setAngle(now.getSeconds() * 6 - 90);
rotateMinuteHand.setAngle(now.getMinutes()* 6 - 90);
rotateHourHand.setAngle(now.getHours()* 30 - 90);
txtTime.setText(time);
});
try {
// running "long" operation not on UI thread
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
});
@Override
public void start(Stage stage) {
// create minutes hand
Path minuteHand = new Path(
new MoveTo(0, 0),
new LineTo(15, -5),
new LineTo(100,0),
new LineTo(15,5),
new ClosePath());
minuteHand.setFill(Color.DARKGRAY);
minuteHand.getTransforms().add(rotateMinuteHand);
minuteHand.setTranslateX(minuteHand.getBoundsInLocal().getWidth()/2);
// create second hand
Line secondHand = new Line(0,0, 90, 0);
secondHand.getTransforms().add(rotateSecondHand);
secondHand.setTranslateX(secondHand.getBoundsInLocal().getWidth()/2);
// create hour hand
Path hourHand = new Path(
new MoveTo(0, 0),
new LineTo(20, -8),
new LineTo(60,0),
new LineTo(20,8),
new ClosePath());
hourHand.setFill(Color.LIGHTGRAY);
hourHand.getTransforms().add(rotateHourHand);
hourHand.setTranslateX(hourHand.getBoundsInLocal().getWidth()/2);
BorderPane root = new BorderPane();
root.setCenter(new StackPane(minuteHand, hourHand, secondHand));
root.setBottom(txtTime);
BorderPane.setAlignment(txtTime, Pos.CENTER);
Scene scene = new Scene(root, 400, 350);
stage.initStyle(StageStyle.UTILITY);
stage.setScene(scene);
stage.setTitle("Clock, chapter 2");
timer.setDaemon(true);
timer.start();
stage.show();
System.out.println(minuteHand.getBoundsInLocal().getWidth());
}
public static void main(String[] args) {
launch(args);
}
}
The clock will appear as follows: