即使oldVaule和newValue相同,如何使JavaFX Property Listener触发事件? [英] How to make JavaFX Property Listener to fire an event even if the oldVaule and newValue are the same?
问题描述
让我们考虑一个示例代码:
Lets consider a sample code:
SimpleIntegerProperty simpleIntegerProperty = new SimpleIntegerProperty(0);
simpleIntegerProperty.addListener((observable, oldValue, newValue) -> {
// execution code when the event is fired.
});
当我使用setValue()
方法设置新值时,如果oldValue和newValue相同,则不会触发该事件.仅当它们不同时.
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.
示例:
- 我有一个
ListView<Element>
和一个包含某些元素"的ObservableList<Element>
绑定. - 我可以在应用程序的其他位置添加更多元素.
- 有一个开始"按钮,它启动一个过程-它 遍历列表,并对每个元素执行一些操作.
-
Procedure
是不同的类.它会处理元素,还包含SimpleIntegerPorperty
-currentlyChosenElementIndex
来指示当前所选元素的索引.
- I have a
ListView<Element>
binded with anObservableList<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 containsSimpleIntegerPorperty
-currentlyChosenElementIndex
to indicate an index of the currently chosen element.
在处理当前元素时,我希望ListView
能够表明这一点.现在,在此过程中,在继续进行操作的同时,GUI被阻塞,并且在ListView
上选择了当前元素.该过程结束后,应用程序将currentlyChosenElementIndex
重置为零,这是我遇到的问题.该过程开始时,未选择第一个元素,因为与以前相同的应用程序setValue()
.
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?
推荐答案
如果您的Procedure
的currentlyChosenElementIndex
表示当前正在处理的元素的索引,则当 no时将其等于0
元素当前正在处理中,实际上会使您的应用程序处于不一致状态.对于表示索引的内容,通常的惯例是使用-1
表示无值".因此,我认为将currentlyChosenElementIndex
初始化为-1
,并在过程完成后将其重置为-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.)
这确实意味着您在使用该值时必须小心,以免出现任何ArrayIndexOutOfBoundsException
,即您必须检查特殊值并将其分开对待.
This does mean you have to be careful when using that value, to avoid any ArrayIndexOutOfBoundsException
s - i.e. you have to check for the special value and treat it separately.
这是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);
}
}
这篇关于即使oldVaule和newValue相同,如何使JavaFX Property Listener触发事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!