after restore from minimize WebView become disabled in javafx - javafx

My problem is when I am restoring minimized javafx application from windows taskbar WebView become disabled.
After resize stage it becomes enabled.
Main java Code:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
AnchorPane root = new AnchorPane();
FXMLLoader loader = new FXMLLoader(getClass().getResource("Browser.fxml"));
root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Shivam Jewels ERP");
primaryStage.setMaximized(true);
primaryStage.show();
primaryStage.iconifiedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (t1) {
System.out.println("minimized:");
}
}
});
primaryStage.maximizedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
System.out.println("maximized:");
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
My JavaFX Code Is:
public class BrowserController implements Initializable {
#FXML
private WebView browser;
#FXML
private GridPane grdPn;
#Override
public void initialize(URL location, ResourceBundle resources) {
final WebEngine webEngine = browser.getEngine();
browser.getEngine().setUserStyleSheetLocation(getClass().getResource("application.css").toExternalForm());
browser.setContextMenuEnabled(false);
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
webEngine.load("http://192.168.2.6:4200");
}
}
This is my CSS Code:
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
-webkit-border-radius: 10px;
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
-webkit-border-radius: 10px;
border-radius: 10px;
background: rgba(0,0,0,0.5);
}
::-webkit-scrollbar-thumb:window-inactive {
background: rgba(0,0,0,0.1);
}
This is my FXML Code:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.web.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="382.0" prefWidth="353.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.BrowserController">
<children>
<GridPane fx:id="grdPn" layoutX="57.0" layoutY="135.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" />
</columnConstraints>
<rowConstraints>
<RowConstraints fillHeight="false" valignment="CENTER" vgrow="SOMETIMES" />
<RowConstraints vgrow="SOMETIMES" />
</rowConstraints>
<children>
<WebView fx:id="browser" minHeight="-1.0" minWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.valignment="CENTER" GridPane.vgrow="ALWAYS" />
</children>
</GridPane>
</children>`enter code here`
</AnchorPane>

This is actually a JDK bug reported multiple times here, here and here. The behavior you described happens when satge is maximized.
This bug was scheduled to be fixed in JDK9 but according to this is also going to be fixed in JDK8u122:
Approved to backport to 8u-dev for 8u122.
JDK 8u122 is planned to be released in January 2017 so it won't be long before we can use it but for now you can download and use early access release from JDK 8u122 early access page.
I myself downloaded it and tested the code and fortunately problem no longer exists.

Related

JavaFX (Scene builder) arrow keys OnKeyPressed

