Button EventHandler in Controller class is not working [duplicate] - java

This question already has an answer here:
Handle event on disabled Node
(1 answer)
Closed 4 years ago.
Hello I have two problems.
First one:
When I start my app, I can press my ToggleButton with spacebar.
Second one:
(Even without ToggleButton) My EventHandler on my button is not working, on pressing spacebar nothing is going on.
Main class:
public class Main extends Application {
#Override
public void start(Stage stage) throws IOException {
Parent parent = (Parent) FXMLLoader.load(getClass().getResource("/application/MainView.fxml"));
Scene scene = new Scene(parent);
scene.getRoot().setFocusTraversable(true);
stage.setScene(scene);
stage.setTitle("Login Page");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Fxml file:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="501.0" prefWidth="597.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<Button fx:id="button" layoutX="136.0" layoutY="184.0" mnemonicParsing="false" prefHeight="133.0" prefWidth="127.0" text="Button" />
<Label fx:id="label" layoutX="199.0" layoutY="106.0" prefHeight="61.0" prefWidth="198.0" text="Label" />
<ToggleButton fx:id="toggleButton" layoutX="318.0" layoutY="186.0" mnemonicParsing="false" prefHeight="133.0" prefWidth="127.0" text="ToggleButton" />
</children>
</AnchorPane>
Controller class:
public class MainController implements Initializable {
#FXML
private Button button;
#FXML
private Label label;
#FXML
private ToggleButton toggleButton;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
button.setDisable(true);
button.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.SPACE) {
System.out.println("space pressed");
button.setDisable(false);
}
}
});
button.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.SPACE) {
System.out.println("space pressed");
button.setDisable(true);
}
}
});
}
}

At first as #c0der mentioned you hasn't been assined event handler
as the button is disabled via button.setDisabled(true) so it can't receive any events such as mouse and key events see that javafx doc
Edit with some search i found another similar question in stackoverflow

Related

Getting NullPointerException when button is clicked (JavaFX and FXML) [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
What's the difference between fx:id and id: in JavaFX?
(4 answers)
Closed 5 years ago.
After finally getting the gui to run without problems whenever i click on a designated button the IDE (Eclipse) putting out a long massage with this error in its end.
Caused by: java.lang.NullPointerException
at ui.IOTabController.handleSourceFolderPathSubmit(IOTabController.java:32)
... 62 more
Here is the IOTab.fxml (relevent part):
<AnchorPane fx:id="IOTab" minHeight="0.0" minWidth="0.0"
prefHeight="282.0" prefWidth="676.0" xmlns="http://javafx.com/javafx/9"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="ui.IOTabController">
<children>
<Pane layoutX="12.0" layoutY="50.0" prefHeight="37.0" prefWidth="625.0">
<children>
<TextField id="sourceFolderFullPath" layoutX="167.0" layoutY="1.0" prefHeight="26.0"
prefWidth="391.0"/>
<Button id="sourceFolderPathSubmit" layoutX="567.0" mnemonicParsing="false" text="Enter"
onAction="#handleSourceFolderPathSubmit"/>
<Text layoutY="18.0" strokeType="OUTSIDE" strokeWidth="0.0"
text="Source folder's full path:" />
</children>
</Pane>
...
</AnchorPane>
Its controller IOTabController.java:
public class IOTabController implements Initializable{ // IOTab.fxml controller
#FXML private MainController Main;
// variables from fxml file to inject.
#FXML private AnchorPane IOTab;
#FXML private TextField sourceFolderFullPath;
#FXML private Button sourceFolderPathSubmit;
#FXML private TextField csvFileName;
#FXML private Button csvFileNameSubmit;
#FXML private Button deleteExistingData;
#FXML private Button convertCsvToKml;
#FXML
protected void handleSourceFolderPathSubmit(ActionEvent event) { // handles path submit.
if (sourceFolderFullPath.getText().isEmpty()) { // if empty field
AlertHelper.showAlert(Alert.AlertType.ERROR, "Form Error!", "Please enter path.");
return;
}
AlertHelper.showAlert(Alert.AlertType.CONFIRMATION, "SUCCESS","Path inserted.");
}
// class for creating alerts.
public static class AlertHelper {
public static void showAlert(Alert.AlertType alertType, String title, String message) {
Alert alert = new Alert(alertType);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
}
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
}
}
The main controller:
public class MainController { // main.fxml controller.
#FXML private IOTabController IOTabController;
#FXML public void initialize() {
System.out.println("Application started");
IOTabController.initialize(null, null);
}
}
The gui is running fine until I click on "sourceFolderPathSubmit" button which pops the warning above every click (no matter if i put text in the TextField or not). I cant seem to find what to add or remove to make the button work.

JavaFx actions between controllers

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.

Javafx button event needs double clicking

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");
}

Change Label depending on boolean JavaFX

