Is it possible to know , from inside a ChangeListener receiving a ChangeEvent from a JSpinner,
which button (increment/decrement) has been pressed?
Short answer : No there's no way to know which button was pressed
Long answer : depending on your model and your change listener, if you do a comparison between the new value and the previous value, it is possible to know if the user went forward or backward.
You can inspect the object firing the event. Perhaps save the value prior to the event and determine whether it went up or down during the event.
Compare the actual value to the previous one. Here is how:
ChangeEvent ce = ...
((JSpinner)ce.getSource()).getPreviousValue();
You can check the new value against the old value by storing the old value:
int currentValue = spinner.getValue();
spinner.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent e) {
int value = spinner.getValue();
if(value > currentValue) {
// up was pressed
} else if(value < currentValue) {
// down was pressed
}
currentValue = value;
}
});
JSpinner is a composite component, it's possible to add mouseListeners to the components it contains. You'd have to experiment a bit to work out how to distinguish the buttons from one another and from the text field. One quick and dirty way would be to check their coordinates.
I'm not sure if you want to iterate over the components contained by the JSpinner itself, or those contained by the container returned by JSpinner.getEditor(), so try both.
Related
Currently, I'm developing a Restaurant management system in java. when I check a checkbox it will add money to the total amount, all I want to know that if I uncheck that checked checkbox again then it will subtract that added amount again. following is my checkbox code
private void jcbWPizzaMouseClicked(java.awt.event.MouseEvent evt) {
double cMeal = Double.parseDouble(jlblCostOfMeal.getText());
double WavePizza = Double.parseDouble(jtxtWP.getText());
double iWavePizza = 350;
if (jcbWPizza.isSelected()) {
i[1] = (WavePizza * iWavePizza) + cMeal;
String pMeal = String.format("%.2f", i[1]);
jlblCostOfMeal.setText(pMeal);
}
}
if you are using JCheckBox there is a method boolean isSelected() so to verify whether the JCheckBox is selected you can try :
if (checkbox.isSelected()) {
// selected, do something...
} else {
// un-selected, do something else...
}
You want to change the value of total amount based on the object jcbWPizza's selected stated. you can add ItemListener to the checkbox
jcbWPizza.addItemListener(new ItemListener() {
#Override public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
//checkbox has been selected //do selected action...
} else {
//checkbox has been deselected //do deselected action...
};
}
})
Your question raises a number of issues that you might not have thought about.
Firstly, by relying on a MouseEvent, you will not capture those times when the checkbox changes state due to some other reason (for example, by code, or if the user uses the keyboard to change selection). Adding an ItemListener would be a more generation approach.
In the future, there might be a number of other things that affect the price. Having the code that updates the price hidden in this checkbox listener seems like the wrong approach. What would be better is to add a listener that just calls a general "update the displayed price" method. Within that method, you could then check the state of each of the widgets that will affect the final price and calculate accordingly. It then doesn't matter if the checkbox is or was hidden or not, as each time it is asked to update, it will calculate the total from scratch.
Finally, bear in mind that Swing is not thread-safe. Whilst a single Thread will call your listeners, there is no guarantee that only a single Thread will be calling your "recalculate the price" method. Ensure that if more than one Thread should call your code at the same time, you don't end up getting your state out of sync. Avoiding a "the checkbox has been toggled, so add or subtract" logic is a good idea once again, as it adds additional state that needs to be kept in sync.
I am using cell factory for listview with checkboxes like:
listView.setCellFactory(CheckBoxListCell.forListView(new Callback < Bean, ObservableValue < Boolean >> () {
#Override
public ObservableValue < Boolean > call(Bean item) {
BooleanProperty observable = new SimpleBooleanProperty();
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
if (!beanChoices.contains(item.toString())) {
beanChoices.add(item.toString());
observable.setValue(true);
//listView.scrollTo(listView.getItems().size() - 1);
}
} else if (wasSelected) {
if (beanChoices.contains(item.toString())) {
beanChoices.remove(item.toString());
observable.setValue(false);
}
}
});
/* [Code] which compares values with bean item string value and select observable to true for that for edit mode
but here the observer not called for beanItem that are under scrollpane of listview. But on scroll it gets called. */
return observable;
}
}));
It works fine but not for all cases.
Case: When I have say more than 10 entries, the scrollpane comes. Say I have beanChoices to be checked that are at 8 or 9 index(you have to scroll to view them). The listener is not called for the items not visible(that are under scrollpane). On Debug, I found that listener is called when I scroll down.
Problem: when I get checked values from beanChoices for above case, it return empty.
Detail: I have beanChoices which I need to make checked for listview items (edit mode). When I update without changing anything. (Assume that the value which is under the scrollpane of listview will be selected and added to beanChoices)
The Callback is used to retrieve the property for the checked state when the item is associated with a cell. The item may be removed from a cell and put in a new one at any time. This is how ListView (and similar controls like TableView) works. CheckBoxListCell simply gets the checked state property every time a new item is associated with the cell.
The return value is also used to set the initial state of the CheckBox. Since you do not properly initialize the property with the correct value the initial state is not preserved.
Also note that it makes little sense to update the value of the property to the new value in the change listener. It happens anyway.
Since BooleanProperty is a wrapper for primitive boolean the possible values are true and false; the ChangeListener only gets called when !Objects.equals(oldValue, newValue) you can be sure that isNowSelected = !wasSelected.
Of course you also need to return the value:
#Override
public ObservableValue < Boolean > call(Bean item) {
final String value = item.toString();
BooleanProperty observable = new SimpleBooleanProperty(beanChoices.contains(value));
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
beanChoices.add(value);
} else {
beanChoices.remove(value);
}
});
return observable;
}
I also recommend using a Collection of Beans instead of relying on the string representation of the objects. toString many not produce unique results and Beans.equals would be the better choice to compare the objects.
I have an JFormattedTextField created by
JFormattedTextField(NumberFormat.getInstance);
I would like to augment its behaviour, so that if the user
enters a zero and the field loses focus, it reacts exactly as if the user had entered e.g. "foo".
Using an InputVerifier somehow wrecked the reverting behaviour, and using a custom subclass of DecimalFormat did not revert when zero was entered, but instead cleared the field.
(By zero I mean, anything that parses to BigDecimal.ZERO.)
The code I used:
new DecimalFormat(){
{
setParseBigDecimal(true);
}
public Number parse(String txt, ParsePosition pos){
BigDecimal num = (BigDecimal) super.parse(txt, pos);
if(num == null || num.compareTo(BigDecimal.ZERO) == 0)
return null;
else return num;
}
Zero was not accepted then, but the field only reverted on letters entered.
You can add a FocusListener to do a similar check to what is done internally:
JFormattedTextField ftf = new JFormattedTextField(NumberFormat.getInstance());
ftf.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
Object lastValid = ftf.getValue();
try {
ftf.commitEdit();
} catch (ParseException e1) {
ftf.setValue(lastValid);
}
Object newValue = ftf.getValue();
if (newValue instanceof Long)
if ((Long) newValue == 0l)
ftf.setValue(lastValid);
}
});
Notes on this approach:
A JFormattedTextField has a focusLostBehavior which instructs what to do on focus lost. I assumed it will always be COMMIT_OR_REVERT (the default).
Be careful if you registered a PropertyChangeListener to the text field, as I did not handle firing its events carefully. While inputs which can't be parsed will "immediately" be reverted (and not fire a PropertyChangeEvent event), a value which is parsed to 0 will first be committed (and fire a PropertyChangeEvent) and only then reverted (firing a PropertyChangeEvent again).
Notes on other approaches:
While I think this is the most direct approach, there are almost certainly other ways to accomplish this which involve extending and overriding some methods. The hierarchy from the text field itself to the format chosen for it can be a bit complicated and modification can be done in some of the steps, though care should be taken to not break anything.
Input verification can also work, but it behaves differently - it holds the focus until the input is verified, instead of allowing its loss and reverting. It is ultimately the programmer's choice.
Putting something similar to the code by #user1803551 into a PropertyChangeListener worked even better - before, there was an issue with hitting "return" when there was a default button in the form - it would receive "action performed" without the user having to change the input value. Plus, we get by without an additional variable.
DecimalFormat format = new DecimalFormat();
format.setParseBigDecimal(true);
JFormattedTextField ftf = new JFormattedTextField(format):
ftf.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("value"))
if ( ((BigDecimal) evt.getNewValue()).compareTo(BigDecimal.ZERO) ==0 )
ftf.setValue(evt.getOldValue());
}
});
I have a checkbox. I will obtain one value from a database to determine whether the checkbox can be edited or not. If this value is zero, the checkbox should not be selected. How do I achieve that in code? Please help me out here. This is my code:
String status = "0"; // (obtained from the database)
if(status)
{
// should not be editable - can't be selected.
} else {
// can be selected.
}
If this is REALLY what you want to do instead of using a JLabel with appropriate text and/or icon, you can create an action listener for the checkbox and have it call setSelected:
// the action listener for the checkbox
private void myCheckBoxActionPerformed(java.awt.event.ActionEvent evt)
{
if (status.equals("0")
myCheckBox.setSelected(false);
else
myCheckBox.setSelected(true);
}
To say the least, this isn't an elegant solution, but it does give the appearance that the checkbox isn't editable.
Use the setEnabled method for that.
I have a jFormattedTextField in my program and I need to update a jLabel's text when jFormattedTextField value has been changed validly.
Actually jFormattedTextField gets a number and jLabel displays diffrence between this number and another number.
I currently do this by listenning to "FocusLost" event of jFormatted text.
How can i do this?
register a PropertyChangeListener for the property "value" to the formattedField
PropertyChangeListener l = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
String text = evt.getNewValue() != null ? evt.getNewValue().toString() : "";
label.setText(evt.getNewValue());
}
};
formattedTextField.addPropertyChangeListener("value", l);
Do not use DocumentListener nor FocusListener: the former is notified too often (on every keytyped, before parsing happened) the latter is too brittle.
Probably the easiest way to do this is to use a javax.swing.event.DocumentListener that you attache to the text field. Then, as the user types, the label can be updated.
I don't remember the exact sequence, but the listener's insertUpdate() may be called before the formatted text field is validated. So, you may also need to check for valid numbers in your listener too.