javafx bidirectional change notification won't work? - java

consider the following situation:
I am binding the string-property of a TextField to a float-property:
TextField txtField;
SimpleFloatProperty floatprop;
void bind(){
txtField.textProperty().bindBidiretional(floatprop, NumberFormat.getInstance);
}
Furthermore, I want to check whether the value is positive and I want to reset the value if necessary:
floatprop.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
SimpleFloatProperty prop = (SimpleFloatProperty)ov;
if(t.floatValue() <= 0)
prop.set(t1.floatValue());
else
prop.set(t.floatValue());
}
});
That does not work. No matter what I type into my textfield, the value is not checked, i.e negative values are not reset.
Edit: With a litte bit of distance, I also see why. On setting prop, the changed-event will be triggered for a handy endless recursion. How can I solve this issue?

For me, it worked to do this check at the beginning of changed:
if (t != null && t.equals(t1))
return;
Strangely, this way sometimes fails as well. Then, there is no other way than using a flag, like this:
MyListener implements ChangeListener<...>{
boolean fired;
public void changed(...){
if(fired)
return;
fired = true;
// Change the variable...
fired = false;
}
}
I don't like the latter way, but it looks like there is no alternative.

Related

Work with Value from PropertyValueFactory in TableView - JavaFx

In my JavaFx project I have a scene with a few views. For the footer of the window I have a class extending from TableView:
public class FooterView extends TableView<Area> implements ViewTemplate {...}
This displays a Table with some data from a .csv-file.
When it comes to assigning the value of the specific presentationmodel property to the cells I do it like that:
TableColumn<Area,Double> powerColumn = new TableColumn<>("Power Overview");
powerColumn.setCellValueFactory(new PropertyValueFactory<>("powerPerArea"));
this.setItems(filePM.getAreas()); //filePm is my filehandler
this.getColumns().addAll(powerColumn, other_columns);
getAreas() looks like this:
public List<Area> readCantonsFromFile() {
try (Stream<String> stream = getStreamOfLines(AREA_FILE_NAME)) {
return stream.skip(1)
.map(l -> new Area(l.split(DELIMITER, 12)))
.collect(Collectors.toList());
}
}
In the constructor of Area i set the properties. One of the properties is the mentioned powerPerArea
private final DoubleProperty powerPerArea = new SimpleDoubleProperty();
...
public void setPowerPerCanton(double powerPerCanton) {
this.powerPerCanton.set(powerPerCanton);
}
My question: Is there a way to change the value in the FooterView before the value is displayed? I tried something like this:
powerColumn.setCellValueFactory(Math.round(new PropertyValueFactory<>("powerPerArea")));
But it seems that I mixed up DoubleProperty, Double and ObservableDouble. Can I even modify the value in here?
The problem: I can not round the value in the setter because I add a double value in a loop through this function:
public void addPowerPerArea(double power){
double sum = getPowerPerCanton() + power;
setPowerPerCanton(sum);
}
And rounding the value in here would give me a wrong result. (rounding not precise enough). I need to do it in the end when all sums are added
You should use the cellValueFactory to determine which data are displayed in the cells: in this case the data returned by your PropertyValueFactory is the actual double value returned from powerPerAreaProperty().get(), which is exactly what you want.
If you want to control how the data are displayed, you should use a cellFactory. So to display the data in a particular format, including limiting the number of decimal places, you can do:
powerColumn.setCellFactory(tc -> new TableCell<Area, Double>() {
#Override
protected void updateItem(Double power, boolean empty) {
super.updateItem(power, empty);
if (empty) {
setText(null);
} else {
setText(String.format("%.0f", power.doubleValue()));
}
}
});
The point here is that you should not modify the data based on how you want to display it; the purpose of having both cellValueFactory and cellFactory is to separate the display of the data from the actual data itself.
An alternative to returning custom cells from a cellFactory would be to use a custom cellValueFactory to return the property formatted as string:
TableColumn<Area, String> powerColumn = new TableColumn<>("Power Overview");
powerColumn.setCellValueFactory(cd -> cd.getValue().powerPerAreaProperty().asString(""%.0f""));
I see two ways to do this. You could:
use the setCellFactory method and in the updateItem method you format it. Should look something like this, haven't tested
powerColumn.setCellFactory(column -> {
return new TableCell<Area, Double>() {
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(Math.round(item), empty);
}
};
});
OR: You could make another property of your Area class that is bound to the existing powerOfArea property but returns the value rounded. I am sure that is somehow possible, you could just override some functions of a readOnlyDoubleProperty but there should be a better way. Maybe via DoubleBindings.