I'm in the middle of a school project, but I'm stuck...
In Scene Builder there is an OnKeyPressed and an OnKeyReleased 'method', and when I assign my own method (See code further down) I want to check if I'm pressing the left or right arrow key.
The problem is, OnKeyReleased works fine, but the OnKeyPressed only works when I hold down shift!
Does anyone know the cause of this issue? and if so, how it can be fixed/worked around?
(I'm on a MacBook, could this have anything to do with it?)
Thanks :)
Method for OnKeyPressed:
#FXML
private void Input(KeyEvent e)
{
if(e.getCode().equals(KeyCode.LEFT))
{
System.out.println("Pressed left arrow key");
}
}
FullCode:
package seasweeper.seasweeper;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public class SecondaryController implements Initializable {
#FXML
private Label Label_Character;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
private void switchToPrimary() throws IOException
{
App.setRoot("primary");
}
#FXML
private void Input(KeyEvent e)
{
if(e.getCode().equals(KeyCode.LEFT))
{
System.out.println("Pressed left");
}
System.out.println("KeyCode: " + e.getCode());
}
}
FXML code:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" onKeyPressed="#moveLeft" onKeyReleased="#moveLeft" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seasweeper.seasweeper.PacificController">
<children>
<Button layoutX="422.0" layoutY="64.0" mnemonicParsing="false" onAction="#switchToPrimary" prefHeight="27.0" prefWidth="164.0" text="Switch to Primary" />
<Label layoutX="489.0" layoutY="31.0" text="Pacific" />
<Label fx:id="Label_Character" layoutX="273.0" layoutY="192.0" text="Character" />
</children>
</AnchorPane>
Other Scene Controller:
package seasweeper.seasweeper;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
public class PrimaryController {
#FXML
private Button Button_Arctic;
#FXML
private Button Button_Southern;
#FXML
private Button Button_Pacific;
#FXML
private Button Button_Atlantic;
#FXML
private Button Button_Indian;
#FXML
private Button Button_Pacific1;
#FXML
private void switchToPacific() throws IOException {
App.setRoot("pacific");
}
#FXML
private void switchToSouthern() throws IOException {
App.setRoot("southern");
}
#FXML
private void switchToAtlantic() throws IOException {
App.setRoot("atlantic");
}
#FXML
private void switchToIndian() throws IOException {
App.setRoot("indian");
}
#FXML
private void switchToArctic() throws IOException {
App.setRoot("arctic");
}
}
Other FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seasweeper.seasweeper.PrimaryController">
<children>
<Button fx:id="Button_Arctic" layoutX="226.0" layoutY="14.0" mnemonicParsing="false" onAction="#switchToArctic" prefHeight="77.0" prefWidth="148.0" text="Arctic" />
<Button fx:id="Button_Southern" layoutX="226.0" layoutY="309.0" mnemonicParsing="false" onAction="#switchToSouthern" prefHeight="77.0" prefWidth="148.0" text="Southern" />
<Button fx:id="Button_Pacific" layoutX="14.0" layoutY="162.0" mnemonicParsing="false" onAction="#switchToPacific" prefHeight="77.0" prefWidth="148.0" text="Pacific" />
<Button fx:id="Button_Atlantic" layoutX="226.0" layoutY="162.0" mnemonicParsing="false" onAction="#switchToAtlantic" prefHeight="77.0" prefWidth="148.0" text="Atlantic" />
<Button fx:id="Button_Indian" layoutX="438.0" layoutY="162.0" mnemonicParsing="false" onAction="#switchToIndian" prefHeight="77.0" prefWidth="148.0" text="Indian" />
<Button fx:id="Button_Shop" layoutX="556.0" layoutY="14.0" mnemonicParsing="false" prefHeight="30.0" prefWidth="30.0" text="\$" textFill="#009712" />
</children>
</AnchorPane>
On the same issue I changed OnKeyPressed from AnchorPane to some Button on that AnchorPane and OnKeyPressed works fine with arrows :)
#Override
public void initialize(URL location, ResourceBundle resources) {
someButton.addEventHandler(KeyEvent.KEY_PRESSED,
new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
switch (event.getCode().toString()) {
// your logic here
}
event.consume();
}
});
}
This one is a little tricky, but still relatively simple:
Node node = null;
node.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.KP_UP) { // KeyPressed_UP
// Do something
}
if (event.getCode() == KeyCode.KP_DOWN) { // KeyPressed_DOWN
// Do something
}
if (event.getCode() == KeyCode.KP_LEFT) { // KeyPressed_LEFT
// Do something
}
if (event.getCode() == KeyCode.KP_RIGHT) { // KeyPressed_RIGHT
// Do something
}
}
});
Remember to change "node" with your object (it could be a Scene, AnchorPane -or any kind of pane-, buttons, etc).

Make checkbox enabled in TableView in javaFx with fxml

