How to refresh JavaFX screen twice after sleep? - javafx

I am using JavaFX to show images on the screen. My FxScreenController code concept is like:
package my.image.concept;
import java.util.function.Consumer;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import org.springframework.stereotype.Component;
#Component
public class FxScreenController {
private ImageSenderClient imageClient;
#FXML
private ImageView imageView;
private Image image;
public FxScreenController(Image image) {
this.image = image;
}
#FXML
public void initialize(){
final ImageSubscriber imageSubscriber = new ImageSubscriber(image);
imageClient.GetImageFrom().subscribe(imageSubscriber);
}
private static class ImageSubscriber implements Consumer<Image> {
private ImageView imageFile;
public ImageSubscriber(Image newImage) {
this.imageFile.setImage(newImage);
}
#Override
public void accept(Image newImage) {
Platform.runLater(() -> imageFile.setImage(newImage));
}
}
}
I am displaying an image that comes from the Subscriber method.
In order to show image on JavaFX Screen, I am calling Platform.runLater(). The image is shown properly.
I would like to show my previous image back after a 2-seconds pause like this:
#Override
public void accept(Image newImage) {
Platform.runLater(() -> imageFile.setImage(newImage));
try {
Thread.sleep(2000); // Wait for 2 sec before updating back to prev image
} catch (InterruptedException ignored) {
//
}
Platform.runLater(() -> imageFile.setImage(prewImage));
}
However, here it doesn't show my newImage because it doesn't refresh the screen.
How can I can refresh the FX screen before calling Thread.sleep, so that I can show my newImage for 2 seconds on the FX screen?

Creating new Thread solved my problem.
#Override
public void accept(Image newImage) {
Thread timerThread = new Thread(() -> displayImage(newImage));
timerThread.start();
}
private void displayImage(Image newImage) {
Platform.runLater(() ->
imageBg.setImage(newImage)
);
try {
Thread.sleep(2000); // Wait for 2 sec before updating back to main
}
catch (InterruptedException ignored) {
}
imageBg.setImage(prevImage);
}

Related

Switching views/fxml on gluon Application

I am developing a gluon application with JavaFX but i can't understand very well how to switch scene (or view?) by clicking a button.
If i click the button "load from file" in the image below, my code should perform some tasks, and then it should change the view, loading a new fxml, that i've added to the app manager.
Screenshoot
main class that extends Application:
package com.knnapplication;
import com.knnapplication.views.ExampleView;
import com.knnapplication.views.PrimaryView;
import com.knnapplication.views.SecondaryView;
import com.gluonhq.charm.glisten.application.AppManager;
import com.gluonhq.charm.glisten.visual.Swatch;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import static com.gluonhq.charm.glisten.application.AppManager.HOME_VIEW;
public class KNNApplication extends Application {
public static final String PRIMARY_VIEW = HOME_VIEW;
public static final String SECONDARY_VIEW = "Secondary View";
public static final String EXAMPLE_VIEW = "Example View";
private final AppManager appManager = AppManager.initialize(this::postInit);
#Override
public void init() {
appManager.addViewFactory(PRIMARY_VIEW, () -> new PrimaryView().getView());
appManager.addViewFactory(SECONDARY_VIEW, () -> new SecondaryView().getView());
appManager.addViewFactory(EXAMPLE_VIEW, () -> new ExampleView().getView());
DrawerManager.buildDrawer(appManager);
}
#Override
public void start(Stage primaryStage) throws Exception {
appManager.start(primaryStage);
}
private void postInit(Scene scene) {
Swatch.BLUE.assignTo(scene);
scene.getStylesheets().add(KNNApplication.class.getResource("style.css").toExternalForm());
((Stage) scene.getWindow()).getIcons().add(new Image(KNNApplication.class.getResourceAsStream("/icon.png")));
}
public static void main(String args[]) {
launch(args);
}
}
event that handles the button click
#FXML
void LoadFile(ActionEvent event) {
//connection to server
InetAddress addr;
try {
addr = InetAddress.getByName("127.0.0.1");
} catch (UnknownHostException e) {
System.out.println(e.toString());
return;
}
Client c;
try {
c=new Client("127.0.0.1", 2025, label);
/*
HERE I SHOULD SWITCH VIEW
*/
AppManager.getInstance().switchView("EXAMPLE_VIEW");
} catch (IOException e) {
label.setText(e.toString());
System.out.println(e.toString());
return;
} catch (NumberFormatException e) {
label.setText(e.toString());
System.out.println(e.toString());
return;
} catch (ClassNotFoundException e) {
label.setText(e.toString());
System.out.println(e.toString());
return;
}
//label.setText("KNN caricato da file");
}
Searching on the web i've found this kind of method, using this line of code " AppManager.getInstance().switchView("EXAMPLE_VIEW");", but it still not work and i can't understand very well how does it works.
I hope you can help me. Thank you so much!
To load a new view you should make a view object and load the FXML/ the nodes on it. Then you can switch to that view by calling home.getAppManager().switchView("your view here"). like in the following example:
FloatingActionButton fab = new FloatingActionButton(MaterialDesignIcon.ADD.text,
e -> home.getAppManager().switchView(ADDHUB_VIEW));
fab.showOn(your view);
also you need to add this view to the app manager in init in main
appManager.addViewFactory(YOUR_VIEW, () -> new YourView().getView());

