JavaFX addListener not working - java

I'm quite a newbie on JavaFX and I need to bind the visible property of a Label in a way that, if the value it represents reaches 0, the Label should be invisible. Also, it needs to be updated when the bounded integerProperty value changes.
This is my code:
#FXML
private Label kingRewardLabel;
// many other stuff between
IntegerProperty kingBonus = mainApp.getLocalModel().getMap().kingBonus();
kingBonus.addListener((observable, oldValue, newValue) -> {
if (newValue.equals(0)) {
kingRewardLabel.setVisible(false);
} else {
kingRewardLabel.setText(String.valueOf(newValue.intValue()));
}
});
// testing the listener
kingBonus.setValue(25);
I have already tried to debug a little but everything seems fine, no error, no exception thrown, just the listener does not work, or at least not as I expect, because the Label still show the default text "Label", instead of "25"

You can use simply bindings to achieve this:
kingRewardLabel.textProperty().bind(kingBonus.asString());
kingRewardLabel.visibleProperty().bind(kingBonus.greaterThan(0));
The Label kingRewardLabel will display the value of the IntegerProperty kingBonus and it is only visible if the displayed value is greater than zero.
But, if you want to stay with listeners:
kingBonus.addListener((obs, oldVal, newVal) -> {
kingRewardLabel.setVisible(newVal.intValue() > 0);
kingRewardLabel.setText(newVal.toString());
});
This is almost the same as your listener in the question, but in that case, if the Label became invisible, it will never become visible again as kingRewardLabel.setVisible(true) is never called.
Finally, to answer your question about why the listener is "not working" - there can be two possible reasons:
1) The Label, which one is displayed is not the Label stored in kingRewardLabel
2) At the time when you call kingBonus.setValue(25);, the value stored in kingBonus is already 25, no changed event will be fired, therefore the listener is not executed at all.

You can go like this:
kingBonus.addListener(l -> {
int value = kingBonus.getValue().intValue();
System.out.println("Entered listener for value:" + value);
if (value == 0)
kingRewardLabel.setVisible(false);
else
kingRewardLabel.setText(value+"");
});
});

Related

Java JSpinner increase value with down arrow

I have a JSpinner that has all integers as model. I want it's value to increase with the down arrow and value to decrease with up arrow,which is the exact opposite of the default usage.
I have already done this using a variable that has the previous value and added a Change Listener. Whenever a value changes i compare it to the previous. If it's about to increase i instead decrease it and vice versa.
I wanted to know if there is another way of doing it. Like a listener who knows which arrow is pressed.
SpinnerNumberModel model = new SpinnerNumberModel(5, 1, 100, -1); // Note: -ve step
Andrew's answer is the way to go. This is another way to achieve it via a ChangeListener, since you mentioned it.
A ChangeListener fires every time the value changes in the spinner field.
The steps are:
Keep a field reference of its current value.
Detect the new value in listener
If the new value is higher than the old value, it means the ↑ arrow pressed
Otherwise, it means the ↓ arrow pressed
According to the change, set the opposite value
An SSCCE of what I mean:
public class SpinnerExample extends JFrame {
private int oldValue;
public SpinnerExample() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
JSpinner spinner = new JSpinner();
spinner.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int newValue = (int) spinner.getValue();
spinner.removeChangeListener(this);
if (newValue > oldValue) //next value button pressed
spinner.setValue(oldValue - (newValue - oldValue));
else
spinner.setValue(oldValue + (oldValue - newValue));
oldValue = (int) spinner.getValue();
spinner.addChangeListener(this);
}
});
add(spinner);
setLocationByPlatform(true);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new SpinnerExample().setVisible(true));
}
}
Note that the listener is removed before changing the value and after the change is added again. If you dont remove-change-add the listener, you will end up with a StackOverFlow error since changing the value will fire the listener again and again.
Also, remember that this in line spinner.removeChangeListener(this); refers to the ChangeListener. Converting this listener to a lambda expression:
spinner.addChangeListener(e -> {
int newValue = (int) spinner.getValue();
spinner.removeChangeListener(this);
if (newValue > oldValue) //next value button pressed
spinner.setValue(oldValue - (newValue - oldValue));
else
spinner.setValue(oldValue + (oldValue - newValue));
oldValue = (int) spinner.getValue();
System.out.println("oldValue:" + oldValue);
spinner.addChangeListener(this);
});
you will not able to remove the listener, since the this will be a reference to SpinnerExample instance and not the listener. Long story short, keep it an anonymous class and not a lambda.
Another way to accomplish is to switch the handlers/listeners of these buttons (probably reflection will have to interfere), but it will require a lot more effort. Plus there is no guarantee that will work for other look and feels, since they are initiated in <LookAndFeel>-ComboBoxUI.