I created a table view, linked it with my model and put into fxml like this:
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("table.fxml"));
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
public class Controller {
#FXML
private javafx.scene.control.TableView<TableView> tableView;
#FXML
public void initialize() {
ObservableList<TableView> data = tableView.getItems();
data.add(new TableView("Item1", true));
data.add(new TableView("Item2", false));
}
}
TableView.java
public class TableView {
private SimpleStringProperty text;
private SimpleBooleanProperty check;
TableView(String text, boolean check) {
this.text = new SimpleStringProperty(text);
this.check = new SimpleBooleanProperty(check);
}
public boolean getCheck() {
return check.get();
}
public SimpleBooleanProperty checkProperty() {
return check;
}
public void setCheck(boolean check) {
this.check.set(check);
}
public String getText() {
return text.get();
}
public SimpleStringProperty textProperty() {
return text;
}
public void setText(String text) {
this.text.set(text);
}
}
CheckBoxTableCellFactory.java
public class CheckBoxTableCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
public TableCell<S, T> call(TableColumn<S, T> param) {
return new CheckBoxTableCell<>();
}
}
table.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import table.CheckBoxTableCellFactory?>
<VBox prefHeight="600.0" prefWidth="900.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="table.Controller">
<SplitPane focusTraversable="true" VBox.vgrow="ALWAYS">
<Pane prefHeight="200.0" prefWidth="200.0">
<TableView fx:id="tableView" layoutX="6.0" layoutY="4.0" prefHeight="406.0" prefWidth="694.0">
<columns>
<TableColumn prefWidth="75.0" text="Check">
<cellValueFactory>
<PropertyValueFactory property="check" />
</cellValueFactory>
<cellFactory>
<CheckBoxTableCellFactory />
</cellFactory>
</TableColumn>
<TableColumn prefWidth="200.0" text="Text">
<cellValueFactory>
<PropertyValueFactory property="text"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</Pane>
</SplitPane>
</VBox>
I looks good -
screenshot
but checkboxes are disabled - I cannot check/un-check them. When I click on them, they don't change their state (checked/un-checked). How to fix it?
By default, a TableView is not editable meaning any custom controls within the TableView are also disabled.
You can enable editing by either updating your FXML definition of the TableView to include editable="true" or in your controller with tableView.setEditable(true);.

Progress bar not updating in javaFX

