Get seconds value and display to console window - java

I have coded a simple countdown timer in JavaFX and implements the Timer with binding so whenever the value of timeSeconds changes, the timerLabel text also changes.
How to get the value of current seconds and show it to console window?
the output should display the value of current seconds every new line like:
5
4
3
2
1
0
public class FXTimerBinding extends Application
{
// private class constant and somme variables
private static final Integer STARTTIME = 5;
private Timeline timeline;
private Label timerLabel = new Label();
private IntegerProperty timeSeconds = new SimpleIntegerProperty(STARTTIME);
#Override
public void start(Stage primaryStage)
{
// setup the Stage and the Scene(the scene graph)
primaryStage.setTitle("FX Timer binding");
Group root = new Group();
Scene scene = new Scene(root, 300, 250);
// configure the label
timerLabel.setText(timeSeconds.toString());
timerLabel.setTextFill(Color.RED);
timerLabel.setStyle("-fx-font-size: 4em;");
// Bind the timerLabel text property to the timeSeconds property
timerLabel.textProperty().bind(timeSeconds.asString());
// create and configure the Button
Button button = new Button("Start timer");
button.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event)
{
if(timeline != null)
timeline.stop();
timeSeconds.set(STARTTIME);
timeline = new Timeline();
KeyValue keyValue = new KeyValue(timeSeconds, 0);
KeyFrame keyFrame = new KeyFrame(Duration.seconds(STARTTIME + 1), keyValue);
timeline.getKeyFrames().add(keyFrame);
timeline.playFromStart();
System.out.println("get every seconds value and display to console window");
}
});
from: http://www.asgteach.com/blog/?p=334

If you want to perform some other action when the actual value of timeSeconds changes, just add a listener to it:
timeSeconds.addListener((observable, oldTimeValue, newTimeValue) -> {
// code to execute here...
// e.g.
System.out.println("Time left: "+newTimeValue);
});
If you are changing the UI in response to the countdown changing value, though, a binding of the kind of already have is preferable, imho.

Related

How to make a method call wait for an animation to finish

I want to make a notification window with animated text. A notification would be sent by a button click and the animation would start playing. My problem is that when I click the button again before the previous animation is done, two animations get executed at once. How do I make each method call of "sendMessage()" wait for the other to finish? If it has any significance there are multiple nodes that call the sendMessage() method in my program unlike in my MRE, so I want some kind of Queue with messages. Here is my MRE:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
#Override
public void start(Stage stage) throws IOException {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> sendMessage("Some animated text."));
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private void sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
public static void main(String[] args) {
launch();
}
}
For the specific example you posted, the easiest approach is to disable the button immediately prior to starting the animation, and enable it again when the animation stops. Here is one way to do this:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
#Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> {
Animation animation = sendMessage("Some animated text.");
button.disableProperty().bind(Bindings.equal(animation.statusProperty(), Animation.Status.RUNNING));
});
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private Animation sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
return timeline ;
}
public static void main(String[] args) {
launch();
}
}
If you want to allow these messages to accumulate in a queue, and a new animation to start when the old one finishes, you need to keep a queue of the messages and a reference to a current animation that's running (if there is one). You can poll the queue from an AnimationTimer and start a new animation when a new message appears, if there is no current animation running.
I'd recommend thinking about whether this is the approach you want to take; there's no guarantee here that your messages will not appear more quickly than they can be animated, in which case the queue will grow indefinitely. However, this is an implementation if you can otherwise assure that this is not the case:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
private final Queue<String> messages = new LinkedList<>();
private Animation currentAnimation = null ;
#Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> messages.add("Some animated text."));
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long l) {
if (currentAnimation == null || currentAnimation.getStatus() == Animation.Status.STOPPED) {
String message = messages.poll();
if (message != null) {
currentAnimation = sendMessage(message);
currentAnimation.play();
}
}
}
};
timer.start();
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private Animation sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
return timeline ;
}
public static void main(String[] args) {
launch();
}
}
Note there are no threading considerations here. The handle() method is invoked on the FX Application Thread, so the only requirement is that the messages are placed in the queue on the same thread. This happens in this example because the button's event handler is invoked on that thread. If your messages are coming from a background thread, you should ensure they are added to the queue on the FX Application Thread, either by using Platform.runLater(...) or (preferably) by using the JavaFX Concurrency API (i.e. by retrieving the messages in a Task or Service and adding them to the queue in an onSucceeded handler).
Set a boolean (best do it atomic, because multithreading and stuff...) at animation start & end.
Disable the Button when boolean is in animation phase.