Sound stops working after pressing a key 53 times

I'm creating a game in JavaFX (something like Space Invaders) and I'm having problems with the shooting sound, particularly when I press a key quite a few times not only the sound stops being played but other sounds also stop working.
I've done some small research and it seems that this kind of problem is fairly popular and it involves releasing the MediaPlayer object/instance but I can't call that method(?).
I've tried using dispose() method but it disables the shot sound completely.
I have two classes, GameApp:
primaryStage.getScene().setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.SPACE && playable) {
Audio.playPlayerShotSound();
}
}
});
and Audio:
public class Audio {
private static Media shot = new Media(new File("resources/playerShot.wav").toURI().toString());
public static void playPlayerShotSound() {
MediaPlayer shotSound = new MediaPlayer(shot);
shotSound.setVolume(0.2);
shotSound.play();
}
I've created another class using JavaFX and the sound stops after pressing Space 64 times.
package examples;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
import javafx.scene.layout.Pane;
import java.io.File;
public class GameInst extends Application {
Pane root;
private AnimationTimer timer;
private static Media shot = new Media(new File("resources/playerShot.wav").toURI().toString());
int count = 0;
private Parent createContent() {
root = new Pane();
root.setPrefSize(500, 500);
timer = new AnimationTimer() {
#Override
public void handle(long now) {
onUpdate();
}
};
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
onUpdate();
}
};
timer.start();
return root;
}
private void onUpdate() {
}
#Override
public void start(Stage primaryStage) throws Exception {
root = new Pane();
primaryStage.setTitle("Space Invaders");
primaryStage.setScene(new Scene(createContent()));
primaryStage.getScene().setOnKeyPressed(event -> {
switch (event.getCode()) {
case SPACE:
MediaPlayer shotSound = new MediaPlayer(shot);
shotSound.setVolume(0.1);
shotSound.play();
count++;
System.out.println(count);
}
});
primaryStage.show();
}
public static void main (String[] args){
launch(args);
}
}
For what you are trying to do the AudioClip class is probably much better suited as the full blown MediaPlayer. It also probably not a good idea to initialize a new player every time you get an event.

scrollbar and alignment issue with JavaFX web view

