How to make JavaFX Property Listener to fire an event even if the oldVaule and newValue are the same?

十年热恋 提交于 2020-01-04 06:32:53

问题


Lets consider a sample code:

SimpleIntegerProperty simpleIntegerProperty = new SimpleIntegerProperty(0);
simpleIntegerProperty.addListener((observable, oldValue, newValue) -> {
  // execution code when the event is fired.
});

When I set a new value using a setValue() method, if the oldValue and newValue are the same, the event is not fired. Only when they differ.

An example:

  • I have a ListView<Element> binded with an ObservableList<Element> containing some "elements".
  • I can add more elements in a different place of my application.
  • There's a button "Start" which launches a procedure - it iterates throught the list and does some actions with every element.
  • A Procedure is a different class. It does some stuff with elements and also it contains SimpleIntegerPorperty - currentlyChosenElementIndex to indicate an index of the currently chosen element.

When the current element is being proceeded, I'd like the ListView to show that. Now, during the procedure, the GUI is blocked and the current element is selected on the ListView, while being proceeded. After the end of the procedure the application resets currentlyChosenElementIndex to zero and this is an index with I have my problem. When the procedure starts, the first element is not selected because the application setValue() to the same one that was previously.

Is there any way to change that?


回答1:


If your Procedure's currentlyChosenElementIndex represents the index of the element currently being processed, then having it equal to 0 when no element is currently being processed essentially leaves your application in an inconsistent state. The usual convention for something that represents an index is to use -1 to represent "no value". So I think it would make more sense to initialize currentlyChosenElementIndex to -1, and reset it to -1 when the procedure is complete. (This would also be consistent with the selected index of the selection model when nothing is selected.)

This does mean you have to be careful when using that value, to avoid any ArrayIndexOutOfBoundsExceptions - i.e. you have to check for the special value and treat it separately.

Here's a SSCCE:

import java.util.List;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class ProcessListElements extends Application {

    private int count = 0 ;

    @Override
    public void start(Stage primaryStage) {
        ListView<String> listView = new ListView<>();
        for (int i = 0 ; i < 10 ; i++) addElement(listView.getItems());

        Procedure procedure = new Procedure();

        Button startProcessButton = new Button("Start Process");
        Button addItemButton = new Button("Add item");
        Button deleteItemButton = new Button("Delete item");

        TextArea log = new TextArea();

        startProcessButton.setOnAction(e -> {
            log.clear();
            listView.requestFocus();
            new Thread(() -> procedure.process(listView.getItems())).start();
        });

        addItemButton.setOnAction(e -> addElement(listView.getItems()));
        deleteItemButton.setOnAction(e -> listView.getItems().remove(listView.getSelectionModel().getSelectedIndex()));
        deleteItemButton.disableProperty().bind(listView.getSelectionModel().selectedItemProperty().isNull());

        HBox controls = new HBox(5, startProcessButton, addItemButton, deleteItemButton);
        controls.setAlignment(Pos.CENTER);
        controls.setPadding(new Insets(5));


        BorderPane root = new BorderPane(listView, null, log, controls, null);

        procedure.currentlyChosenElementIndexProperty().addListener((obs, oldIndex, newIndex) -> {
            Platform.runLater(() -> 
                listView.getSelectionModel().clearAndSelect(newIndex.intValue()));
        });

        procedure.currentlyChosenElementIndexProperty().addListener((obs, oldIndex, newIndex) -> {
            Platform.runLater(() -> {
                controls.setDisable(newIndex.intValue() != Procedure.NO_ELEMENT);
            });
        });

        procedure.currentlyChosenElementIndexProperty().addListener((obs, oldIndex, newIndex) -> {
            if (oldIndex.intValue() != Procedure.NO_ELEMENT) {
                log.appendText("Processing of element "+oldIndex.intValue()+" complete\n");
            }
            if (newIndex.intValue() != Procedure.NO_ELEMENT) {
                log.appendText("Processing element "+newIndex.intValue()+" started\n");
            }
        });


        Scene scene = new Scene(root, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void addElement(List<String> list) {
        count++ ;
        list.add("Item "+count);
    }

    public static class Procedure {

        private static final int NO_ELEMENT = - 1; 

        private final ReadOnlyIntegerWrapper currentlyChosenElementIndex = new ReadOnlyIntegerWrapper(NO_ELEMENT);

        public void process(List<?> items) {
            if (Platform.isFxApplicationThread()) {
                throw new IllegalStateException("This method blocks and must not be executed on the FX Application Thread");
            }
            try {
                for (int i = 0 ; i < items.size(); i++) {
                    currentlyChosenElementIndex.set(i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            currentlyChosenElementIndex.set(NO_ELEMENT);
        }

        public final ReadOnlyIntegerProperty currentlyChosenElementIndexProperty() {
            return this.currentlyChosenElementIndex.getReadOnlyProperty();
        }


        public final int getCurrentlyChosenElementIndex() {
            return this.currentlyChosenElementIndexProperty().get();
        }

    }

    public static void main(String[] args) {
        launch(args);
    }
}



回答2:


You cannot do this by simply using SimpleIntegerProperty class. But you can extend class and add the required functionality. Create a class like this

public class NotifySetIntegerProperty extends SimpleIntegerProperty {
    private OnSetValueListener valueListener;

    public NotifySetIntegerProperty(int initialValue) {
        super(initialValue);
    }

    @Override
    public void set(int newValue) {
        super.set(newValue);
        if(valueListener!= null) {
            valueListener.onValueSet(newValue);
        }
    }

    public void setValueListener(OnSetValueListener valueListener) {
        this.valueListener = valueListener;
    }

    public interface OnSetValueListener {
        void onValueSet(int value);
    }
}

Then you can use it and be notified when setValue or set method is called

NotifySetIntegerProperty property = new NotifySetIntegerProperty(0);
property.setValueListener(new NotifySetIntegerProperty.OnSetValueListener() {
    @Override
    public void onValueSet(int value) {
        System.out.println(value);
    }
});
property.setValue(1);
property.setValue(0);

Will output

1
0



回答3:


I had a similar problem with DoubleProperty and solved it this way:

class DelicateSimpleDoubleProperty extends SimpleDoubleProperty{
            @Override
            public void set( double newValue ) {
                if (get() == newValue){
                    super.set(newValue + 0.0000000000001);
                }else {
                    super.set(newValue);
                }
            } 
}


来源:https://stackoverflow.com/questions/43393423/how-to-make-javafx-property-listener-to-fire-an-event-even-if-the-oldvaule-and-n

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!