JavaFX, Having the same object/item in different scenes in the same stage?

I am working on an application in JavaFX where I need multiple scenes to switch between. But it seems like I can't have the same item (Example: a toolbar) In multiple scenes, it just shows the item in one of the scenes. Maybe it isn't possible to have the same item in different scenes, so my question is how do I do it then? Do I need multiple stages and if that is the case how do I change between stages? I am not using FXML for this project, we have to code it.. My current code:
public class Main extends Application {
private Label time;
private int minute;
private int hour;
private int second;
public static void main(String[] args) {
launch(args);
}
// CLOCK RUNNING
public void initialize() {
Timeline clock = new Timeline(new KeyFrame(Duration.ZERO, e -> {
Calendar cal = Calendar.getInstance();
second = cal.get(Calendar.SECOND);
minute = cal.get(Calendar.MINUTE);
hour = cal.get(Calendar.HOUR);
//System.out.println(hour + ":" + (minute) + ":" + second);
time.setText(hour + ":" + (minute) + ":" + second);
}),
new KeyFrame(Duration.seconds(1))
);
clock.setCycleCount(Animation.INDEFINITE);
clock.play();
}
#Override
public void start(Stage primaryStage) throws Exception {
//Specify The Size of Scenes, and the scenes.
BorderPane root1 = new BorderPane();
BorderPane root2 = new BorderPane();
Scene scene1 = new Scene(root1, 1100, 900);
Scene scene2 = new Scene(root2,1100,900);
// Get CSS File
scene1.getStylesheets().add("Helmuth.css");
time = new Label("Time:");
initialize();
//ToolBar i want this to be shown in both scenes //
Button homebt = new Button("Home");
Button tabelbt = new Button("Tabel");
ToolBar toolBar = new ToolBar();
toolBar.getItems().add(homebt);
toolBar.getItems().add(tabelbt);
toolBar.getItems().add(time);
Label label1 = new Label("Welcome to the first scene!");
Button button1 = new Button("Go to scene 2");
button1.setOnAction(e -> primaryStage.setScene(scene2));
VBox layout1 = new VBox();
layout1.getChildren().addAll(button1,toolBar);
Button button2 = new Button("Go Back");
button2.setOnAction(e -> primaryStage.setScene(scene1));
VBox mainbox = new VBox();
mainbox.setAlignment(Pos.TOP_CENTER);
mainbox.getChildren().addAll(button2, toolBar);
// Start scene 1
root2.setCenter(mainbox);
root1.setCenter(layout1);
primaryStage.setScene(scene1);
primaryStage.setTitle("Helmuth");
boolean b = false;
primaryStage.setResizable(b);
primaryStage.show();
}
}
Why do you want to switch between different scenes. A solution to your problem might be to just exchange the root node of the scene.

Check how long was a button pressed

