I'm working on a GUI calculator and am having trouble modifying the text field at the top of the calculator when a button is pressed. My text field is labeled 'text', but in the listener class I'm having trouble referencing it. This is my listener class:
class ClearListener implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent e) {
calculator1.clear();
text.setText(0.0);
}
}
Obviously the reference to the TextField in the ClearListener class doesn't work because the scope doesn't include that class, but I'm not sure how else I would reference it.
I tried this:
public class UserInterfaceCalculator extends Application {
private MemoryCalculator calculator1 = new MemoryCalculator();
private String operator = null;
private TextField text = new TextField("0.0");
private Boolean equalsPressed = true;
#Override
public void start(Stage primaryStage) throws Exception {
This fixes the scope issue, and the text field still shows correctly with the initial value of 0.0, but then the value doesn't change from 0.0 even when I set a new value in the listener classes upon a button press.
Related
I am trying to disable the OK button in a javaFX dialog untill all of the text fields have content.
Due to the ButtonType not having FXML support it has to be added to the Dialog in the Controller class of the main Window. due to this I'm unable to (cant find a way) to link the button to a variable inside the dialog controller.
I have tried handling the process in the main Controller class as follows:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("addDialog.fxml"));
try {
dialog.getDialogPane().setContent(loader.load());
} catch(IOException e) {
e.getStackTrace();
}
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(true);
AddDialogController controller = loader.getController();
// below calls on a getter from the addDialogController.java file to check if the input fields are full
if (controller.getInputsFull()) {
dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(false);
}
unfortunately this didn't work, the above code can only be run once before or after the window is called and cant run during.
so is there a way to access the OK ButtonType that comes with javaFX inside the dialog controller if it has been declared outside?
Or is there another way to disable the button based of information from the dialog controller that is being updated by the user?
thanks for any help
Edit 1:
As requested the addDialogController, this is very bare bones and incomplete, hopefully it helps:
import data.Contact;
import data.ContactData;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
public class AddDialogController {
#FXML
private TextField firstNameField;
#FXML
private TextField lastNameField;
#FXML
private TextField numberField;
#FXML
private TextArea notesArea;
private boolean inputsFull;
public void processResults() {
String first = firstNameField.getText().trim();
String last = lastNameField.getText().trim();
String number = numberField.getText().trim();
String notes = notesArea.getText().trim();
Contact contact = new Contact(first, last, number, notes);
// ContactData.add(contact);
}
#FXML
public void handleKeyRelease() {
boolean firstEmpty = firstNameField.getText().trim().isEmpty() && firstNameField.getText().isEmpty();
boolean lastEmpty = lastNameField.getText().trim().isEmpty() && lastNameField.getText().isEmpty();
boolean numberEmpty = numberField.getText().trim().isEmpty() && numberField.getText().isEmpty();
boolean notesEmpty = notesArea.getText().trim().isEmpty() && notesArea.getText().isEmpty();
inputsFull = !firstEmpty && !lastEmpty && !numberEmpty && !notesEmpty;
System.out.println(firstEmpty);
System.out.println(lastEmpty);
System.out.println(numberEmpty);
System.out.println(notesEmpty);
System.out.println(inputsFull);
System.out.println();
}
public boolean isInputsFull() {
return this.inputsFull;
}
First, delete your handleKeyRelease method. Never use key event handlers on text input components: for one thing they will not work if the user copies and pastes text into the text field with the mouse. Just register listeners with the textProperty() instead, if you need. Also, isn't (for example)
firstNameField.getText().trim().isEmpty() && firstNameField.getText().isEmpty()
true if and only if
firstNameField.getText().isEmpty();
is true? It's not clear what logic you are trying to implement there.
You should simply expose inputsFull as a JavaFX property:
public class AddDialogController {
#FXML
private TextField firstNameField;
#FXML
private TextField lastNameField;
#FXML
private TextField numberField;
#FXML
private TextArea notesArea;
private BooleanBinding inputsFull ;
public BooleanBinding inputsFullBinding() {
return inputsFull ;
}
public final boolean getInputsFull() {
return inputsFull.get();
}
public void initialize() {
inputsFull = new BooleanBinding() {
{
bind(firstNameField.textProperty(),
lastNameField.textProperty(),
numberField.textProperty(),
notesArea.textProperty());
}
#Override
protected boolean computeValue() {
return ! (firstNameTextField.getText().trim().isEmpty()
|| lastNameTextField.getText().trim().isEmpty()
|| numberField.getText().trim().isEmpty()
|| notesArea.getText().trim().isEmpty());
}
};
}
public void processResults() {
String first = firstNameField.getText().trim();
String last = lastNameField.getText().trim();
String number = numberField.getText().trim();
String notes = notesArea.getText().trim();
Contact contact = new Contact(first, last, number, notes);
// ContactData.add(contact);
}
}
and then all you need is
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
AddDialogController controller = loader.getController();
dialog.getDialogPane().lookupButton(ButtonType.OK)
.disableProperty()
.bind(controller.inputsFullBinding().not());
It seems values of nodes (textfields, checkboxes, etc...) can only be accessed via an event listener on them. Why am I saying that ?
- First I'm new to JavaFx
- Second : below is the scenario I'm using
public class FXMLDocumentController implements Initializable {
#FXML
private JFXCheckBox myCheckBox;
#FXML
private JFXButton myButton;
#Override
public void initialize(URL url, ResourceBundle rb) {
myCheckBox.addEventHandler(MouseEvent.MOUSE_CLICKED, (e)->{
if(myCheckBox.isSelected()){
System.out.Println("Printed from event handler");
});
myButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) -> {
myMethod();
});
}
public void myMethod(){
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
for(Node node : root.getChildrenUnmodifiable()){
if(node.getId().equals("myCheckBox"){
JFXCheckBox myChk = (JFXCheckBox) node;
if(myChk.isSelected()){
System.out.Println("Printed from my inner method");
}
System.out.Println(myChk.isSelected());
}
}
}
}
What happend is when I check the box, the event handler does how supposed. It prints Printed from event handler. But right afetr that, when clicking myButton, the myMethod always prints false.
When I use a class variable indicating if the checkbox is selected and read it in myMethod, it says the check box has been checked ...
What is going on ?
The same thing happens with textfields ...
I have a root layout with multiples Tabs. From my main App I open the root layout. There I included multiple FXMLs with their own controllers.
I am trying to pass the main controller to one of the Tabes controller.
The issue I am having, everything works as expected, but I get a null exception when I try to click on an action button from the new tab.
RootLayout FXML
<fx:indlue fx:id="myNewTabAnchorPane" source="NewTabFXML.fxml"/>
RootLayout Controller
#FXML NewTabController newTabController;
mainTabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){
#override
public void change(ObservableValue<? extends Tab> observable, Tab oldValue, Tab newValue){
if(newValue == myTab){
newTabController.setMyRootController(this);
}
NewTabController
public void setMyRootController(RootController rootController){
this.rootController = rootController;
System.out.println(rootController.getID); // this prints fine
}
However, if I trigger this action I get blank from the same controller
#FXML
public void createAction(ActionEvent event) throws IOException{
System.out.println(rootController.getID); // with this I get null value.
}
What am I missing?
Here is the problem:
#FXML NewTabController newTabController;
It should be myNewTabAnchorPaneController which is not a partially lowercased class name, but fx:id + Controller concatenation.
I am trying to update a text field through observer pattern. The update function in the observer (FXML controller) is called after clicking on a listItem in another controller class. And that works fine. The only problem is that my textfield won't update.
Here is my update function in the observer.
#Override
public void update(Observable o, final Object arg) {
System.out.println("test"); // works
firstNameTextField.setText("test"); // doesn't work (text field is still empty)
System.out.println(firstNameTextField.getText()); //works and shows me the word "test" on my console
}
The funny thing is, if I print the text from the text field on my console it's printing the word "test" on the console. It seems like the text field value is updated but it doesn't show up on the ui.
EDIT:
This is my MainController
public class MainController extends Observable implements Initializable {
private ObservableList<String> items = FXCollections.observableArrayList("item1", "item2");
private List<UserProfile> userProfiles = new ArrayList<UserProfile>();
private String[] tabTitles = { "Profile"};
#FXML
private TabPane tabPane;
#FXML
ListView<String> listView;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
for (String tabTitle : tabTitles) {
Tab tab = new Tab(tabTitle);
tab.setClosable(false);
tabPane.getTabs().add(tab);
}
tabPane.getSelectionModel().clearSelection();
for (Tab tab : tabPane.getTabs()) {
try {
String newStringValue = tab.getText();
Parent root = FXMLLoader.load(getClass().getResource("profile.fxml"));
tab.setContent(root);
FXMLLoader fxmlLoader = new FXMLLoader();
Object p = fxmlLoader.load(getClass().getResource("profile.fxml").openStream());
if (fxmlLoader.getController() instanceof ProfileController) {
ProfileController profileController = (ProfileController) fxmlLoader.getController();
this.addObserver(profileController);
}
} catch (IOException e) {
e.printStackTrace();
}
}
tabPane.getSelectionModel().selectFirst();
listView.setItems(items);
listView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
setChanged();
notifyObservers();
}
});
}
}
ProfileController
public class ProfileController implements Initializable, Observer {
#FXML
TextField firstNameTextField;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
}
#Override
public void update(Observable o, final Object arg) {
System.out.println("test"); // works
firstNameTextField.setText("test"); // doesn't work (text field is still empty)
System.out.println(firstNameTextField.getText()); //works and shows me the word "test" on my console
}
}
Can anybody help me out with this?
Thanks!
When you execute
Object p = fxmlLoader.load(getClass().getResource("profile.fxml").openStream());
if (fxmlLoader.getController() instanceof ProfileController) {
ProfileController profileController = (ProfileController) fxmlLoader.getController();
this.addObserver(profileController);
}
you load the structure represented by profile.fxml, and place that hierarchy (including firstNameTextField) in the object you called p. When you invoke update(...) on profileController, it changes the text in the text field that is part of the hierarchy of p. However, you never do anything with p: you don't display it in your UI. So when you change the text of the text field, the changes are of course invisible (because you are changing a text field that isn't displayed).
Presumably, since you said you have the text field displayed, somewhere in the code you couldn't be bothered to include you are loading profile.fxml and displaying the content in the UI. You need to get the reference to that controller, and register it as an observer. Registering an arbitrary instance of the same class will not have the desired effect.
My JavaFx FXML application has an issue.
When I select a checkbox on a form, I want to execute a method based on the checkbox that was clicked. Is there any way that I can pass the name of the checkbox through to the method so I can perform some conditional work on it?
I have two checkboxes and only one can be selected. When I click on one, the other should be de-selected and vice versa. Obviously the code below will not work so I am looking to pass the name of the object that was clicked.
Any help would be appreciated,
many thanks.
#FXML private void updateRcs(){
if (chkRcsuri.isSelected()){
chkRcsuri2.setSelected(false);
}
if (chkRcsuri2.isSelected()){
chkRcsuri.setSelected(false);
}
}
You can use change tracking or use Event handling mechanism of JavaFX.
With checkboxes like this,
final CheckBox chk1 = new CheckBox("chk 1");
final CheckBox chk2 = new CheckBox("chk 2");
Change tracking
chk1.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
chk2.setSelected(!newValue);
}
});
chk2.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
chk1.setSelected(!newValue);
}
});
Using event handling
EventHandler eh = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (event.getSource() instanceof CheckBox) {
CheckBox chk = (CheckBox) event.getSource();
System.out.println("Action performed on checkbox " + chk.getText());
if ("chk 1".equals(chk.getText())) {
chk2.setSelected(!chk1.isSelected());
} else if ("chk 2".equals(chk.getText())) {
chk1.setSelected(!chk2.isSelected());
}
}
}
};
chk1.setOnAction(eh);
chk2.setOnAction(eh);
Wouldn't radio buttons give you a mutually exclusive selection? Just make sure you set the groupname to be the same - selecting one would then automatically de-select the other and you can just put additional logic in the Action event.
Better than trying to re-write same functionality around checkboxes.
So I was trying to do a similar thing, except I had multiple checkboxes and then one that would be nonsensical to have selectable in conjuction with the others. I made two seperate listeners and set one general purpose one to the main boxes, and a specialized one to the exception.
#FXML private CheckBox redCB = new CheckBox();
#FXML private CheckBox blueCB = new CheckBox();
#FXML private CheckBox greenCB = new CheckBox();
#FXML private CheckBox whiteCB = new CheckBox();
#FXML private CheckBox blackCB = new CheckBox();
#FXML private CheckBox colorlessCB = new CheckBox();
//assigning listeners
redCB.selectedProperty().addListener(colorCheckChange);
blueCB.selectedProperty().addListener(colorCheckChange);
greenCB.selectedProperty().addListener(colorCheckChange);
whiteCB.selectedProperty().addListener(colorCheckChange);
blackCB.selectedProperty().addListener(colorCheckChange);
colorlessCB.selectedProperty().addListener(colorlessCheckChange);
//note: this is the only different one^^^
//making listeners
ChangeListener colorCheckChange = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
if (new_val)
colorlessCB.setSelected(false);
}};
ChangeListener colorlessCheckChange = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
if (new_val)
{
redCB.setSelected(false);
blueCB.setSelected(false);
greenCB.setSelected(false);
blackCB.setSelected(false);
whiteCB.setSelected(false);
}
}
};
The first one basically just makes sure that colorlessCB isn't selected while trying to select the other colors, and vice verca. This way you also avoid the problem of de-selecting one, and the other one automatically reselecting itself.
This is my solution. But be sure about the a variable to be appropriate for you
//First in FXML file
<CheckBox fx:id="chkbxAuto" mnemonicParsing="false" onAction="#autoConfig" text="Auto" />
// in controler
public class FXMLController implements Initializable {
private static int a = 0;
//references to lables
....
#FXML
private Label lblStateValue;
#FXML
private Group grpSetting; // a group of elements which I want to be disabled and enabled
...
#FXML
private void autoConfig(ActionEvent event) {
System.out.println("Configing automatically");
a++;
if(a%2==1){
lblStateValue.setText("Auto configuration enabled"); // it change a lable to show the state
grpSetting.setDisable(true); // it disable a group of elements
}
else{
lblStateValue.setText("Auto configuration disabled");
grpSetting.setDisable(false);
}
a%=10;
}
...
I know this question is pretty old, but I found when looking for the same problem. I found a solution which
a) seems clearer (at least to me comparing to the listener definition) for reading source code and
b) Defining a changeListener on a checkBox gave me some problems.
Anyway my solution was to define an onAction function for the checkbox.
yourCheckboxName.setOnAction(this::aFunctionName);
...
void aFunctionName() {
if(yourCheckboxName.isSelected()) {
doThis()
} else {
doThat()
}
}
Attention This needs Java8 or higher.
None of the above options take advantage of the most commpact lambda expresions that can be used to add this very repetitive listeners.
Let suppose you have two checkboxes:
chk1.selectedProperty().addListener((observable, oldValue, newValue) -> chk2.setSelected(!newValue));
chk2.selectedProperty().addListener((observable, oldValue, newValue) -> chk1.setSelected(!newValue));