Change TextField Display with RadioButton

I'm having a problem with Label and TextField placement in a gridpane in response to toggled RadioButtons.
The window will display the same options except for two different labels and text fields which depend on which RadioButton the user has selected.(See images attached).
InHouse RB Selected
Outsourced RB Selected
I added the RadioButton objects to a Toggle Group, then coded the "on actions" to add the "Machine ID" or "Company Name" fields to the GridPane as needed when one of the options is selected.
My problem is that I can only select each option once, and the display of the second option only overlaps the first instance instead of replacing it. If I try to switch back again, I get a runtime error(in Netbeans) about adding the same object twice to the grid.
Any code that I have tried that could remove the node from the display had no affect on the menu's behavior.
ArrayList<Label> typeSpecLabels = new ArrayList<Label>();
ArrayList<TextField>typeSpecFields = new ArrayList<TextField>();
typeSpecLabels.add(machineIDLabel);
typeSpecLabels.add(companyLabel);
typeSpecFields.add(machineIDField);
typeSpecFields.add(companyNameField);
inHouseBtn.setOnAction(inHouseSpecificEvent ->
{
typeSpecLabels.add(machineIDLabel);
grid1.add(typeSpecLabels.get(0),0,8,1,1);
grid1.add(typeSpecFields.get(0), 1,8,1,1);
if(outSourceBtn.isArmed() == true){
grid1.getChildren().remove(companyLabel);
grid1.getChildren().remove(companyNameField);
}
});
outSourceBtn.setOnAction(outSourceSpecificEvent ->
{
typeSpecLabels.add(companyLabel);
grid1.add(companyLabel,0,8,1,1);
grid1.add(companyNameField,1,8,1,1);
if(outSourceBtn.isArmed() == true){
grid1.getChildren().remove(machineIDLabel);
grid1.getChildren().remove(machineIDField);
}
});
I have heard that I could try using 2 or 3 different scenes(one for each state of the RadioButtons), so I may try that. But if it can be done the way I have coded it so far, I would prefer to do it that way.
I would suggest to remove all type specific labels and fields from your grid and then add ones that you need. So the code will look like following:
inHouseBtn.setOnAction(inHouseSpecificEvent ->
{
grid1.getChildren().removeAll(machineIDLabel, companyLabel, machineIDField, companyNameField);
grid1.add(machineIDLabel,0,8,1,1);
grid1.add(machineIDField, 1,8,1,1);
});
outSourceBtn.setOnAction(outSourceSpecificEvent ->
{
grid1.getChildren().removeAll(machineIDLabel, companyLabel, machineIDField, companyNameField);
grid1.add(companyLabel,0,8,1,1);
grid1.add(companyNameField,1,8,1,1);
});
This code ended up working, as Pavlo suggested.
Although I just removed the objects specific to the opposing event.
The code works with no errors or overlapping.
inHouseBtn.setOnAction(inHouseSpecificEvent ->
{
grid1.add(machineIDLabel,0,8,1,1);
grid1.add(machineIDField,1,8,1,1);
grid1.getChildren().remove(companyLabel);
grid1.getChildren().remove(companyNameField);
});
outSourceBtn.setOnAction(outSourceSpecificEvent ->
{
grid1.add(companyLabel,0,8,1,1);
grid1.add(companyNameField,1,8,1,1);
grid1.getChildren().remove(machineIDLabel);
grid1.getChildren().remove(machineIDField);
});

