I'm trying to bind a DoubleProperty to a TextField and the only binding functionnal is the bidirectionnal and not the unidirectionnal binding.
My specific TextField :
public class DoubleTextField extends TextField{
public DoubleTextField() {
super();
Pattern validDoubleText = Pattern.compile("-?(\\d*)|(\\d+\\.\\d*)");
UnaryOperator<TextFormatter.Change> doubleCheck = change -> {
String newText = change.getControlNewText();
if (validDoubleText.matcher(newText).matches()) {
return change;
} else
return null;
};
this.setTextFormatter(new TextFormatter<Double>(new DoubleStringConverter(), null, doubleCheck));
}
}
public class Controller implements Initializable {
#FXML
GridPane gpTest;
#FXML
TextField tfUSDT;
Model model;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
this.model = new Model();
DoubleTextField dtfPrice = new DoubleTextField();
gpTest.add(dtfPrice, 1, 0);
tfUSDT.textProperty().bind(model.getDpActualUsdt().asString("%f"));
//dtfPrice.textProperty().bind(model.getDpActualPrice().asString("%f"));
dtfPrice.textProperty().bindBidirectional(model.getDpActualPrice(), new NumberStringConverter());
}
public void modifyUSDT(ActionEvent e) {
this.model.getDpActualUsdt().set(50);
}
public void modifyPrice(ActionEvent e) {
this.model.getDpActualPrice().set(50);
}
}
public class Model {
private DoubleProperty dpActualUsdt, dpActualPrice;
public Model() {
dpActualPrice = new SimpleDoubleProperty(0);
dpActualUsdt = new SimpleDoubleProperty(0);
}
public void setDpActualUsdt(DoubleProperty dpActualUsdt) {
this.dpActualUsdt = dpActualUsdt;
}
public void setDpActualPrice(DoubleProperty dpActualPrice) {
this.dpActualPrice = dpActualPrice;
}
public DoubleProperty getDpActualPrice() {
return dpActualPrice;
}
public DoubleProperty getDpActualUsdt() {
return dpActualUsdt;
}
}
<FlowPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<children>
<GridPane fx:id="gpTest">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<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>
<TextField fx:id="tfUSDT" />
<Button mnemonicParsing="false" onAction="#modifyUSDT" text="Modify USDT" GridPane.rowIndex="1" />
<Button mnemonicParsing="false" onAction="#modifyPrice" text="Modify Price" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
</GridPane>
</children>
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader fxml = new FXMLLoader(getClass().getResource("/resources/ihm.fxml"));
Pane mainPane = fxml.load();
Scene scene = new Scene(mainPane);
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Here are my classes
With this the code work as expected but if i comment dtfPrice.textProperty().bindBidirectional(model.getDpActualPrice(), new NumberStringConverter()) and decomment //dtfPrice.textProperty().bind(model.getDpActualPrice().asString("%f"));
He doesn't work
Thanks for everyone helping me !
Related
Thank you for your help. I've been trying to create a ribbon responsive layout like the one in Word, where the items resize one after another, and so far I haven't had much luck with it.
custom_control.fxml
<fx:root type="javafx.scene.layout.VBox" fx:id="dis" minHeight="-1.0" minWidth="-1.0" prefWidth="350.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
<GridPane fx:id="grid">
<children>
<Button text="Button" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<RadioButton text="RadioButton" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<Button onAction="#doSomething" text="Click Me" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<ComboBox GridPane.columnIndex="0" GridPane.rowIndex="1" />
<Slider GridPane.columnIndex="0" GridPane.rowIndex="2" />
<CheckBox text="CheckBox" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<TextField fx:id="textField" prefWidth="200.0" GridPane.columnIndex="0" GridPane.rowIndex="3" />
<MenuButton fx:id="mb" mnemonicParsing="false" text="" GridPane.columnIndex="1" GridPane.rowIndex="3">
<items>
<MenuItem mnemonicParsing="false" text="Action 1" />
<MenuItem mnemonicParsing="false" text="Action 2" />
</items>
</MenuButton>
</children>
<columnConstraints>
<ColumnConstraints hgrow="NEVER" maxWidth="+Infinity" minWidth="10.0" percentWidth="60.0" prefWidth="100.0" />
<ColumnConstraints hgrow="NEVER" maxWidth="+Infinity" minWidth="10.0" percentWidth="40.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 minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
</fx:root>
CustomController.java
public class CustomController extends VBox {
/**
* Initializes the controller class.
*/
#FXML private TextField textField;
#FXML private VBox dis;
#FXML private GridPane grid;
//dis.prefWidthProperty().bind(grid.widthProperty());
public CustomController() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("custom_control.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public String getText() {
return textProperty().get();
}
public void setText(String value) {
textProperty().set(value);
}
public StringProperty textProperty() {
return textField.textProperty();
}
public void adaptWidth(double width) {
/*textField.textProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("textfield changed from " + oldValue + " to " + newValue);
});*/
}
#FXML
protected void doSomething() {
System.out.println("The button was clicked!");
}
}
**RibbonJavaFX.java**
public class RibbonJavaFX extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
HBox start = new HBox();
CustomController rib1= new CustomController();
rib1.setText("Ribbon 1");
CustomController rib2 = new CustomController();
rib2.setText("Ribbon 2");
CustomController rib3 = new CustomController();
rib3.setText("Ribbon 3");
start.getChildren().add(rib1);
start.getChildren().add(rib2);
start.getChildren().add(rib3);
rib3.prefWidthProperty().bind(rib2.widthProperty());
rib2.prefWidthProperty().bind(rib1.widthProperty());
Scene scene = new Scene(start,1800,400);
stage.setScene(scene);
//scene.getStylesheets().add(getClass().getResource("custom_control.css").toExternalForm());
stage.setTitle("Custom Control");
stage.setWidth(300);
stage.setHeight(200);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
So this is how my work looks so far. I've been trying to bind the 3 controllers together, but since it has to be made dynamic and for future use, because all controllers are the same, so they have to adapt to each other. My goal is to make them resize one after another, like the Word toolbar. Can somebody please help me with this? I don't expect a full answer, a hint is maybe all I need.
Updated answer!
This answer uses a SplitPane and an HBox to accomplish the task. It grows the approapitate StackPane depending the position of the SplitPane's divider and the direction the divider is moving. You should be able to make a custom control using these ideas.
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication232 extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller
import java.net.URL;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicBoolean;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
HBox hBox;
#FXML
SplitPane splitPane;
#FXML
AnchorPane anchorPane2;
#FXML
StackPane sp1, sp2, sp3, sp4;
double orgX, orgY;
double PANE_MIN_WIDTH = 0;
double PANE_STARTING_WIDTH = 200;
double PANES_TOTAL_WIDTH = 800;
AtomicBoolean initialMove = new AtomicBoolean(true);
#Override
public void initialize(URL url, ResourceBundle rb)
{
SplitPane.Divider divider = splitPane.getDividers().get(0);
divider.positionProperty().addListener((obs, oldValue, newValue) -> {
//System.out.println("oldValue: " + oldValue.doubleValue() + " newValue: " + newValue);
//System.out.println(anchorPane2.getWidth() + " :: " + (PANES_TOTAL_WIDTH - 2 * PANE_STARTING_WIDTH));
if (newValue.doubleValue() > oldValue.doubleValue() && !initialMove.get()) {
if (anchorPane2.getWidth() <= PANES_TOTAL_WIDTH && anchorPane2.getWidth() > PANES_TOTAL_WIDTH - PANE_STARTING_WIDTH) {
sp1.setMinWidth(PANE_MIN_WIDTH);
sp1.setMaxWidth(anchorPane2.getWidth() - (PANES_TOTAL_WIDTH - PANE_STARTING_WIDTH));
}
else if (anchorPane2.getWidth() <= (PANES_TOTAL_WIDTH - PANE_STARTING_WIDTH) && anchorPane2.getWidth() > (PANES_TOTAL_WIDTH - 2 * PANE_STARTING_WIDTH)) {
sp2.setMinWidth(PANE_MIN_WIDTH);
sp2.setMaxWidth(anchorPane2.getWidth() - (PANES_TOTAL_WIDTH - 2 * PANE_STARTING_WIDTH));
}
else if (anchorPane2.getWidth() <= (PANES_TOTAL_WIDTH - 2 * PANE_STARTING_WIDTH) && anchorPane2.getWidth() > (PANES_TOTAL_WIDTH - 3 * PANE_STARTING_WIDTH)) {
sp3.setMinWidth(PANE_MIN_WIDTH);
sp3.setMaxWidth(anchorPane2.getWidth() - (PANES_TOTAL_WIDTH - 3 * PANE_STARTING_WIDTH));
}
}
else if (newValue.doubleValue() < oldValue.doubleValue()) {
if (anchorPane2.getWidth() < (PANES_TOTAL_WIDTH - 3 * PANE_STARTING_WIDTH)) {
sp4.setMaxWidth(PANE_STARTING_WIDTH);
sp4.setMinWidth(anchorPane2.getWidth());
}
else if (anchorPane2.getWidth() < (PANES_TOTAL_WIDTH - 2 * PANE_STARTING_WIDTH) && anchorPane2.getWidth() >= (PANES_TOTAL_WIDTH - 3 * PANE_STARTING_WIDTH)) {
sp3.setMaxWidth(PANE_STARTING_WIDTH);
sp3.setMinWidth(anchorPane2.getWidth() - PANE_STARTING_WIDTH);
}
else if (anchorPane2.getWidth() < (PANES_TOTAL_WIDTH - PANE_STARTING_WIDTH) && anchorPane2.getWidth() >= (PANES_TOTAL_WIDTH - 2 * PANE_STARTING_WIDTH)) {
sp2.setMaxWidth(PANE_STARTING_WIDTH);
sp2.setMinWidth(anchorPane2.getWidth() - 2 * PANE_STARTING_WIDTH);
}
else if (anchorPane2.getWidth() <= PANES_TOTAL_WIDTH && anchorPane2.getWidth() >= (PANES_TOTAL_WIDTH - PANE_STARTING_WIDTH)) {
sp1.setMaxWidth(PANE_STARTING_WIDTH);
sp1.setMinWidth(anchorPane2.getWidth() - 3 * PANE_STARTING_WIDTH);
}
}
initialMove.set(false);//Used becase of the first pass, the AnchorPane's width = 0
});
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<SplitPane fx:id="splitPane" dividerPositions="0.0" prefHeight="160.0" prefWidth="808.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication232.FXMLDocumentController">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<AnchorPane fx:id="anchorPane2" maxWidth="1.7976931348623157E308" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="800.0">
<children>
<HBox fx:id="hBox" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="203.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<StackPane fx:id="sp1" maxWidth="200.0" minWidth="0.0" prefWidth="200.0" style="-fx-background-color: green;" />
<StackPane fx:id="sp2" maxWidth="200.0" minWidth="200.0" prefWidth="200.0" style="-fx-background-color: blue;" />
<StackPane fx:id="sp3" maxWidth="200.0" minWidth="200.0" prefWidth="200.0" style="-fx-background-color: yellow;" />
<StackPane fx:id="sp4" maxWidth="200.0" minWidth="200.0" prefWidth="200.0" style="-fx-background-color: brown;" />
</children>
</HBox>
</children>
</AnchorPane>
</items>
</SplitPane>
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);
I'm building a DialogPane in FXML and I'm trying to figure out how to respond to button presses on the dialog since onAction is not a valid parameter for ButtonType. I've attached my FXML and Controller Class. There is very little documentation about DialogPane and even less about doing it in FXML so I'm not sure how to proceed.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ButtonType?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<DialogPane fx:id="loginPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="LoginController">
<content>
<GridPane hgap="5.0" vgap="5.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="300.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 minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Driver Name" />
<TextField fx:id="driverTxt" GridPane.columnIndex="1" />
<Label text="URL" GridPane.rowIndex="1" />
<TextField fx:id="urlTxt" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="Username" GridPane.rowIndex="2" />
<TextField fx:id="userTxt" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="Password" GridPane.rowIndex="3" />
<TextField fx:id="passTxt" GridPane.columnIndex="1" GridPane.rowIndex="3" />
</children>
</GridPane>
</content>
<buttonTypes>
<ButtonType fx:id="loginButton" text="Login" />
<ButtonType fx:id="cancelBtn" text="Cancel" />
</buttonTypes>
</DialogPane>
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.ButtonType;
import javafx.scene.control.DialogPane;
import javafx.scene.control.TextField;
public class LoginController {
#FXML
DialogPane loginPane;
#FXML
TextField driverTxt;
#FXML
TextField urlTxt;
#FXML
TextField userTxt;
#FXML
TextField passTxt;
#FXML
ButtonType loginButton;
#FXML
private void loginButtonAction(){
// How do I do something here
}
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
}
}
Typically you wouldn't need to do this. You would usually show a DialogPane in a Dialog<ButtonType> calling its showAndWait() method, which returns an Optional<ButtonType> representing the button pressed (if any). So normal usage would be something like
public class LoginController {
public static final ButtonType LOGIN = new ButtonType("Login");
#FXML
DialogPane loginPane;
#FXML
TextField driverTxt;
#FXML
TextField urlTxt;
#FXML
TextField userTxt;
#FXML
TextField passTxt;
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
}
public String getDriver() {
return driverTxt.getText();
}
public String getUrl() {
return urlTxt.getText();
}
public String getUser() {
return userTxt.getText();
}
public String getPass() {
return pass.getText();
}
}
and make the following changes to your FXML file:
<buttonTypes>
<LoginController fx:constant="LOGIN" />
<ButtonType fx:constant="CANCEL" />
</buttonTypes>
Then you would use this with:
Dialog<ButtonType> dialog = new Dialog<>();
FXMLLoader dialogLoader = new FXMLLoader(getClass().getResource("Login.fxml"));
dialog.setDialogPane(dialogLoader.load());
LoginController controller = dialogLoader.getController();
dialog.showAndWait().filter(LoginController.LOGIN::equals)
.ifPresent(button -> {
String driver = controller.getDriver();
// etc etc
// process login...
});
As an alternative to exposing the text from the text fields, you could define a processLogin() method in the controller itself that read the text fields and did whatever you need to do:
public class LoginController {
public static final ButtonType LOGIN = new ButtonType("Login");
#FXML
DialogPane loginPane;
#FXML
TextField driverTxt;
#FXML
TextField urlTxt;
#FXML
TextField userTxt;
#FXML
TextField passTxt;
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
}
public void processLogin() {
String driver = driverTxt.getText();
// etc...
// process login...
}
}
then just do
// ...
dialog.showAndWait().filter(LoginController.LOGIN::equals)
.ifPresent(button -> controller.processLogin());
If you really need to register an onAction handler with the login button, do it in the initialize() method in the controller:
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
Button login = (Button) loginPane.lookupButton(loginButton);
login.setOnAction(e -> { /* ... */ });
}
but this is really against the intended use of the dialog pane API.
One final alternative would be to override the createButton method in DialogPane. To do this, you'd need a subclass of DialogPane, which would mean using the FXML custom component pattern.
So this would look something like:
public class LoginPane extends DialogPane {
public static final ButtonType LOGIN = new ButtonType("Login");
#FXML
TextField driverTxt;
#FXML
TextField urlTxt;
#FXML
TextField userTxt;
#FXML
TextField passTxt;
public LoginPane() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("LoginPane.fxml"));
loader.setRoot(this);
loader.setController(this);
loader.load();
} catch (IOException exc) {
// bad if you get here...
throw new UncheckedIOException(exc);
}
}
#Override
public Node createButton(ButtonType buttonType) {
Node button = super.createButton(buttonType);
if (buttonType == LOGIN) {
((Button) button).setOnAction(e -> processLogin());
}
return button ;
}
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
}
public void processLogin() {
String driver = driverTxt.getText();
// etc...
// process login...
}
}
and the FXML would then look like
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ButtonType?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<fx:root type="DialogPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
<content>
<GridPane hgap="5.0" vgap="5.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="300.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 minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Driver Name" />
<TextField fx:id="driverTxt" GridPane.columnIndex="1" />
<Label text="URL" GridPane.rowIndex="1" />
<TextField fx:id="urlTxt" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="Username" GridPane.rowIndex="2" />
<TextField fx:id="userTxt" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="Password" GridPane.rowIndex="3" />
<TextField fx:id="passTxt" GridPane.columnIndex="1" GridPane.rowIndex="3" />
</children>
</GridPane>
</content>
<buttonTypes>
<LoginPane fx:constant="LOGIN" />
<ButtonType fx:constant="CANCEL" />
</buttonTypes>
</fx:root>
You would use this version with
Dialog dialog = new Dialog();
dialog.setDialogPane(new LoginPane());
dialog.showAndWait();
so if you are looking to encapsulate as much as possible into the login pane and fxml, this is probably the cleanest option.
Note that the usage of DialogPane is fairly completely documented in the API docs for Dialog and DialogPane.
I have 2 FXML files, with 2 controllers. Each FXML file has its own controller. First scene has a button that takes you to the 2nd scene. Now my question is, how can I start executing the commands "label.setText ("Hello There \n");" in my 2nd controller without doing any additional ActionEvent other than the button click from scene1 to take me to scene2 .
MainController:
public void startNewScene() throws IOException {
root = FXMLLoader.load(getClass().getResource("FXML.fxml"));
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
2nd controller as per the example in the comment below from " ItachiUchiha"
public class MyController implements Initializable {
#FXML private Button button;
#FXML private Label label;
#FXML
#Override
public void initialize(URL location, ResourceBundle resources) {
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
label.setText("hello there, you clicked me");
}
}
);
}
}
Now the 2nd scene gets uploaded but nothing is outputted to the screen or to the console. But when I click on the button on scene everything works.
So I tried to set "onMouseClicked" and "onAction" to initialize and/or handle for both label and button but I get errors "handler method not found." in the FXML file
FXML file:
<GridPane 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="ciacv.MyController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<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>
<Label fx:id="label" />
<Button fx:id="button" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" />
</children>
</GridPane>
The fx:id of your button in your fxml is butn1 where as in your controller you have are trying to inject the Button with declaration
#FXML private Button button;
You need to use the same fx:id that you have declared in the fxml to correctly inject the object into its reference i.e.
#FXML private Button butn1;
Edit - As per the comments
You need to set the Scene on the Stage
public void startNewScene() throws IOException {
root = FXMLLoader.load(getClass().getResource("FXML.fxml"));
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
Edit - 2
No you have not written anything to set the text in the label when the fxml gets loaded.
You have just written
label.setText("hello there, you clicked me");`
inside the button's action. If you need to set the text in label at load, you can write the following statement inside intialize()
#Override
public void initialize(URL location, ResourceBundle resources) {
// Set Label's text intialize() is called
label.setText("Helll There...");`
// Set Label's text on Button's action
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
label.setText("hello there, you clicked me");
}
}
}
In my FXML I created a gridpane. Now I want to add dynamic element (Like button, textfield)
by java code (not by FXML), while I am trying to do so, I am getting error. Please Help.
my FXML:
<AnchorPane fx:controller="tableview.TableViewSample" id="AnchorPane" maxHeight="- Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml">
<children>
<GridPane fx:id="greadpane" layoutX="0.0" layoutY="0.0" prefHeight="400.0" prefWidth="600.0">
<columnConstraints>
<ColumnConstraints fx:id="col0" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints fx:id="row0" 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>
</GridPane>
</children>
</AnchorPane>
My Java Code:
public class TableViewSample extends Application {
#FXML private GridPane greadpane;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws IOException {
Pane myPane = (Pane)FXMLLoader.load(getClass().getResource
("tabviewexamlpe.fxml"));
Scene scene = new Scene(myPane);
stage.setTitle("Table View ");
stage.setWidth(450);
stage.setHeight(500);
stage.setScene(scene);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
greadpane.add(label, 0, 0);
stage.show();
}
}
You get a null pointer cause you try to do operation before stage.show() so fxml are not yet initialized. don't do dirty things and put your greadPane.add on a separate controller
public class Controller implements Initializable {
#FXML
private GridPane greadpane;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
greadpane.add(label, 0, 0);
}
}
and assign your fxml to this controler. and it's will be ok
I ran into the same problem and used Agonist_ advice, however instead of separating the gridPane into a new controller i just created a thread that runs 10ms later to execute the code which is waiting for stage.show().
public GameController(Game game) {
game.addObserver(this);
new Thread() {
#Override
public void run() {
try {
Thread.sleep(10);
Platform.runLater(() -> {
game.startBeginnerRound();
});
} catch (InterruptedException ex) {
Logger.getLogger(GameController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}.start();
}
In this example the gridPane is updated when the observable notifies it, in this case when game.startBeginnerRound() is executed.