I am using Javafx webview to embed a webpage inside a standalone Java application. The application has a small frame size of 415 by 180. I would like the top left hand corner of the webpage to appear in the top left corner of the jfxPanel. For some reason the webpage LHS appears further to the left of my jfxPanel LHS. I could tweak the page with css to indent the page but was wondering if there is an easy way to solve this in my java code?
Also, the scroll bars have disappeared. I only need the vertical scroll bar. Any ideas what I need to change so that I can have a vertical scroll bar?
In my main java program I have:
JPanel jPanel = new JPanel();
browser browser = new browser(jPanel);
browser.loadURL("http://www.url.com");
jtp.addTab("tab", jPanel);
The bowser class is:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javax.swing.*;
import java.awt.*;
import java.net.MalformedURLException;
import java.net.URL;
public class browser extends JFrame {
private final JFXPanel jfxPanel = new JFXPanel();
private WebEngine engine;
private JPanel panel;
public browser(JPanel panel) {
super();
this.panel = panel;
initComponents();
}
private void initComponents() {
createScene();
panel.add(jfxPanel, BorderLayout.CENTER);
}
private void createScene() {
Platform.runLater(new Runnable() {
#Override
public void run() {
WebView view = new WebView();
engine = view.getEngine();
jfxPanel.setScene(new Scene(view));
}
});
}
public void loadURL(final String url) {
Platform.runLater(new Runnable() {
#Override
public void run() {
String tmp = toURL(url);
if (tmp == null) {
tmp = toURL("http://" + url);
}
engine.load(tmp);
}
});
}
private static String toURL(String str) {
try {
return new URL(str).toExternalForm();
} catch (MalformedURLException exception) {
return null;
}
}
}

Handle event on CheckListView of Controls FX

I try to work with Controls FX and the Check List View component, but I have several issues on how to use it :
By default, cell are not selected when I add item in the CheckListView, how can I do to have it selected by default ? I think I have to use setCheckModel but I'm lost.
How can I handle an event when someone click on a checkBox ? I don't know what to do, because event that I handle are on the node but not on the checkBox. I don't understand how to use the eventHandler with this component.
EDIT :
Here's what I do :
departureCheckListView.setItems(myListAirport.getObservableDepartureAirtport());
departureCheckListView.getItems().addListener(new ListChangeListener<String>() {
#Override
public void onChanged(Change<? extends String> c) {
c.next();
if (c.wasAdded()) {
System.out.println(c.getAddedSubList().get(0));
//departureCheckListView.getSelectionModel().select(c.getAddedSubList().get(0));
Platform.runLater(new Runnable() {
#Override
public void run() {
departureCheckListView.getCheckModel().check(c.getAddedSubList().get(0));
}
});
}
}
});
The first item that I add is checked, but the followed items.
I don't know if this could helps, but my list is sorted.
For your first case, use a Listener on the List of Items in the CheckListView, to check if an item is added to it or nor, then, use the getSelectionModel().select(<Item>) to select it.
checkListView.getItems().addListener(new ListChangeListener<String>() {
#Override
public void onChanged(Change<? extends String> c) {
c.next();
if (c.wasAdded()) {
checkListView.getSelectionModel().select(c.getAddedSubList().get(0));
}
}
});
For the second case, use getCheckModel().getCheckedItems() to get the List of Items that have checked values. Similarly, check if a an item has been added / removed from the list.
checkListView.getCheckModel().getCheckedItems().addListener(new ListChangeListener<String>() {
#Override
public void onChanged(ListChangeListener.Change<? extends String> c) {
c.next();
if(c.wasAdded()) {
System.out.println("Item Checked : " + c.getAddedSubList().get(0));
} else if (c.wasRemoved()) {
System.out.println("Item Unchecked : " + c.getRemoved().get(0));
}
}
});
Complete MCVE - Tested with ControlsFX - 8.40.9
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.CheckListView;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
final ObservableList<String> listOfItems = FXCollections.observableArrayList();
for (int i = 0; i <= 100; i++) {
listOfItems.add("Item " + i);
}
final CheckListView<String> checkListView = new CheckListView<>(listOfItems);
// Select the first checkListView element
checkListView.getItems().addListener(new ListChangeListener<String>() {
#Override
public void onChanged(Change<? extends String> c) {
c.next();
if (c.wasAdded()) {
checkListView.getSelectionModel().select(c.getAddedSubList().get(0));
}
}
});
// On CheckBox event
checkListView.getCheckModel().getCheckedItems().addListener(new ListChangeListener<String>() {
#Override
public void onChanged(ListChangeListener.Change<? extends String> c) {
c.next();
if(c.wasAdded()) {
System.out.println("Item Checked : " + c.getAddedSubList().get(0));
} else if (c.wasRemoved()) {
System.out.println("Item Unchecked : " + c.getRemoved().get(0));
}
}
});
Button button = new Button("Add");
button.setOnAction(e -> {
checkListView.getItems().add(0, "Itachi");
checkListView.requestFocus();
});
Scene scene = new Scene(new VBox(checkListView, button), 300, 275);
primaryStage.setTitle("Welcome");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Update : For checking the new added item check-box, instead of selecting
Use :
checkListView.getCheckModel().check(c.getAddedSubList().get(0));
instead of
checkListView.getSelectionModel().select(c.getAddedSubList().get(0));
If you want it to be checked and at the same time selected, you can use both of them.