I'm facing the following problem: My progressBar is not updating in JavaFx.
I have written the following code:
public class FXMLDocumentController implements Initializable {
static final int max = 1000000;
Task task = new Task<Void>() {
#Override public Void call() {
for (int i = 1; i <= max; i++) {
updateProgress(i, max);
}
return null;
}
};
#FXML
public AnchorPane root;
#FXML
public ProgressBar progressWater;
#FXML
public ProgressBar levelGas;
#Override
public void initialize(URL url, ResourceBundle rb) {
if (!Main.isSplashLoaded) {
loadSplashScreen();
}
}
private void loadSplashScreen() {
try {
Main.isSplashLoaded = true;
StackPane pane = FXMLLoader.load(getClass().getResource(("SplashFXML.fxml")));
root.getChildren().setAll(pane);
FadeTransition fadeIn = new FadeTransition(Duration.seconds(3), pane);
fadeIn.setFromValue(0);
fadeIn.setToValue(1);
fadeIn.setCycleCount(1);
FadeTransition fadeOut = new FadeTransition(Duration.seconds(3), pane);
fadeOut.setFromValue(1);
fadeOut.setToValue(0);
fadeOut.setCycleCount(1);
fadeIn.play();
fadeIn.setOnFinished((e) -> {
fadeOut.play();
});
fadeOut.setOnFinished((e) -> {
AnchorPane paneMain;
try {
paneMain = FXMLLoader.load(getClass().getResource("/sample/FXMLDocument.fxml"));
root.getChildren().setAll(paneMain);
paneMain.setOpacity(0);
FadeTransition fadeInmain = new FadeTransition(Duration.seconds(3), paneMain);
fadeInmain.setFromValue(0);
fadeInmain.setToValue(1);
fadeInmain.play();
fadeInmain.setOnFinished((ex)-> {
// progressWater.setProgress(0.5);
double maxlev = progressWater.getPrefWidth();
progressWater.progressProperty().bind(fadeInmain.fromValueProperty());
/**
* Action goes here
*/
new Thread(){
public void run() {
for (double i = 0.0; i < maxlev; i++){
final double step = i;
Platform.runLater(() ->
progressWater.setProgress( step / 10 ));
System.out.printf("Complete: %02.2f%n", step /10);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}.start();
progressWater.progressProperty().unbind();
/**
* Action ends here
*/
});
} catch (IOException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
});
} catch (IOException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
When executing the code, the thread updates, and it prints in the console the completion, however, the progressWater bar does not update in the UI.
Below is my FXML document:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.effect.GaussianBlur?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="MainPane" fx:id="root" prefHeight="322.0" prefWidth="232.0" style="-fx-background-color: #000000;" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.FXMLDocumentController">
<children>
<ImageView fitHeight="322.0" fitWidth="232.0" pickOnBounds="true">
<image>
<Image url="#background.jpg" />
</image>
</ImageView>
<ProgressBar fx:id="progressWater" layoutX="-80.0" layoutY="170.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="5.0" prefWidth="200.0" progress="1.0" rotate="90.0">
<effect>
<GaussianBlur radius="2.0" />
</effect>
</ProgressBar>
<ProgressBar fx:id="levelGas" layoutX="113.0" layoutY="170.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="5.0" prefWidth="200.0" progress="0.0" rotate="90.0">
<effect>
<GaussianBlur radius="2.0" />
</effect>
</ProgressBar>
</children>
</AnchorPane>
Any ideas about what am I doing wrong?
It occurs to me immediately that you are before running the thread that spins and updates the progress..
progressWater.progressProperty().bind(fadeInmain.fromValueProperty());
You're binding the progress property to something else entirely that you aren't updating seemingly, have you tried removing this binding to allow the property to float on its own and not be tied to the propagating events from the fadein property?

StringProperty and Switch Statement Javafx

My brain is about to explode. I am pretty new to java, so far self-taught but I really want to get this code to work. I am also not the best at making sense of the java docs.
I am trying to create a gameloop where the user can freely traverse the switch statement by clicking the buttons.
However, previously, when my gameloop method was called, it ran the code with the current settings without letting me use the buttons to traverse the switch statement.
I think this was because my "choice" variable did not have an action listener attached to it (because a listener will make the program sit on the item, waiting for something to happen?). Unfortunately, my choice variable was a String variable and you cannot attach an actionlistener to a String variable.
Now I tried using the StringProperty class to create a string object to be able to attach an action listener to it.
But now I don't even know how to use StringProperty with switch statement and I just feel like I am on the wrong track.
Does it make sense what I am trying to accomplish?
Can anybody help me create a gameloop where the user can freely traverse the switch statement by clicking the buttons?
♥♥♥!
Here is my code:
Controller:
package sample;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
import static java.lang.System.out;
public class Controller implements Initializable {
public String question;
public StringProperty choice = new SimpleStringProperty(this, "choice", "");
// this code starts when the XML file is loaded.
#Override
public void initialize(URL location, ResourceBundle resources) {
question = "0";
gameloop();
} public String getChoice() {
return choice.get();
}
public StringProperty choiceProperty() {
return choice;
}
public void setChoice(String choice) {
this.choice.set(choice);
}
// public String choice = "";
public Controller() {
}
// Here are the buttons trying to add an actionlistener to the choice object.
// Maybe wrong path to go
#FXML
public void button1() {
choiceProperty().addListener((v, oldValue, newValue) ->{setChoice("c1");});
}
#FXML
public void button2() {
choiceProperty().addListener((v, oldValue, newValue) ->{setChoice("c2");});
out.println(choice); //think this prints out where it is stored in the memory
}
#FXML
public void button3() {
choiceProperty().addListener((v, oldValue, newValue) ->{setChoice("c3");});
out.println(choice);
}
//this block of code does not work because of the switch (choice) statement.
//Where "c1","c2","c3" have incompatible a type error.
public void gameloop() {
switch (question) {
case "0":
switch (choice) {
case "c1":
out.println("you chose 1");
question = "1";
break;
case "c2":
out.println("you chose 2");
break;
case "c3":
out.println("you chose 3");
break;
}
case "1":
switch (choice) {
case "c1":
out.println("you chose a");
question = "0";
break;
case "c2":
out.println("you chose b");
break;
case "c3":
out.println("you chose c");
break;
}
}
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<BorderPane layoutX="163.0" layoutY="82.0" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: black;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<bottom>
<GridPane BorderPane.alignment="CENTER">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button fx:id="button1" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button1" onMouseClicked="#button1" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" />
<Button fx:id="button2" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button2" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" GridPane.rowIndex="1" />
<Button fx:id="button3" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button3" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" GridPane.rowIndex="2" />
</children>
</GridPane>
</bottom>
<center>
<TextArea fx:id="textArea" editable="false" prefHeight="200.0" prefWidth="200.0" promptText="..." style="-fx-border-color: grey;" wrapText="true" BorderPane.alignment="CENTER" />
</center>
<top>
<Label fx:id="label" text="Chapter 1" textFill="WHITE" BorderPane.alignment="CENTER" />
</top>
<left>
<ImageView fx:id="image1" fitHeight="75.0" fitWidth="75.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
</left>
<right>
<ImageView fx:id="image2" fitHeight="75.0" fitWidth="75.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
</right></BorderPane>
</children>
</AnchorPane>
Main:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 800, 500));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Registering listeners in the button event handlers is nonsense. Using listeners to modify the property they listen to is even worse. Passing a property as argument for a switch will not even compile.
In this case the smallest modification to make this work would be registering a single listener in the initialize method, calling the gameloop method from this listener and using the value stored in the property as argument for the switch.
#Override
public void initialize(URL location, ResourceBundle resources) {
question = "0";
gameloop();
choiceProperty().addListener((observable, oldValue, newValue) -> gameloop());
}
...
#FXML
private void button1() {
setChoice("c1");
}
...
public void gameloop() {
switch (question) {
case "0":
switch (choice.get()) {
...
}
...
}
}
Note however you'll get a LOT of repeated code this way. Better come up with a data structure to store the game data instead of hardcoding everything.
E.g.
public class GameData {
private final ReadOnlyObjectWrapper<Question> question;
public ReadOnlyObjectProperty<Question> questionProperty() {
return question.getReadOnlyProperty();
}
public Question getQuestion() {
return question.get();
}
private final Map<Integer, Question> questions;
public Map<Integer, Question> getQuestions() {
return questions;
}
public GameData(int initialQuestion, Map<Integer, Question> questions) {
this.questions = questions;
this.question = new ReadOnlyObjectWrapper(questions.get(initialQuestion));
}
public void activateChoice(Choice choice) {
question.set(questions.get(choice.getNextQuestion()));
}
}
public class Question {
private final String text;
public String getText() {
return text;
}
public Question(String text, Choice... choices) {
this.text = text;
this.choices = Collections.unmodifiableList(new ArrayList(Arrays.asList(choices)));
}
private final List<Choice> choices;
public List<Choice> getChoices() {
return choices;
}
}
public class Choice {
private final String text;
private final int nextQuestion;
public Choice(int nextQuestion, String text) {
this.text = text;
this.nextQuestion = nextQuestion;
}
public String getText() {
return text;
}
public int getNextQuestion() {
return nextQuestion;
}
}
public class Controller {
private GameData gameData;
#FXML
private Button button1, button2, button3;
private Button[] buttons;
#FXML
private void initialize() {
buttons = new Button[]{button1, button2, button3};
}
public void setGameData(GameData gameData) {
this.gameData = gameData;
gameData.questionProperty().addListener((observable, oldValue, newValue) -> setQuestion(newValue));
setQuestion(gameData.getQuestion());
}
private void setQuestion(Question question) {
System.out.println(question.getText());
for (Button b : buttons) {
b.setVisible(false);
}
for (int i = 0, max = Math.min(buttons.length, question.getChoices().size()); i < max; i++) {
Button b = buttons[i];
Choice c = question.getChoices().get(i);
b.setUserData(c);
b.setText(c.getText());
b.setVisible(true);
}
}
#FXML
private void button(ActionEvent evt) {
Node source = (Node) evt.getSource();
Choice choice = (Choice) source.getUserData();
out.println("You've chosen " + choice.getText());
gameData.activateChoice(choice);
}
}
...
<Button fx:id="button1" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" />
<Button fx:id="button2" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" GridPane.rowIndex="1" />
<Button fx:id="button3" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" GridPane.rowIndex="2" />
...
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
Map<Integer, Question> questions = new HashMap<>();
questions.put(0, new Question("0",
new Choice(1, "1"),
new Choice(0, "2"),
new Choice(0, "3")
));
questions.put(1, new Question("1",
new Choice(0, "a"),
new Choice(1, "b"),
new Choice(1, "c")
));
GameData data = new GameData(0, questions);
loader.<Controller>getController().setGameData(data);

Bug in JavaFX by binding with middle man onto slider value property?

I stumbled upon a problem using a slider in JavaFX.
I create a fxml-file with a Slider and add a controller to it.
Inside the controller I have a DoubleProperty, which binds to the Slider's valueProperty. Then, when I want to bind to this property from somewhere else, I bind to the property, which is inside the controller (some kind of a middle-man approach see Figure).
But when I do so, it does not work properly.
When I use the slider, the values get updated accordingly for a while, but when I wiggle it around, at some point it seems to stop updating the binding and refuses to do so again even after releasing and pressing it again.
When I delete the middle-man property in the controller and just pipe through the valueProperty from the slider directly, it is working.
Program:
Main.java
public class Main extends Application{
private MainController controller;
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("main.fxml"));
Parent root = loader.load();
controller = loader.getController();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
showSlider();
}
private void showSlider() {
SliderShower sliderShower = new SliderShower();
sliderShower.show();
sliderShower.getSliderValueProp().addListener(((observable, oldValue, newValue) -> {
controller.setText(Double.toString((double)newValue));
}));
}
public static void main(String[] args) {
launch(args);
}
}
main.fxml
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane prefHeight="100.0" prefWidth="100.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sliderBug.main.MainController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label fx:id="label" text="Label" />
</children>
</GridPane>
MainController.java
public class MainController {
#FXML
private Label label;
public void setText(String text) {
label.setText(text);
}
}
SliderShower.java
public class SliderShower {
private Parent root;
private SliderShowerController controller;
private Stage stage;
private DoubleProperty sliderValueProp;
public SliderShower() {
sliderValueProp = new SimpleDoubleProperty(0);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("sliderShower.fxml"));
try {
root = loader.load();
controller = loader.getController();
stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setScene(new Scene(root));
sliderValueProp.bind(controller.getSliderValueProp());
} catch (IOException e) {
e.printStackTrace();
}
}
public DoubleProperty getSliderValueProp() {
return sliderValueProp; // This does not work
// return controller.getSliderValueProp(); // This would work
}
public void show() {
stage.show();
}
}
sliderShowerController.java
public class SliderShowerController {
#FXML
private Slider sliderUI;
DoubleProperty getSliderValueProp() {
return sliderUI.valueProperty();
}
}
sliderShower.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane prefHeight="100.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sliderBug.sliderShower.SliderShowerController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Slider fx:id="sliderUI" max="200.0" min="1.0" />
</children>
</GridPane>
Here is a link to a repository depicting the problem:
https://github.com/Chrisss50/sliderBug
Am I doing something wrong or is this just a bug?
Greetings
Bindings in JavaFX use weak listeners under the hood. This is intended to prevent memory leaks, but has the side effect that if the properties to which something is bound go out of scope, the binding will not prevent them being garbage collected and will cease to work if they are. See this question and this blog post.
You can verify this is the issue by adding
root.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
System.out.println("GC");
System.gc();
}
});
to the SliderShower constructor. After doing that, double-clicking near the slider will force garbage collection, and the binding will immediately cease to work.
In your case, Main instantiates SliderShower as a local variable in the showSlider method, and consequently it goes out of scope as soon as the method completes. One (somewhat unnatural) fix is to forcibly retain a reference to the SliderShower:
package sliderBug.main;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import sliderBug.sliderShower.SliderShower;
/**
* Created by Christopher Juerges on 16/02/17.
*/
public class Main extends Application{
private MainController controller;
private SliderShower sliderShower ;
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("main.fxml"));
Parent root = loader.load();
controller = loader.getController();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
showSlider();
}
private void showSlider() {
sliderShower = new SliderShower();
sliderShower.show();
sliderShower.getSliderValueProp().addListener(((observable, oldValue, newValue) -> {
controller.setText(Double.toString((double)newValue));
}));
}
public static void main(String[] args) {
launch(args);
}
}
Another fix is to use a listener instead of the binding:
public SliderShower() {
sliderValueProp = new SimpleDoubleProperty(0);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("sliderShower.fxml"));
try {
root = loader.load();
root.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
System.out.println("GC");
System.gc();
}
});
controller = loader.getController();
stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setScene(new Scene(root));
// sliderValueProp.bind(controller.getSliderValueProp());
controller.getSliderValueProp().addListener((obs, oldValue, newValue) ->
sliderValueProp.set(newValue.doubleValue()));
} catch (IOException e) {
e.printStackTrace();
}
}

Resources