I am super new to JavaFX and for my checkers game I want to display who's turn it is above my board. I have read through multiple very similar questions, but it doesn't seem to get me where I want to go. I hope you can help me!
I have a FXML file with the following code:
<HBox fx:id="topBox" alignment="TOP_CENTER" prefHeight="45.0" prefWidth="200.0" spacing="10.0" BorderPane.alignment="CENTER" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController">
<children>
<Label fx:id="labelTurn" text="test" />
<Button fx:id="buttonResign" alignment="CENTER" mnemonicParsing="false" onAction="#doResign" prefHeight="25.0" prefWidth="80.0" text="Resign" />
</children>
</HBox>
In my main:
public class Main extends Application {
Checkers content = new Checkers();
BorderPane border = new BorderPane();
#Override
public void start(Stage primaryStage) throws Exception {
try {
border.setTop(FXMLLoader.load(getClass().getResource("Sample.fxml")));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
border.setCenter(createContent());
Scene scene = new Scene(border);
primaryStage.setTitle("Checkers from main");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
My controller:
public class SampleController {
#FXML
private Label labelTurn;
#FXML
private HBox topBox;
#FXML
private Button buttonResign;
private Checkers content = new Checkers();
#FXML
void doResign(ActionEvent event) {
content.resign();
// System.exit(0); //TODO eine tatsächliche Methode eingeben!!
}
public void setLabelTurn(Label labelTurn) {
this.labelTurn = labelTurn;
String setLabelText() {
String s = " ";
if (content.getWhiteTurnPropValue()) {
labelTurn.setText("White, it is your turn.");
s = "White, it is your turn.";
return s;
} else {
labelTurn.setText("Black, it is your turn.");
s = "Black, it is your turn.";
return s;
}
}
#FXML
void initialize() {
assert buttonResign != null : "fx:id=\"buttonResign\" was not injected: check your FXML file 'Sample.fxml'.";
assert topBox != null : "fx:id=\"topBox\" was not injected: check your FXML file 'Sample.fxml'.";
assert labelTurn != null : "fx:id=\"labelTurn\" was not injected: check your FXML file 'Sample.fxml'.";
labelTurn.textProperty().setValue(setLabelText());
}
}
My game data has a BooleanProperty to show who's turn it is. It gets changed after every successful move.
BooleanProperty whiteTurnProp = new SimpleBooleanProperty(true);
public BooleanProperty getWhiteTurnProp() {
return whiteTurnProp;
}
public void setWhiteTurnPropValue(Boolean whiteTurn) {
this.whiteTurnProp.setValue(whiteTurn);
}
public boolean getWhiteTurnPropValue() {
return this.whiteTurnProp.getValue();
}
As far as I have understood, I need to bind the Label to the value, which I have done in the initialize method. But when I play and the value in my game class changed, the label stays the same. How do I update the label? I think I need to add a change listener to something, but I dont know how to do that and more specifically WHERE.
Thank you so much for your help! I shortened my code a bit and hope it is still not too long.
Best,
Lisa
In initialize: labelTurn.textProperty().setValue(setLabelText()); is not a binding, it is a simple set on the property. A really strange one as setLabelText() sets the text of the Label and also return the string which was set, which value is used again to the text of the Label.
You could create a real binding instead:
labelTurn.textProperty().bind(Bindings.createStringBinding(() -> {
String s = " ";
if (content.getWhiteTurnProp().get())
s = "White, it is your turn.";
else
s = "Black, it is your turn.";
return s;
}, content.getWhiteTurnProp()));

JavaFX Thread with progressindicator not spinning, work done in non FXThread

I would like to indicate a loading process with the JavaFX progressindicator.
The problem is that the indicator isn't rotating when the code is executed the first time.
On the second time it is rotating that means the bindings are working, both the disable and the visible-property.
I also change the state in the FX thread and do the work on a seperate thread so i can
see no error here.
Does anyone see the problem?
Controller:
//vars
#FXML private ProgressIndicator loadingIndicator;
private BooleanProperty isSaving = new SimpleBooleanProperty(false);
#Override
public void initialize(URL location, ResourceBundle resources) {
parentToDisable.disableProperty().bind(isSaving);
loadingIndicator.visibleProperty().bind(isSaving);
}
#FXML
void onSave(ActionEvent event) {
isSaving.set(true); //<<<<<<<<<problem
// separate non-FX thread
new Thread() {
// runnable for that thread
public void run() {
//++++++++++//long running task......+++++++++++++++
// update ProgressIndicator on FX thread
Platform.runLater(new Runnable() {
public void run() {
isSaving.set(false); //<<<<<<<<<problem
}
});
}
}.start();
}
Fxml:
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="600.0"
prefWidth="500.0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com
/fxml/1">
<content>
<StackPane>
<children>
<VBox fx:id="parentToDisable">
<!-- shortened -->
<Button fx:id="btnSave" mnemonicParsing="false" onAction="#onSave"
text="Speichern" />
<!-- shortened -->
</VBox>
<ProgressIndicator fx:id="loadingIndicator"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" prefHeight="60.0" prefWidth="60.0"
visible="false" />
</children>
</StackPane>
</content>
</ScrollPane>
That looks like a bug: RT-33261. It seems to be fixed in the latest pre-release of JDK 8u20.
My guess is it's to do with the progress indicator not being visible when the scene graph is first constructed. As a workaround, remove the visible="false" attribute from the ProgressIndicator in the fxml file, and wrap the binding in a Platform.runLater(...) call:
public void initialize(...) {
Platform.runLater(() -> loadingIndicator.visibleProperty().bind(isSaving));
}

Categories

Resources