Inherited member becomes null after initialize - java

I made a simple FX program to replicate the problem I am seeing. I have a TestController that extends AbstractBar which extends AbstractFoo. After calling the initialize() method, a simple GUI displays with a button. I am expecting a "Hello World!" message to print to the console when the button is pressed, but I am getting a null value instead. Why does this happen and how do I fix this?
Expected result: Hello World!
Button result: null
Button result: null
Main.java
package com.test.main;
import com.test.gui.TestController;
import javafx.embed.swing.JFXPanel;
public class Main {
private static TestController controller;
public static void main(String[] args) {
String fxml = "/gui/Simple.fxml";
String content = "Hello World!";
new JFXPanel();
controller = new TestController();
controller.initialize(fxml, content);
System.out.println("Expected result: " + controller.getContent());
}
}
TestController.java
package com.test.gui;
import javafx.fxml.FXML;
public class TestController extends AbstractBar {
#FXML
private void testButton() {
System.out.println("Button result: " + super.getContent());
}
}
AbstractBar.java
package com.test.gui;
public abstract class AbstractBar extends AbstractFoo {
#Override
public void initialize(String fxml, String content) {
// Do stuff.
super.initialize(fxml, content);
}
}
AbstractFoo.java
package com.test.gui;
import java.io.IOException;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public abstract class AbstractFoo {
protected String fxml;
protected String content;
protected Stage stage;
protected Parent root;
public void initialize(String fxml, String content) {
this.fxml = fxml;
this.content = content;
Platform.runLater(() -> {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(this.getClass().getResource(fxml));
try {
this.root = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
this.stage = new Stage();
this.stage.setScene(new Scene(root));
this.stage.show();
});
}
public String getContent() {
return this.content;
}
}
Simple.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Button?>
<BorderPane fx:id="root" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.test.gui.TestController">
<center>
<Button fx:id="button" text="Click me!" onAction="#testButton" />
</center>
</BorderPane>

As pointed out by the comments, it is correct that the FXMLLoader is not using the same controller instance I am expecting. The simple fix is to remove the fx:controller attribute from the fxml file. and add loader.setController(this) to AbstractFoo. Now everything works as expected, what a big oversight...
com.test.gui.TestController#568bf312
Expected result: Hello World!
com.test.gui.TestController#568bf312
Button result: Hello World!
Simple.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Button?>
<BorderPane fx:id="root" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" >
<center>
<Button fx:id="button" text="Click me!" onAction="#testButton" />
</center>
</BorderPane>
AbstractFoo.java
package com.test.gui;
import java.io.IOException;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public abstract class AbstractFoo {
protected String fxml;
protected String content;
protected Stage stage;
protected Parent root;
public void initialize(String fxml, String content) {
this.fxml = fxml;
this.content = content;
Platform.runLater(() -> {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(this.getClass().getResource(fxml));
loader.setController(this);
try {
this.root = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
this.stage = new Stage();
this.stage.setScene(new Scene(root));
this.stage.show();
});
}
public String getContent() {
return this.content;
}
}

Related

Label not displaying text even after change (JavaFX)

I'm working on a project and I need to display the information of a Medicine object from a ListView to another Scene.
The user would select a Medicine from the ListView and press the Button to see it's details in the next Scene that would be displayed by Labels. The problem now is, I have transferred the info, and the text property of the Label has changed (observed through println and debugging), but the Label just won't display the changed text.
this is the main
package app;
import app.data.MedicineData;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import org.w3c.dom.events.Event;
import java.io.IOException;
public class Pharmachine extends Application {
public static final double WIDTH = 480;
public static final double HEIGHT = 720;
#Override
public void start(Stage stage) throws Exception{
Parent homePage = FXMLLoader.load(getClass().getResource("homepage.fxml"));
Scene homePageScene = newDefaultScene(homePage);
stage.setResizable(false);
stage.setScene(homePageScene);
stage.setTitle("Pharmachine");
stage.show();
}
#Override
public void init(){
MedicineData.getInstance().loadData();
}
public static Scene newDefaultScene(Parent parent){
Scene scene = new Scene(parent, WIDTH, HEIGHT);
scene.addEventHandler(MouseEvent.MOUSE_CLICKED,
(mouseEvent) -> {
Node targetNode = scene.getFocusOwner();
if(targetNode != null && targetNode.isFocused()) {
if(targetNode.getParent() != null)
targetNode.getParent().requestFocus();
mouseEvent.consume();
}
}
);
return scene;
}
public static void navigateTo(ActionEvent actionEvent, String filename){
Stage mainWindow = (Stage) ((Node) actionEvent.getSource()).getScene().getWindow();
Scene currentScene = ((Node) actionEvent.getSource()).getScene();
try {
currentScene.setRoot(FXMLLoader.load(Pharmachine.class.getResource(filename)));
mainWindow.setScene(currentScene);
} catch(IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){ launch(args); }
}
this is the fxml for the list's page
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="app.ListpageController" fx:id="pageRoot"
stylesheets="#styles/main.css">
<left>
<GridPane fx:id="listArea" alignment="CENTER">
<ListView fx:id="medicineList"
GridPane.rowIndex="1"/>
</GridPane>
</left>
<center>
<GridPane fx:id="buttonsArea" alignment="CENTER"
hgap="10" vgap="10">
<Button text="Back"
onAction="#displayHomePage"
GridPane.halignment="CENTER"/>
<Button text="Details"
onAction="#displayDetailsPage"
GridPane.rowIndex="1"
GridPane.halignment="CENTER"/>
</GridPane>
</center>
</BorderPane>
here is the controller for the list's page
package app;
import app.data.Medicine;
import app.data.MedicineData;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.util.Callback;
import java.io.IOException;
public class ListpageController {
#FXML
private BorderPane pageRoot;
#FXML
private GridPane listArea;
#FXML
private GridPane buttonsArea;
#FXML
private ListView<Medicine> medicineList;
#FXML public void initialize(){
listArea.setMaxWidth(Pharmachine.WIDTH * 3/5);
medicineList.setMinWidth(listArea.getMaxWidth());
medicineList.setMinHeight(Pharmachine.HEIGHT);
medicineList.setItems(MedicineData.getInstance().getMedicines());
medicineList.setCellFactory(new Callback<>() {
#Override
public ListCell<Medicine> call(ListView<Medicine> medicineListView) {
ListCell<Medicine> listCell = new ListCell<>(){
#Override
public void updateItem(Medicine medicine, boolean empty){
super.updateItem(medicine, empty);
if(!empty){
HBox medName = new HBox(new Label(medicine.getName()));
HBox.setHgrow(medName, Priority.ALWAYS);
HBox medPrice = new HBox(new Label(String.format("RM%.2f", medicine.getPrice())));
HBox medLabel = new HBox(medName, medPrice);
setGraphic(medLabel);
}
}
};
listCell.setOnMouseClicked(
(mouseEvent) -> {
if(mouseEvent.getClickCount() == 2){
medicineList.getSelectionModel().clearSelection();
}
}
);
return listCell;
}
});
}
#FXML private void displayHomePage(ActionEvent actionEvent){
Pharmachine.navigateTo(actionEvent, "homepage.fxml");
}
#FXML private void displayDetailsPage(ActionEvent actionEvent){
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("detailspage.fxml"));
try {
loader.load();
} catch(IOException e){
e.printStackTrace();
}
DetailspageController controller = loader.getController();
Medicine selectedMedicine = medicineList.getSelectionModel().getSelectedItem();
if(selectedMedicine != null) {
controller.setInfo(selectedMedicine);
Pharmachine.navigateTo(actionEvent, "detailspage.fxml");
}
}
}
this is the fxml for the next scene (just simple for now)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.text.Text?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:id="pageRoot" fx:controller="app.DetailspageController"
stylesheets="#styles/main.css">
<center>
<GridPane fx:id="detailsArea"
alignment="CENTER" hgap="10" vgap="10">
<Label text="Details"/>
<Text text="Name: "
GridPane.rowIndex="1"/>
<Label fx:id="medNameLabel" text="~"
GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Text text="Price: "
GridPane.rowIndex="2"/>
<Label fx:id="medPriceLabel" text="~"
GridPane.rowIndex="2" GridPane.columnIndex="1"/>
</GridPane>
</center>
</BorderPane>
and this is the scene's controller
package app;
import app.data.Medicine;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import java.net.URL;
import java.util.ResourceBundle;
public class DetailspageController implements Initializable {
private Medicine selectedMedicine;
#FXML
private BorderPane pageRoot;
#FXML
private GridPane detailsArea;
#FXML
private Label medNameLabel;
#FXML
private Label medPriceLabel;
#Override
public void initialize(URL url, ResourceBundle rb){
}
public void setInfo(Medicine medicine){
selectedMedicine = medicine;
medNameLabel.setText(selectedMedicine.getName());
medPriceLabel.setText(String.format("RM%.2f", selectedMedicine.getPrice()));
System.out.println(medNameLabel.textProperty());
System.out.println(medNameLabel.getText());
medNameLabel.setStyle("-fx-background-color: red;");
}
}
The help would mean a lot. Thank you :]
In your displayDetailsPage method, you load a scene from detailspage.fxml, and you update its labels by calling the setInfo method of the controller.
Then, you call Pharmachine.navigateTo, which loads detailspage.xml again and replaces the scene root with the newly loaded root. You updated the text of the labels in the first details page, but the second details page is brand new, so it doesn’t have those changes.
The second argument in the navigateTo method should be of type Parent rather than a String. navigateTo should not attempt to load any .fxml file; let that be the caller’s responsibility.
Side note: When a list has multiple attributes for each data item, you should probably use a TableView rather than a ListView.