I am making a simulator in which I have a button with different behavior depending on the duration of the press.
If the button is pressed less than 3 seconds, prints nothing, between 3 and 10, prints 1 and higher than 10 prints 2.
Should I try with MouseListener or ActionListener? Any example code would be great! Thanks.
Listen to changes in the pressed property:
public class StageTest extends Application{
private long startTime;
#Override
public void start(Stage primaryStage) {
Button btn = new Button("Hold");
Label label= new Label();
btn.pressedProperty().addListener((obs, wPressed, pressed) -> {
if (pressed) {
startTime = System.nanoTime();
label.setText("");
} else {
label.setText("Button was pressed for "+ (System.nanoTime() - startTime) + " nanos");
}
});
Pane root = new VBox(btn, label);
Scene scene = new Scene(root, 300, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
A simple hack:
Create a timer and start the timer as soon as the button is pressed. Set the interval of the timer to one second then have a counter that increments each time the timer event is fired to keep track of how many seconds it was pressed. As soon as the button is released stop the timer and perform any action you want to

My TimerTask Function cannot update the label.setText

Im writing a small program that involves a timer but for some reason I cant get the TimerTask to update the value of my Label.setText
public class Main extends Application {
Label TimerLabel;
/* Create a Button named button */
Button button;
/* Create three Radiobuttons named Radio1,Radio2,Radio3 */
RadioButton Radio1, Radio2, Radio3;
Timer QuestionTimer = new Timer();
TimerTask QuestionTick = new TimerTask() {
#Override
public void run() {
TimerLabel.setText(String.valueOf(Integer.valueOf(TimerLabel.getText())+1));
}
};
public static void main(String[] args) {
launch(args);
}
/*UI Part*/
#Override
public void start(Stage primaryStage) throws Exception {
/*Window(Stage) Title is set to "Try 1"*/
primaryStage.setTitle("Try 1");
/*Button properties*/
button = new Button();
button.setText("Click Me");
//Radio Button
Radio1 = new RadioButton();
Radio1.setText("Click Me 1");
Radio1.setOnAction(e->{
System.out.println("Radio Button Clicked");
});
Radio2 = new RadioButton();
Radio2.setText("Click Me 2");
Radio2.setOnAction(e->{
System.out.println("Radio Button 2 Clicked");
});
Radio3 = new RadioButton();
Radio3.setText("Click Me 3");
Radio3.setOnAction(e->{
System.out.println("Radio Button 3 Clicked");
});
TimerLabel = new Label();
TimerLabel.setText("0");
/*Here my layout is defined */
Pane Buttonlayout = new Pane();
button.setLayoutX(200);
button.setLayoutY(200);
Radio1.setLayoutX(15);
Radio1.setLayoutY(20);
Radio2.setLayoutX(15);
Radio2.setLayoutY(40);
Radio3.setLayoutX(15);
Radio3.setLayoutY(60);
TimerLabel.setLayoutX(100);
TimerLabel.setLayoutY(20);
Buttonlayout.getChildren().add(button);
Buttonlayout.getChildren().add(Radio1);
Buttonlayout.getChildren().add(Radio2);
Buttonlayout.getChildren().add(Radio3);
Buttonlayout.getChildren().add(TimerLabel);
/*Here we define the scene (aka everything inside the stage (inside the window))*/
Scene scene1 = new Scene(Buttonlayout,300,250);
primaryStage.setScene(scene1);
primaryStage.show();
QuestionTimer.scheduleAtFixedRate(QuestionTick,1000,1000);
}
}
So thats my code, I know most of it seems stupid but I first wanted to start programming on the timer and well it didnt work. Any kind of help would be appreciated
From the code it appears you are utilizing the java.util.Timer class. The documentation states Implementation note: All constructors start a timer thread. The the JavaFX UI should not be updated from another thread, which is what you are doing with the timer.
Rather than updating the UI directly use the Platform.runLater(Runnable) to schedule the UI tasks on the main JavaFX thread.
javafx.application.Platform.runLater(new Runnable() {
#Override
public void run() {
TimerLabel.setText(String.valueOf(Integer.valueOf(TimerLabel.getText())+1));
}
}
And please, do yourself a favor and get rid of the method chaining. It will make it easier to debug when Integer.valueOf throws an Exception.
String timer_label = TimerLabel.getText();
Integer timer_int = Integer.valueOf(timer_label);
String timer_text = String.valueOf(timer_int + 1);
TimerLabel.setText(timer_text);

Use TimeLine to trigger a void method every certain seconds JavaFX

Im trying to update a label every certain seconds, I tried using a normal Timer but since its in another thread it cannot make changes to the label, this is the Timer:
public void setTimer(Timer timer, int seconds, String userName, String content, VBox tabContent,ArrayList<Integer> countTweetsArray, Label statusLabel) {
TabContent tabContentObj = new TabContent();
timer.schedule(new TimerTask() {
#Override
public void run() {
setTweet(userName, content);
//tabContentObj.createStatusScreen(tabContent, countTweetsArray, remainingTweets);
System.out.println(content+" after "+seconds);
System.out.println("countTweetsArray: "+countTweetsArray.get(0));
statusLabel.setText(countTweetsArray.get(0).toString());
countTweetsArray.set(0, (countTweetsArray.get(0)+1));
tabContentObj.timersMap.put(userName, timer);
}
}, (seconds*1000));
}
I read that I can make periodic changes to a label using TimeLine but I cant understand how it works the keyvalues and the keyframes, Is there a way to just trigger a void method without any animation involved?
You can use the KeyFrame constructor that takes a Duration and an event handler:
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(seconds), e -> {
// code to execute here...
})
);
timeline.play();
Update: if you need a button to stop it, you can do that with
Button button = new Button("Stop");
button.setOnAction(e -> timeline.stop());

Categories

Resources