I'm writing a custom control which displays an error icon and a message in a tooltip if the validation in a form fails. My version without the custom control looks like this:
<HBox>
<TextField fx:id="name"></TextField>
<Label fx:id="error" focusTraversable="false" visible="false">
<graphic>
<ImageView fitHeight="24.0" fitWidth="24.0" pickOnBounds="true" preserveRatio="true"/>
</graphic>
<tooltip>
<Tooltip fx:id="errorTooltip"/>
</tooltip>
</Label>
</HBox>
The result is this:
My efforts to create a custom control lead to this:
<fx:root type="javafx.scene.layout.HBox" xmlns:fx="http://javafx.com/fxml">
<children/>
<Label fx:id="error" focusTraversable="false" visible="false">
<graphic>
<ImageView fitHeight="24.0" fitWidth="24.0" pickOnBounds="true" preserveRatio="true"/>
</graphic>
<tooltip>
<Tooltip fx:id="errorToolTip"/>
</tooltip>
</Label>
</fx:root>
This is the code behind the fxml:
package control;
[imports omitted for brevity]
#DefaultProperty(value = "children")
public final class ValidatedControl extends HBox implements Initializable {
#FXML
private Label error;
#FXML
private Tooltip errorToolTip;
private StringProperty errorToolTipProperty;
public ValidatedControl() {
final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ValidatedControl.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public void setErrorToolTip(final String errorToolTip) {
this.getErrorToolTipProperty().setValue(errorToolTip);
}
public String getErrorToolTip() {
return this.getErrorToolTipProperty().getValueSafe();
}
#Override
public void initialize(final URL location, final ResourceBundle resources) {
this.errorToolTip.textProperty().bind(this.getErrorToolTipProperty());
this.error.visibleProperty().bind(this.getErrorToolTipProperty().isNotEmpty());
}
public StringProperty getErrorToolTipProperty() {
if (this.errorToolTipProperty == null) {
this.errorToolTipProperty = new SimpleStringProperty();
}
return this.errorToolTipProperty;
}
}
I can use the control in fxml but the child component I add is always the last child which means the error icon is displayed to its left.
My control is used like this:
<ValidatedControl>
<TextField>
</TextField>
</ValidatedControl>
How do I get it to display the icon on the right side?
Now I do understand your problem. This might not fix your problem when you add your ValidatedControl in FXML, but when you do it programmatically try this:
ValidatedControl vc = new ValidatedControl();
TextField textField = new TextField();
vc.getChildren().add(textField);
textField.toBack();
Another way would be to go ItachiUchiha's way and add a Pane in your FXML as first child. But instead of overwriting the getChildren() method, write a new Method addNode(Node n) and add the node to the Pane.
forget about my first answer ;)
Related
I'm trying to write my first application with javaFX and FXML files, but I got stuck with accesing variables of the FXMLs.
I am able to start the program with the first scene (first FXML). There is a button I'm able to interact with, and the first scene hides and the second scene appears. In that scene, the user has to insert a value in a text field. I'm able to save this into a variable by confirming this input over another button.
This button click hides the second scene and opens the first scene again. Now I want to set the value of a label in that first scene to the value of the variable I saved the user's input in. But this throws an error message.
It seems like this label value is not accessable, even if this label is part of the currently loaded FXML, but I am able to get the value of a label of the second FXML (user interaction field), which isn't visible anymore.
Can anyone help me, how to make this Label of the first FXML readable and changable?
My code is quite long, but I tired to give you the most important parts:
#FXML
private Label RollsMax;
#FXML
private TextField roll_input;
#FXML
private String amountRolls;
... // several more declarations
#FXML
void btn_confirmInput_onClick(ActionEvent event){ //btn_confirmInput is part of scene2.fxml
event.consume();
amountRolls=roll_input.getText();
System.out.println(amountRolls); // here I get the result of the user's input
try{
root=FXMLLoader.load(getClass().getResource("project/scene1.fxml"));
stage=(Stage)((Node)event.getSource()).getScene().getWindow();
scene=new Scene(root);
stage.setScene(scene);
stage.show();
} //end try
catch (IOException e){
e.printStackTrace();
} //end catch
RollsMax.setText(amountRolls); // THIS LINE IS NOT WORKING AS RollsMax SEEMS NOT AVAILABLE, EVEN IF IT IS PART OF SCENE1.fxml
} // end btn_confirmInput_onClick()
Any ideas? The error I get is 'java.lang.RuntimeException: java.lang.reflect.InvocationtargetException' as well as a'java.lang.NullPointerException'.
Thanks a lot!
Endcoder
To share data between the scenes, we'll introduce a Model class that holds the shared information:
public class Model {
private SimpleStringProperty valueProperty = new SimpleStringProperty("N/A");
public SimpleStringProperty getValueProperty() {
return valueProperty;
}
}
And an interface to be implemented by the controllers of the two scenes (actually the controllers of the two roots of the two scenes).
The interface adds the functionality of injection a Model into the controller:
public interface Controller {
void setModel(Model model);
}
sceneOne.fxml with a button to switch scene and a label to display user's input:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/16"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="SceneOneController">
<children>
<Label layoutX="159.0" layoutY="40.0" text="Scene 1">
<font>
<Font size="24.0" />
</font>
</Label>
<Button layoutX="264.0" layoutY="246.0" mnemonicParsing="false" onAction="#changeScene" prefHeight="26.0" prefWidth="102.0" text="Get Input" />
<Label fx:id="inputValue" layoutX="187.0" layoutY="142.0" text="Label" />
</children>
</AnchorPane>
And its controller which implements the Controller interface:
public class SceneOneController implements Controller {
#FXML Label inputValue;
private Model model;
public void changeScene(ActionEvent e) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sceneTwo.fxml"));
Parent root = loader.load();
Controller controller = loader.getController(); //get a reference to sceneTwo controller
controller.setModel(model);
Stage stage = (Stage)((Node)e.getSource()).getScene().getWindow();
stage.setScene(new Scene(root));
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void setModel(Model model) {
this.model = model;
if(model != null){
inputValue.textProperty().unbind();
inputValue.textProperty().bind(model.valueProperty);
}
}
}
sceneTwo.fxml with a button to switch scene and a TextField for the user's input:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane fx:id="main" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="SceneTwoController">
<children>
<Label layoutX="150.0" layoutY="41.0" text="Scene 2">
<font>
<Font size="24.0" />
</font>
</Label>
<Button layoutX="264.0" layoutY="246.0" mnemonicParsing="false" onAction="#changeScene" prefHeight="26.0" prefWidth="102.0" text="Update" />
<TextField fx:id="inputValue" layoutX="121.0" layoutY="138.0" prefHeight="25.0" prefWidth="162.0" />
</children>
</AnchorPane>
And its controller:
public class SceneTwoController implements Controller {
#FXML TextField inputValue;
private Model model;
public void changeScene(ActionEvent e) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sceneOne.fxml"));
Parent root = loader.load();
Controller controller = loader.getController(); //get a reference to sceneOne controller
controller.setModel(model);
Stage stage = (Stage)((Node)e.getSource()).getScene().getWindow();
stage.setScene(new Scene(root));
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void setModel(Model model) {
this.model = model;
if(model != null){
inputValue.textProperty().unbind();
model.valueProperty.bind(inputValue.textProperty());
}
}
}
And finally the application to test it all:
public class SwitchSceneMVC extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("sceneOne.fxml"));
Parent root = loader.load();
Model model = new Model();
Controller controller = loader.getController();
controller.setModel(model);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(null);
}
}
Use a controller class for every FXML. Then you should be able to interact between them with public methods.
Especially when just hidinng the first scene you should then be able to just write from one scene to the other one.
Alternatively: while starting the second scene make a parameter where you bring the instance of the first scene. Than you should be able to use the needed methods on the first scenes instance.
Like
Code Scene one
#FXML
private void startSceneTwoButton(){
new SceneTwo(this);
}
Code Scene two
FirstScene scene;
public SecondScene(FirstScene scene){
this.scene = scene;
//some code
}
#FXML
private void button(){
scene.enterValue(value);
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have the LineChart with 4 different series. There are checkboxes which should show/hide the correspondent series from chart. But when I pressed checkboxes for "Euler", "Improved Euler", "Runge-Kutta", they do not affect the chart at all, until I press the "Exact"'s checkbox. Can't get the problem absolutely.
Here is how I try to hide/show:
#FXML private NumberAxis xAxis = new NumberAxis();
#FXML private NumberAxis yAxis = new NumberAxis();
#FXML private LineChart<Number, Number> chartAll = new LineChart<>(xAxis, yAxis);
private XYChart.Series<Number, Number> exactSeries = new XYChart.Series<>();
private XYChart.Series<Number, Number> eulerSeries= new XYChart.Series<>();
private XYChart.Series<Number, Number> improvedEulerSeries = new XYChart.Series<>();
private XYChart.Series<Number, Number> rungeKuttaSeries = new XYChart.Series<>();
#FXML private CheckBox exactChartCheckbox;
#FXML private CheckBox eulerChartCheckbox;
#FXML private CheckBox improvedEulerChartCheckbox;
#FXML private CheckBox rungeKuttaChartCheckbox;
#FXML
protected void handlePlotButton(ActionEvent event) {
chartAll.getData().clear();
double x0 = Double.parseDouble(x0Input.getText());
double y0 = Double.parseDouble(y0Input.getText());
double x = Double.parseDouble(xInput.getText());
double step = Double.parseDouble(stepInput.getText());
IVP ivpForExact = new IVP(x0, y0, x, step);
Exact exact = new Exact(ivpForExact);
Euler euler = new Euler(ivpForExact);
ImprovedEuler improvedEuler = new ImprovedEuler(ivpForExact);
RungeKutta rungeKutta = new RungeKutta(ivpForExact);
exactSeries.setName("Exact");
eulerSeries.setName("Euler");
improvedEulerSeries.setName("Improved Euler");
rungeKuttaSeries.setName("Runge-Kutta");
for (int i = 0; i < exact.getGrid().getSize(); i++) {
exactSeries.getData().add(new XYChart.Data<>(exact.getGrid().getXat(i), exact.getGrid().getYat(i)));
eulerSeries.getData().add(new XYChart.Data<>(euler.getGrid().getXat(i), euler.getGrid().getYat(i)));
improvedEulerSeries.getData().add(new XYChart.Data<>(improvedEuler.getGrid().getXat(i), improvedEuler.getGrid().getYat(i)));
rungeKuttaSeries.getData().add(new XYChart.Data<>(rungeKutta.getGrid().getXat(i), rungeKutta.getGrid().getYat(i)));
}
chartAll.getData().add(exactSeries);
chartAll.getData().add(eulerSeries);
chartAll.getData().add(improvedEulerSeries);
chartAll.getData().add(rungeKuttaSeries);
stepInput.clear();
}
#FXML
protected void exactChartCheckboxPressed(ActionEvent event) {
if(!exactChartCheckbox.isSelected()) {
chartAll.getData().get(0).getNode().setVisible(false);
} else {
chartAll.getData().get(0).getNode().setVisible(true);
}
}
#FXML
protected void eulerChartCheckboxPressed(ActionEvent event) {
if(!eulerChartCheckbox.isSelected()) {
chartAll.getData().get(1).getNode().setVisible(false);
} else {
chartAll.getData().get(1).getNode().setVisible(true);
}
}
Here is FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.layout.*?>
<GridPane fx:id="scene" alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<children>
<Pane fx:id="pane" prefHeight="600.0" prefWidth="1080.0">
<children>
<LineChart fx:id="chartAll" createSymbols="false" layoutX="48.0" layoutY="135.0" prefHeight="427.0" prefWidth="698.0">
<xAxis>
<NumberAxis label="x" fx:id="xAxis" />
</xAxis>
<yAxis>
<NumberAxis fx:id="yAxis" autoRanging="false" label="y" lowerBound="-10.0" upperBound="40.0" />
</yAxis>
<opaqueInsets>
<Insets />
</opaqueInsets>
</LineChart>
<Button layoutX="48.0" layoutY="54.0" mnemonicParsing="false" prefHeight="41.0" prefWidth="120.0" text="Solutions" />
<TextField layoutX="883.0" layoutY="211.0" prefHeight="25.0" prefWidth="96.0" text="0" fx:id="x0Input" />
<TextField fx:id="y0Input" layoutX="883.0" layoutY="254.0" prefHeight="25.0" prefWidth="96.0" text="3" />
<TextField layoutX="883.0" layoutY="296.0" prefHeight="25.0" prefWidth="96.0" text="5.5" fx:id="xInput" />
<TextField fx:id="stepInput" layoutX="883.0" layoutY="336.0" prefHeight="25.0" prefWidth="96.0" text="0.1" />
<Label layoutX="841.0" layoutY="215.0" text="x0" />
<Label layoutX="841.0" layoutY="258.0" text="y0" />
<Label layoutX="841.0" layoutY="300.0" text="X" />
<Label layoutX="835.0" layoutY="340.0" text="step" />
<Button fx:id="plotButton" layoutX="896.0" layoutY="385.0" mnemonicParsing="false" onAction="#handlePlotButton" prefHeight="41.0" prefWidth="71.0" text="Plot" />
<CheckBox fx:id="exactChartCheckbox" layoutX="347.0" layoutY="562.0" mnemonicParsing="false" onAction="#exactChartCheckboxPressed" selected="true" text="Exact" />
<CheckBox fx:id="improvedEulerChartCheckbox" layoutX="523.0" layoutY="562.0" mnemonicParsing="false" onAction="#improvedEulerChartCheckboxPressed" selected="true" text="Improved Euler" />
<CheckBox fx:id="rungeKuttaChartCheckbox" layoutX="646.0" layoutY="562.0" mnemonicParsing="false" onAction="#rungeKuttaChartCheckboxPressed" selected="true" text="Runge-Kutta" />
<CheckBox fx:id="eulerChartCheckbox" layoutX="444.0" layoutY="562.0" mnemonicParsing="false" onAction="#eulerChartCheckboxPressed" selected="true" text="Euler" />
</children>
</Pane>
</children>
</GridPane>
Here is the result
after I hide firstly "Euler" and then "Exact":
after I show "Exact" then "Euler":
The problem actually is that chart is only updated when I check/uncheck "Exact" box, all three others do not affect the chart. But when I press the "Exact"'s checkbox, they are trying to be updated also but do it improperly.
To effectively hide the node try setting the managed property
series.getNode().setManaged(false);
Defines whether or not this node's layout will be managed by it's parent. If the node is managed, it's parent will factor the node's geometry into its own preferred size and layoutBounds calculations and will lay it out during the scene's layout pass. If a managed node's layoutBounds changes, it will automatically trigger relayout up the scene-graph to the nearest layout root (which is typically the scene's root node).
If the node is unmanaged, its parent will ignore the child in both preferred size computations and layout. Changes in layoutBounds will not trigger relayout above it. If an unmanaged node is of type Parent, it will act as a "layout root", meaning that calls to Parent.requestLayout() beneath it will cause only the branch rooted by the node to be relayed out, thereby isolating layout changes to that root and below. It's the application's responsibility to set the size and position of an unmanaged node.
Based on the length of my comment, I am posting it in answer rather than in comment.
Firstly, if you check the documentation of node in XYChart.Series
The node to display for this series. This is created by the chart if
it uses nodes to represent the whole series. For example line chart
uses this for the line but scatter chart does not use it. This node
will be set as soon as the series is added to the chart. You can then
get it to add mouse listeners etc.
From the above, it is clear that you can easily rely on node if it is line chart. And from your screenshot I can see that you are already using LineChart. Which is not a problem. If you inspect the node type, it will be of type Path. But things get a little messy if your line chart has data points displayed on it.Sometimes toggling the node(Path) visibility alone cannot get the desired behavior. For example check the below screen shot.
So to hide a series from the chart, I would recommend to add/remove the series from the data rather than toggling the visibility of the series node.
Coming to your problem, I gave a quick check to toggle the lines visiblity and it working fine for all series. Might be some issue in your code. As #Slaw mentioned, a Minimal, Complete, and Verifiable example can help us to understand the issue or even solve your problem by yourself.
It seems, like it depends on your luck or some legacy JavaFX code. When I changed each series to Array of series:
private ArrayList<XYChart.Series<Number, Number>> exactSeries = new ArrayList<>();
private ArrayList<XYChart.Series<Number, Number>> eulerSeries = new ArrayList<>();
private ArrayList<XYChart.Series<Number, Number>> improvedEulerSeries = new ArrayList<>();
private ArrayList<XYChart.Series<Number, Number>> rungeKuttaSeries = new ArrayList<>();
and then create the common function for all cases:
private void turnVisibility(CheckBox checkBox, ArrayList<XYChart.Series<Number, Number>> seriesList) {
if (!checkBox.isSelected()) {
for(XYChart.Series<Number, Number> series: seriesList) {
series.getNode().setVisible(false);
}
} else {
for(XYChart.Series<Number, Number> series: seriesList) {
series.getNode().setVisible(true);
}
}
}
visibility starts work correctly.
How i can do button action for editing TableView. I need to put text from TextArea to table when i touch button. And if put System.out.println in inputToTable() it is work.
public class InputController {
public TextArea inputArea;
public Button inputButton;
private TableController tableController;
public void initialize() {
tableControllerInit();
}
public void inputToTable() {
if(inputArea.getText() != "") {
tableController.tableInfo.setItems(FXCollections.observableArrayList(new InputObject(inputArea.getText())));
}
}
private void tableControllerInit() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("table.fxml"));
fxmlLoader.load();
tableController = fxmlLoader.getController();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class TableController {
#FXML TableView<InputObject> tableInfo;
#FXML TableColumn<InputObject, String> col1;
public void initialize() {
col1.setCellValueFactory(new PropertyValueFactory<>("text"));
}
}
public class Controller implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
public class InputObject {
String text;
public InputObject(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
<BorderPane fx:controller="sample.Controller" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<left>
<fx:include source="table.fxml"/>
</left>
<center>
<fx:include source="input.fxml"/>
</center>
</BorderPane>
<TableView fx:controller="sample.TableController" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:id="tableInfo" prefHeight="400.0" prefWidth="330.0">
<columns>
<TableColumn fx:id="col1" prefWidth="75.0" text="Output" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<VBox fx:controller="sample.InputController" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<TextArea fx:id="inputArea" prefHeight="188.0" prefWidth="270.0" />
<Button fx:id="inputButton" onAction="#inputToTable" mnemonicParsing="false" text="Input">
<VBox.margin>
<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
</VBox.margin>
</Button>
</children>
</VBox>
You load table.fxml twice: once via the fx:include in the main FXML file, and once in InputController, via the FXMLLoader you create in the tableControllerInit() method. Consequently, two instances of TableController are created, one associated with the first UI you load from table.fxml, and one associated with the second UI you load from table.fxml.
The UI you load via the fx:include is displayed in the VBox defined in the main FXML file. The UI you load with the FXMLLoader is never displayed (in fact, you never even keep a reference to it, you just call loader.load() and discard the result). When you try to update the table's items (do you really intend to replace all the existing items, by the way?), you refer to the second controller instance, which is associated with the UI which is never displayed. Consequently, you are updating a table that is not displayed, and you never see any results.
What you really need to do is share the same data between the two controllers associated with the two fx:includes. You can do this simply by injecting those two controllers into the main controller, as described in the "Nested Controllers" section in the documentation.
First, give the fx:include elements fx:id attributes:
<BorderPane fx:controller="sample.Controller" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<left>
<fx:include fx:id="table" source="table.fxml"/>
</left>
<center>
<fx:include fx:id="input" source="input.fxml"/>
</center>
</BorderPane>
Then you can inject the controllers into the main controller by creating fields with the word "Controller" appended to the fx:id. Create a single observable list, which will represent the list of items displayed in the table, and pass it to each controller:
public class Controller implements Initializable {
#FXML
private TableController tableController ;
#FXML
private InputController inputController ;
#Override
public void initialize(URL location, ResourceBundle resources) {
ObservableList<InputObject> items = FXCollections.observableArrayList();
tableController.setTableItems(items);
inputController.setTableItems(items);
}
}
Finally, just define the obvious methods in each of the other two controllers:
public class TableController {
#FXML
private TableView<InputObject> tableInfo;
#FXML
private TableColumn<InputObject, String> col1;
public void initialize() {
col1.setCellValueFactory(new PropertyValueFactory<>("text"));
}
public void setTableItems(ObservableList<InputObject> tableItems) {
tableInfo.setItems(tableItems);
}
}
Now the table is displaying the contents of the items list created in the main controller's initalize() method, and the InputController has a reference to the same list. So all you need to do is update that list in the InputController. I assume you just want to add items to the table (not replace them all):
public class InputController {
#FXML
private TextArea inputArea;
#FXML
private Button inputButton;
private ObservableList<InputObject> tableItems ;
public void setTableItems(ObservableList<InputObject> tableItems) {
this.tableItems = tableItems ;
}
public void inputToTable() {
if(! inputArea.getText().isEmpty()) {
tableItems.add(new InputObject(inputArea.getText()));
}
}
}
More generally, if you have more data to share among the different controllers, you would create one or more "model" classes and share a model instance with the controllers. Then you can observe the properties of the model and update them. See Applying MVC With JavaFx for a more comprehensive example.
I have a javafx scene with several buttons within. The only way that the events of the button will be activated is by double cliking. In fxml the button is using the following action method onAction="#Button1Action". How can I change the functionality of the button1Action event from double click to just one click?
The function onAction:
#FXML
private void Button1Action(ActionEvent event) {
}
and the fxml code:
<Button id="button1" fx:id="button1" maxHeight="1.79.." maxWidth="1.79.." mnemonicParsing="false" onAction="#Button1Action" text="Answer" GridPane.columnIndex="3" GridPane.columnSpan="3" GridPane.halignment="CENTER" GridPane.rowIndex="7" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
</GridPane.margin>
</Button>
You did not posted the body of Button1Action, but most probably it is looking like:
#FXML
private void Button1Action(ActionEvent event) {
button1.setOnAction(e -> System.out.println("Button clicked"));
}
What happens here, that you assign the listener inside the listener, so the actual listener body will be executed on the second click.
Trivial fix is:
#FXML
private void Button1Action(ActionEvent event) {
System.out.println("Button clicked");
}
I am trying to make a GUI for a application and I have run into a problem when trying to change items after the fxmlloader loaded a file in.
At the start I thought it was that I wanted to edit the items before they were initialised (the rendering of the file was still going and i tried to edit the items before that was done). But then I tried to make a timer that every second (for testing purposes such a long time) checked the state of the object and if the object wasn't null it would do something.
However I noticed that the items never stop being null and thus I can't edit them. Anyone has a idea how to change this? I get this problem with Text and GridPane objects so far but not with buttons :/
The controller (the part that matters):
protected void sceneSetter(String filename) {
//loads the FXMLFile of the right sceneand makes the scene 1280x720 (720p)
try {
root = FXMLLoader.load(getClass().getResource(filename));
} catch (IOException e) {
System.out.println("RIP scene setting");
}
Scene scene = new Scene(root, 1280, 720);
//sets the right scene and displays it
stage.setScene(scene);
stage.show();
if(filename.equals("/InterfaceUserChange.fxml")){
final Timer timer = new Timer(1000, null);
timer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
System.out.println("tried it");
if(firstLoginContainer != null){
TextField field = new TextField() {
#Override public void replaceText(int start, int end, String text) {
// If the replaced text would end up being invalid, then simply
// ignore this call!
if (!text.matches("[0-9]*")) {
super.replaceText(start, end, text);
}
}
#Override public void replaceSelection(String text) {
if (!text.matches("[0-9]*")) {
super.replaceSelection(text);
}
}
};
firstLoginContainer.add(field , 1 , 1);
timer.stop();
}
}
});
if(firstLoginContainer == null) {
timer.start();
}
System.out.println("textfield added?");
}
The fxml file (its in a borderpane):
<left>
<VBox fx:id="firstLoginPaneContainer">
<HBox styleClass="profilePane">
<VBox styleClass="pictureDummy">
<ImageView>
<Image url="#testpica200px.jpg"/>
</ImageView>
</VBox>
<GridPane styleClass="firstLoginContainer"
alignment="TOP_LEFT" hgap="10" vgap="10">
<padding>
<Insets top="25" right="25" bottom="10" left="25"/>
</padding>
<Text text="Name: "
GridPane.rowIndex="0" GridPane.columnIndex="0"/>
<TextField fx:id="changeName"
GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<Text text="Age: "
GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<Text text="Study: "
GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<Text text="Courses: "
GridPane.rowIndex="3" GridPane.columnIndex="0"/>
<Text text="Preferences: " fx:id="selfPreferences"
GridPane.rowIndex="4" GridPane.columnIndex="0"/>
<Text text="E-mail: " fx:id="changeEmail"
GridPane.rowIndex="5" GridPane.columnIndex="0"/>
<Text text="Phone number: " fx:id="selfPhonenumber"
GridPane.rowIndex="6" GridPane.columnIndex="0"/>
</GridPane>
</HBox>
</VBox>
</left>