Changing the binded value of a label?

This code binds a Label to an updating SimpleIntegerPropertyValue which is counting down from 10 - 1.
view.OVERALL_PROGRESS_LABEL.textProperty().bind(timeSeconds.divide(100).asString());
How can I bind specific values depending on what currently this timeSeconds value is? For instance if the value of timeSeconds > 500 then display "Greater" otherwise display "Less".
I have tried binding a method which returns an ObservableValue but it is not function correctly. (just manipulating the numbers to see if there is a change)
private void someMethod(){
view.OVERALL_PROGRESS_LABEL.textProperty().bind(test2());
}
private ObservableValue<? extends String> test2() {
ObservableValue<String> test;
if (timeSeconds.getValue() < 500){
test = timeSeconds.multiply(1000).asString();
} else {
test = timeSeconds.divide(1000).asString();
}
return test;
}
You can use Bindings to create bindings based on condition.
view.OVERALL_PROGRESS_LABEL.textProperty().bind(Bindings.when(timeSeconds.
greaterThan(500)).then("Greater").otherwise("Less"));

Find the index of a Java JButton Array

I am writing a Java program in which I have an array of buttons (not a calculator!) and I'm looking for an efficient way to know which JButton was clicked. From what I know of Java so far, the only way to do it is to have them all in the same ActionListener and then loop through looking for a match.
Another solution I just thought of would be to extend JButton to include a unique ID number variable in the constructor. It seems that should work when the event object is cast to JButton after an instanceof check. Kind of like using VB's Tag property which is assigned to the index number.
Is there a better/more elegant way?
Is there a better/more elegant way?
yes to use (for almost JComponents) the put/getClientProperty, there you can to set endless number of properties, can be multiplied in contrast with setName / setActionCommand / etc
getClientProperty can be used as unique identificator for Swing Action or EventHandler (rather than to use ActionListener)
Links to the Javadocs: putClientProperty(), getClientProperty()
Here is an example from programm I writing last few months.
I have an enum called Position
public enum Position {
BB, SB, BU, CO, MP3, MP2, MP1, EP3, EP2, EP1;
}
and have some JToggleButtons each of them holding ist own Position.
public class PositionButton extends JToggleButton {
private final Position p;
public PositionButton(Position p) {
this.p = p;
setText(p.toString());
setActionCommand(p.toString());
}
public Position getPosition() {
return p;
}
}
This allows you to create Buttons in a Loop and get value direct from button without comparing:
ActionListener positionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
PositionButton b = (PositionButton )e.getSource();
Position p = b.getPosition();
//do something with Position
}
}
for (Position p : Position.values()) {
PositionButton b = new PositionButton (p);
b.addActionListener(positionListener);
}
The way I've done it before was using actionPerformed for different buttons. I like it more compared to some other ways I've seen.
public void actionPerformed(ActionEvent clicking)
{
if (clicking.getSource() == button[0])
// Do this
if (clicking.getSource() == button[1])
// Do something different
}
Since you built an array, you can throw the ID right where that 0 is and that's your unique ID.
Add a separate action listener for each button.

Calculation in Wizard