JCheckBox Always Returning True/False But Not Changing

Im currently facing an issue where I am able to add a checkbox to the screen and when clicked it swaps between states as required but when it comes to using the selection data to do things, it always returns the original value whether it be true or false;
My first slice of code was just using the isSelected(); method within an if statement that is constantly run each tick. SVelRand is my checkbox which I create using:
SVelRand = new JCheckBox("Random", true);
SVelRand.setBounds(50, 30, 90, 20);
Then the checking code is as below:
boolean sel = SVelRand.isSelected();
if(sel){
System.out.print("Selected");
}else{
System.out.print("Not Selected");
}
When I found this didn't work I tried to implement an item listener underneath where I create the jcheckbox but it still continues to return the initial value set.
SVelRand.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
System.out.println(SVelRand.isSelected());
}
});
I am also drawing a Label and JTextField to the same panel before adding it to the frame incase that has any bearings. And the reason im using setBounds and not a layout is so I can add each component to precise locations to achieve a non standard layout.
Any help will be appreciated;
In your action listener function just keep track of a (class scope) boolean
// way up above everything
public boolean toggleState=false;
// then later
SVelRand.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
this.toggleState=(!this.toggleState);
String message=(this.toggleState) ? "checked" : "unchecked";
System.out.println("Checkbox is "+message);
}
});
If you are starting your checkbox as checked by default, simply change the initial value of toggleState to true

JavaFX widthProperty not refreshing