Redraw javafx scene [duplicate]

I'm trying to simulate a basic thermostat in an application GUI.
I want to update a label box value every 2 secs with the new temperature value.
For example, my intial temperature will be displayed as 68 degrees and updated to 69, to 70, etc. till 75 every 2 seconds.
This is a piece of code I wrote in Java fx. controlpanel is object of te form where the label box is present. It updates only the final value as 75. It doesnt update it every 2 secs. I have written a method pause to cause a 2 secs delay. All labels are updated with their final values but not updated every 2 secs. When I debug, I can see that the values are increased by one every 2 secs. This code is written in button onClick event
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int i=0;
Timer asd = new Timer(1000,null);
asd.setDelay(1000);
while(i < 10)
{
jTextField1.setText(Integer.toString(i));
i++;
asd.start();
}
}
To solve your task using Timer you need to implement TimerTask with your code and use Timer#scheduleAtFixedRate method to run that code repeatedly:
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
System.out.print("I would be called every 2 seconds");
}
}, 0, 2000);
Also note that calling any UI operations must be done on Swing UI thread (or FX UI thread if you are using JavaFX):
private int i = 0;
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
jTextField1.setText(Integer.toString(i++));
}
});
}
}, 0, 2000);
}
In case of JavaFX you need to update FX controls on "FX UI thread" instead of Swing one. To achieve that use javafx.application.Platform#runLater method instead of SwingUtilities
Here is an alternate solution which uses a JavaFX animation Timeline instead of a Timer.
I like this solution because the animation framework ensures that everything happens on the JavaFX application thread, so you don't need to worry about threading issues.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
public class ThermostatApp extends Application {
#Override public void start(final Stage stage) throws Exception {
final Thermostat thermostat = new Thermostat();
final TemperatureLabel temperatureLabel = new TemperatureLabel(thermostat);
VBox layout = new VBox(10);
layout.getChildren().addAll(temperatureLabel);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
class TemperatureLabel extends Label {
public TemperatureLabel(final Thermostat thermostat) {
textProperty().bind(
Bindings.format(
"%3d \u00B0F",
thermostat.temperatureProperty()
)
);
}
}
class Thermostat {
private static final Duration PROBE_FREQUENCY = Duration.seconds(2);
private final ReadOnlyIntegerWrapper temperature;
private final TemperatureProbe probe;
private final Timeline timeline;
public ReadOnlyIntegerProperty temperatureProperty() {
return temperature.getReadOnlyProperty();
}
public Thermostat() {
probe = new TemperatureProbe();
temperature = new ReadOnlyIntegerWrapper(probe.readTemperature());
timeline = new Timeline(
new KeyFrame(
Duration.ZERO,
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
temperature.set(probe.readTemperature());
}
}
),
new KeyFrame(
PROBE_FREQUENCY
)
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
}
class TemperatureProbe {
private static final Random random = new Random();
public int readTemperature() {
return 72 + random.nextInt(6);
}
}
The solution is based upon the countdown timer solution from: JavaFX: How to bind two values?
Calling Platform.runLater worked for me:
Platform.runLater(new Runnable() {
#Override
public void run() {
}
});

Resources