This is more of a general question. We have a lot of wizard, some of which start a long-running process and display the result after. The question is: what is the correct way to do long calculations?
Formerly most wizards did their calculations in DialogPage#setVisible, something like that:
public void setVisible(final boolean visible) {
if (visible) {
getWizard().getContainer().run(true, true, new MyCalculation());
}
super.setVisible(visible);
}
I don't think that's a good idea, since usually getWizard() gets called a lot in these methods. Moreover, usually the parent wizard gets cast to a specific implementation to get input values from or set the result to other pages. So usually it looks something like this:
public void setVisible(final boolean visible) {
if (visible) {
Input input = ((MyCalculationWizard)getWizard()).getInputPage().getInput();
MyCalculation calculation = new MyCalculation(input);
getWizard().getContainer().run(true, true, calculation);
Output output = calculation.getOutput();
((MyCalculationWizard)getWizard()).getOtherPage().setOutput(output);
}
super.setVisible(visible);
}
Just from looking at the code you know that's very bad style.
So we replaced it with something that calculates in Wizard#getNextPage():
public IWizardPage getNextPage(final IWizardPage page) {
final IWizardPage nextPage = super.getNextPage(page);
if (nextPage == this.myResultPage)
getContainer().run(true, true, new MyCalculation());
return nextPage;
}
That way, the wizard is able to fine-tune a lot better than a page would, and the wizard already knows it's pages and can handle input and output a lot better than a page ever could.
The drawback is: getNextPage() gets called a lot for updating the buttons and every time really the wizard feels like it. So while it works for small processes, it does not cut it for long-running ones.
After some more poking around I found the following to work while overriding Wizard#setContainer:
public void setContainer(final IWizardContainer wizardContainer) {
final IWizardContainer oldContainer = getContainer();
if (oldContainer instanceof WizardDialog)
((WizardDialog) oldContainer).removePageChangingListener(this);
super.setContainer(wizardContainer);
if (wizardContainer instanceof WizardDialog)
((WizardDialog) wizardContainer).addPageChangingListener(this);
}
public void handlePageChanging(final PageChangingEvent event) {
final IWizardPage currentPage = (IWizardPage) event.getCurrentPage();
final IWizardPage nextPage = (IWizardPage) event.getTargetPage();
if (currentPage == this.myInputPage && nextPage == this.myResultPage)
getContainer().run(true, true, new MyCalculation());
}
The big advantage here is that the listener only gets called if the wizard wants to jump between pages, and we are able to really fine-tune the calculation (e.g. to not be called when calling 'Previous'). We are even able to not show the next page after all (event.doit = false).
The drawback is the cast of the container to WizardDialog, because potentially it could be an entirely different implementation.
So the question stands: What is the best way to start long processes in wizards?

LWJGL Keyboard loop

I've created a static Input class, that basicly have a method that I can call, which is this:
public static boolean GetKeyDown(int keyCode) {
while(Keyboard.next()) {
Keyboard.enableRepeatEvents(false);
if (Keyboard.getEventKeyState()) {
if (Keyboard.getEventKey() == keyCode) {
return true;
} else {
return false;
}
}
}
return false;
}
And in my game update loop, I've wanted to use this, instead of having to make a single while-loop:
if(Input.GetKeyDown(KeyCode.S)) {
//Something happens
}
if(Input.GetKeyDown(KeyCode.R)) {
//Something happens
}
//etc..
But it seems that only the first one loaded, will work. In this case 'S'. Is there a way for me to do be able to use the others too?
That is because in your GetKeyDown() method, you call Keyboard.next(), when you call that method it removes the Event of the current key from Keyboard, the only gets refilled with Events, when you call Display.update();
NOTE: This method does not query the operating system for new events. To do that, Display.processMessages() (or Display.update()) must be called first.
Source: LWJGL Docs
You Could
Instead you can use the Keyboard.isKeyDown(int key) method, to achieve what you're trying to do.
Though it returns true/false depending on the following.
Returns: true if the key is down according to the last poll()
But that still doesn't quite fix the problem because it relies on the poll() method.
Fixing The Problem
You can fix the problem by creating some custom methods to use with the Keyboard class, as you already did, though as said the Keyboard Events only gets updated when you call the Display.update(); method.
You already got the right idea about which function to create, though you need to split them into, two different methods. You need a secondary method which you call once each time you want to update your keyboard.
public class MyKeyboard {
/*
* Remember that the index where we store the value,
* is the index of the key. Thereby one key might have
* an index of 400, then your array need to have at least
* the same size, to be able to store it.
*/
public static boolean[] keys = new boolean[100]; // 100 is the amount of keys to remember!
public static void update() {
while(Keyboard.next()) {
if (Keyboard.getEventKey() < keys.length) {
keys[Keyboard.getEventKey()] = Keyboard.getEventKeyState();
}
}
}
public static boolean isKeyDown(int key) {
if ((key > 0) && (key < keys.length)) {
return keys[key];
}
return false;
}
}
Remember to only call the MyKeyboard.update() method once per Display.update() I also renamed your GetKeyDown() method to isKeyDown(), because I think that sounds and describes it better, but you can rename it again in your project if you want to.
The above code was made within this answer, without the use of an IDE, etc. So if there's anything wrong with it I apologize, but just comment and I will fix it.
One problem that arises with this method is the lack of rechecking. Since Keyboard.next() only checks the inputs that have occurred in the current frame. A button which was once pressed will remain "pressed" until it is pressed again. I ran into this problem while trying to implement this solution. The answer to this new problem is here:
public static void update() {
for(int i = 0; i < keys.length; i++) {
keys[i] = false;
}
while(Keyboard.next()) {
keys[Keyboard.getEventKey()] = Keyboard.getEventKeyState();
}
}
You must clear the keypresses of the previous frame by setting everything to false.

Categories

Resources