I am working on a javaFX project in which I need precise control over Node's size.
My code currently correctly updates the prefWidthProperty when needed, and the min and max size is set to use the pref size, yet the widthProperty is not updating.
I have verified this with print statements added as listeners to the properties. PrefWidthProperty updates, widthProperty does not (until I click the Node, possibly giving focus?)
I tried requesting layouts on both itself and its parent, same with focus, did not work.
I currently do not really know how to fix this bug which is very annoying. it seems that currently everything always lags 1 layout pass behind.
Code used to set the size and check for changes:
//Set the size
this.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
this.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
this.prefWidthProperty().bind(getTotalWidthProperty());
public ObservableValue<? extends Number> getTotalWidthProperty() {
return rightArgument.layoutXProperty().add(rightArgument.translateXProperty()).add(rightArgument.widthProperty()).add(H_GAP);
}
//Check for change:
this.prefWidthProperty().addListener(p -> System.out.println("FB_Pref_"+System.currentTimeMillis() + " " + this.getPrefWidth()));
this.widthProperty().addListener(p -> System.out.println("FB______"+System.currentTimeMillis() + " " + this.getWidth()));
As said before, the FB_Pref gets printed correctly on time, FB_____ gets printed way too late.
If you set min/max to pref, this will create a fixed sized node. The min/max/pref sizes will be included during first layout phase. The width "property" reflects the currently set width on your node. So if you do not change the width, your listener will never get an event. You can sysout the width with getWidth, this always returns the current width.
There are two types of listeners in JavaFX, a changelistener and an invalidation listener. You added an invalidation listner with lambdas. This one fires only as needed and not at every change only on a relayout. Look here for more Information: http://docs.oracle.com/javase/8/javafx/properties-binding-tutorial/binding.htm#sthref12
Simply try this one:
this.prefWidthProperty().addListener(
(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
System.out.println("FB_Pref_" + System.currentTimeMillis() + " " + this.getPrefWidth());
});
this.widthProperty().addListener(
(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
System.out.println("FB______" + System.currentTimeMillis() + " " + this.getWidth())
});
This is a long delay to answering the question. I discovered a solution to this when I performed a similar binding task to a prefWidthProperty and prefHeightProperty. It does seem that the width/height of the node is one layout step behind the preferred dimension. Doing the following will solve it (assuming that this extends Node):
prefWidthProperty.addListener((observable, oldValue, newValue) -> {
Platform.runLater(() -> getParent().requestLayout());
};
For some reason, executing the Runnable later on the Application thread works. Each time the preferred dimension is set, requestLayout() is called anyway inside the invalidated() method of MinPrefMaxProperty of the Region class. This is just forcing the update again.

How can I enable a text field when a button is clicked?

before I start, I'm a beginner programmer.
How can I enable a text field when a button is clicked.
I have two frames, one that has the JFields and the other for the exception.
When the exception occurs > setEditable(false)
but what statement should I make to enable the JFields once the user click on okay button -that i've made in the exception-?
I've tried to add static boolean to exception frame, and inside the action performed of this class I initialized that boolean to true.
in the other class, I added an if statment, if that boolean is true, then setEditable(true)
-========-
The point of this program, that when the exception occurs the user cannot enter anything in the fields until he closes the exception window.
I wish you'd help me.
With all love, programmers.
The code of action performed for THE EXCEPTION WINDOW FRAME ( having Okay button. )
public void actionPerformed(ActionEvent e){
{
allow=true; //static boolean
Container TheFrame = OKButton.getParent();
do TheFrame = TheFrame.getParent();
while (!(TheFrame instanceof JFrame));
((JFrame) TheFrame).dispose();
}
The code of action performed for THE MAIN PROGRAM (having three fields, an exception will occur once the user enters non digits )
I added some comments to clarify.
public void actionPerformed(ActionEvent event) {
try{
r =Double.parseDouble(RField.getText());
s=Double.parseDouble(SField.getText());
h=Double.parseDouble(HField.getText());
Cone C = new Cone(r,s,h);//class cone
if (event.getSource() instanceof JButton) {
JButton clickedButton = (JButton) event.getSource();
if (clickedButton == VolumeButton) {
Result.append("VOLUME = "+C.volume()+ "\n");
ifV= true;//this's for clearing the fields for new entries.
}
if (clickedButton == AreaButton) {
Result.append("SURFACE AREA = "+C.surfaceArea()+ "\n");
ifA= true;//this's for clearing the fields for new entries.
}
if(ifA&&ifV){ // clearing the fields for new entries.
SField.setText(CLEAR);
HField.setText(CLEAR);
RField.setText(CLEAR);
ifV=false; ifA= false;}
}
SList.addShape(C);
}
catch(NumberFormatException e){
//Object of type "Exception__" already created
Ex.setVisible(true);//class "Exception__" is the one i've made for Exception window
SField.setText(CLEAR);
HField.setText(CLEAR);
RField.setText(CLEAR);
SField.setEditable(false);
HField.setEditable(false);
RField.setEditable(false);
}/*here, if the user clicked on -that okay in Exception window-
and variable allow initialized to "true" those statements should extend. I guess?
- everything worked correctly except for this ?*/
if(Ex.allow){
SField.setEditable(true);
HField.setEditable(true);
RField.setEditable(true); }
}
THANK YOU ALL IT FINALLY WORKED.
I added
Ex.allow(SField,HField,RField);
to the catch.
and added this method in class Exception__:
public void allow(JTextField js,JTextField jh,JTextField jr){
HField =jh;
SField =js;
RField =jr;
}
finally, to the action performed of class Exception__:
SField.setEditable(true);
HField.setEditable(true);
RField.setEditable(true);
WOHOOOO. It feels so awesome lol. Thanks all. should I delete my question or leave it for others who might face the same problem as mine? :P
Your question needs a lot more detail. But if all you want to to show an 'exception window' and allow the user to do anything else only after she dismisses this window, I think all you need is a MessageDialog:
See JOptionPane
If you need more details to be displayed you can create your own modal JDialog.
See How to Make Dialogs
Make the text field hiden by writing:
jTextfield.setVisible(fasle);
in the constructor of your form code. than use the button event " Action -> Action Performed " and write the code:
jTextfield.setVisible(true);
and thus your text field will be visible only after the button will be clicked.

Categories

Resources