JavaFx Change Window, Without New Controller Constructor

I have 2 fxml file. Example A.fxml. B.fxml. I have 2 controller. AController (A.fxml) BController (B.fxml). A fxml and B fxml have change button changing Scene or fxml. This is button code;
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/infoLibrary/view/A.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
Same code in BContrroller change button. When i click change button scane changing. But everytime init method and controller contructers working too. When user change fxml javafx everytime using new constructor. How can i change windows without new controller constructor?
Use a view model:
public class ViewModel {
public enum View {A, B}
private final ObjectProperty<View> currentView = new SimpleObjectProperty<>(View.A);
public ObjectProperty<View> currentViewProperty() {
return currentView ;
}
public final View getCurrentView() {
return currentViewProperty().get();
}
public final View setCurrentView(View view) {
currentViewProperty().set(view);
}
}
Now in your AController do:
public class AController {
private ViewModel viewModel ;
public void setViewModel(ViewModel viewModel) {
this.viewModel = viewModel ;
}
// button handler:
#FXML
private void goToB(ActionEvent event) {
viewModel.setCurrentView(ViewModel.View.B);
}
}
and BController is similar.
Finally, you set everything up with something like the following, which is executed only once (e.g. in your start() method, or somewhere similar):
Stage stage = ... ; // maybe it's the primary stage in start...
Scene scene = new Scene();
ViewModel viewModel = new ViewModel();
FXMLLoader aLoader = new FXMLLoader(getClass().getResource("/infoLibrary/view/A.fxml"));
Parent a = aLoader.load();
AController aController = aLoader.getController();
aController.setViewModel(viewModel);
FXMLLoader bLoader = new FXMLLoader(getClass().getResource("/infoLibrary/view/B.fxml"));
Parent b = bLoader.load();
BController bController = bLoader.getController();
bController.setViewModel(viewModel);
scene.rootProperty().bind(Bindings.createObjectBinding(() -> {
if (viewModel.getCurrentView() == ViewModel.View.A) {
return a ;
} else if (viewModel.getCurrentView() == ViewModel.View.B) {
return b ;
} else {
return null ;
}
}, viewModel.currentViewProperty());
stage.setScene(scene);
stage.show();
Now the two FXML files are only loaded once (so the controllers are only created once, and their initialize() methods called only once). The switching is managed by changing the state of the ViewModel and observing that state, so the scene's root is changed when the model state changes.
Here is a complete SSCCE:
ViewModel.java:
package sceneswitcher;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class ViewModel {
public enum View {A, B}
private final ObjectProperty<View> currentView = new SimpleObjectProperty<>(View.A);
public ObjectProperty<View> currentViewProperty() {
return currentView ;
}
public final View getCurrentView() {
return currentViewProperty().get();
}
public final void setCurrentView(View view) {
currentViewProperty().set(view);
}
}
AController.java:
package sceneswitcher;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class AController {
private ViewModel viewModel ;
#FXML
private TextField textField ;
public void setViewModel(ViewModel viewModel) {
this.viewModel = viewModel ;
}
// button handler:
#FXML
private void goToB(ActionEvent event) {
viewModel.setCurrentView(ViewModel.View.B);
}
public String getText() {
return textField.getText();
}
}
A.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<VBox fx:controller="sceneswitcher.AController" spacing="5" alignment="CENTER"
xmlns:fx="http://javafx.com/fxml/1">
<Label text='This is view A'/>
<TextField fx:id="textField" />
<Button onAction="#goToB" text="Go to view B"/>
</VBox>
BController.java:
package sceneswitcher;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
public class BController {
private ViewModel viewModel ;
#FXML
private TextArea textArea ;
public void setViewModel(ViewModel viewModel) {
this.viewModel = viewModel ;
}
// button handler:
#FXML
private void goToA(ActionEvent event) {
viewModel.setCurrentView(ViewModel.View.A);
}
public String getText() {
return textArea.getText();
}
}
B.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.Button?>
<VBox fx:controller="sceneswitcher.BController" spacing="5" alignment="CENTER"
xmlns:fx="http://javafx.com/fxml/1">
<Label text="This is view B"/>
<TextArea fx:id="textArea" />
<Button onAction="#goToA" text="Go to View A"/>
</VBox>
Main.java:
package sceneswitcher;
import java.io.IOException;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
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 IOException {
ViewModel viewModel = new ViewModel();
FXMLLoader aLoader = new FXMLLoader(getClass().getResource("A.fxml"));
Parent a = aLoader.load();
AController aController = aLoader.getController();
aController.setViewModel(viewModel);
FXMLLoader bLoader = new FXMLLoader(getClass().getResource("B.fxml"));
Parent b = bLoader.load();
BController bController = bLoader.getController();
bController.setViewModel(viewModel);
Scene scene = new Scene(a, 400, 400);
scene.rootProperty().bind(Bindings.createObjectBinding(() -> {
if (viewModel.getCurrentView() == ViewModel.View.A) {
return a ;
} else if (viewModel.getCurrentView() == ViewModel.View.B) {
return b ;
} else {
return null ;
}
}, viewModel.currentViewProperty()));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Java FX 8, trouble setting the value of text field

I'm trying to populate the value of a text field in Java FX.
I have the Main Class,the controller and the fxml.I have bind the fxml file with controller and the appropriate field in it. When i try to set its value, it fails.
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Main extends Application {
private Stage primaryStage;
private FlowPane rootLayout;
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("test.fxml"));
rootLayout = (FlowPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
testController.java
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class testController {
#FXML
private TextField t1;
public testController() {
System.out.println("hi");
t1 = new TextField("j");
t1.setText("hi");
}
}
FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.FlowPane?>
<FlowPane prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.testController">
<children>
<TextField fx:id="t1" />
</children>
</FlowPane>
You are doing it in the wrong place! If you need to play around with your controls just before your fxml is loaded, you need to do it in the initialize(). For this your controller should implement the Initializable
So your controller becomes :
public class testController implements Initializable{
#FXML
private TextField t1;
public void initialize() {
System.out.println("hi");
//You should not re-initialize your textfield
//t1 = new TextField("j");
t1.setText("hi");
}
}

How to pass a Variable through JavaFX Application to the Controller? [duplicate]

This question already has answers here:
Passing Parameters JavaFX FXML
(10 answers)
Closed 4 years ago.
I'd like to ask if it is possible to pass a Variable through a JavaFX Class what extends Application to my JavaFx Controller? I am very new to JavaFx and only may need a little kick.
The goal is to pass a Id from MyClass to MyController.
My Application class:
public class MyClass extends Application {
private String myVariable="Anything";
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
URL location = getClass().getResource("MyGui.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
Parent root = FXMLLoader.load(location);
Scene scene = new Scene(root);
stage.setTitle(title);
stage.setScene(scene);
stage.show();
}
}
My Controller:
public class Controller extends Group implements Binding {
public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
// HERE I'D LIKE TO GET MY VARIABLE LIKE
System.out.println(myVariable);
}
#Override
public List<Handler> getHandlerChain() {
return null;
}
#Override
public void setHandlerChain(List<Handler> chain) {
}
#Override
public String getBindingID() {
return null;
}
}
First you will have to add the setter and getter in the MyClass (as the var is private) and change it to static:
private static String myVariable;
public String getMyVariable() {
return myVariable;
}
public void setMyVariable(String myVariable) {
this.myVariable = myVariable;
}
Then, as the MyClass is static can do:
System.out.println(MyClass.getMyVariable());
Working example:
MyClass.java
import java.net.URL;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MyClass extends Application {
private static String myVariable;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
setMyVariable("Anything");
URL location = getClass().getResource("MyGui.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
Parent root = FXMLLoader.load(location);
Scene scene = new Scene(root);
stage.setTitle("Hello Word");
stage.setScene(scene);
stage.show();
}
public static String getMyVariable() {
return myVariable;
}
public static void setMyVariable(String myVariable) {
MyClass.myVariable = myVariable;
}
}
MyController.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class MyController implements Initializable{
#FXML Label labelVar;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
labelVar.setText(labelVar.getText() + MyClass.getMyVariable());
}
}
MyGui.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="110.0" prefWidth="305.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MyController">
<children>
<Label fx:id="labelVar" layoutX="24.0" layoutY="32.0" prefHeight="17.0" prefWidth="129.0" text="MyVariable = " />
</children>
</Pane>

What is the main way to connect a view and a model in JavaFX?

What is an expected method of connecting view and model in JavaFX?
Binding?
Suppose I want to make positioning in database with the following controls:
I have data (recordset) object in memory and it's properties are bindable. I.e. they are notifying when current record changes and when the number of records changes.
I want user to be able position inside recordset both with slider and text field.
How to accomplish that? There is no numeric spin in JavaFX, so how to bind text, slider and recordset object (three ends) together? Is it possible?
I cannot give an authoritative answer since I don't work for Oracle nor am I some JavaFX-pert but I usually construct the controller with an instance of the model and in the initialize method of the controller I create the bindings between the controls' properties and the model's properties.
To your concrete problem I have a lame but working solution. Lame because I can't figure out how to use the low-level binding API and I use two properties on the model. Here's how :
FXML :
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.TextField?>
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<Slider GridPane.columnIndex="0" fx:id="slider" min="0" max="99"/>
<Label GridPane.columnIndex="1" text="Record" />
<TextField fx:id="textfield" GridPane.columnIndex="2"/>
</GridPane>
Main.java :
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import sample.Models.Model;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
final Model model = new Model();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("sample.fxml"));
loader.setControllerFactory(new Callback<Class<?>, Object>() {
#Override
public Object call(Class<?> aClass) {
return new Controller(model);
}
});
GridPane root = (GridPane) loader.load();
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
}
Controller.java :
package sample;
import javafx.beans.binding.DoubleBinding;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import sample.Models.Model;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
private final Model model;
public Controller(Model model) {
this.model = model;
}
#FXML
public Slider slider;
#FXML
public TextField textfield;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
slider.valueProperty().bindBidirectional(model.pageProperty());
textfield.textProperty().bindBidirectional(model.pageTextProperty());
}
}
Model.java :
package sample.Models;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class Model {
private IntegerProperty pageProperty;
private StringProperty pageTextProperty;
public Model() {
pageProperty = new SimpleIntegerProperty(2);
pageProperty.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number number, Number number2) {
int value = pageProperty.get();
//System.out.println("Page changed to " + value);
if (Integer.toString(value).equals(pageTextProperty.get())) return;
pageTextProperty.set(Integer.toString(value));
}
});
pageTextProperty = new SimpleStringProperty("2");
pageTextProperty.addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
try {
int parsedValue = Integer.parseInt(observableValue.getValue());
if (parsedValue == pageProperty.get()) return;
pageProperty().set(parsedValue);
} catch (NumberFormatException e) {
// too bad
}
}
});
}
public int getPage() {
return pageProperty.get();
}
public void setPage(int page) {
pageProperty.set(page);
}
public IntegerProperty pageProperty() {
return pageProperty;
}
public String getPageText() {
return pageTextProperty.get();
}
public void setPageText(String pageText) {
pageTextProperty.set(pageText);
}
public StringProperty pageTextProperty() {
return pageTextProperty;
}
}

Categories