[英]How to make JavaFX Property Listener to fire an event even if the oldVaule and newValue are the same?
讓我們考慮一個示例代碼:
SimpleIntegerProperty simpleIntegerProperty = new SimpleIntegerProperty(0);
simpleIntegerProperty.addListener((observable, oldValue, newValue) -> {
// execution code when the event is fired.
});
當我使用setValue()
方法設置新值時,如果oldValue和newValue相同,則不會觸發該事件。 僅當它們不同時。
一個例子:
ListView<Element>
與一個包含一些“元素”的ObservableList<Element>
ListView<Element>
綁定。 Procedure
是不同的類。 它做一些事情的元素,也包含SimpleIntegerPorperty
- currentlyChosenElementIndex
來指示當前選擇的元素的索引。 當當前元素正在處理時,我希望ListView
能夠顯示出來。 現在,在此過程中,GUI被阻止,並在繼續進行操作時在ListView
上選擇了當前元素。 該過程結束后,應用程序會重置currentlyChosenElementIndex
為零,這與我有我的問題的一個指標。 該過程開始時,未選擇第一個元素,因為應用程序setValue()
與先前的相同。
有什么辦法可以改變嗎?
您不能僅通過使用SimpleIntegerProperty
類來執行此SimpleIntegerProperty
。 但是您可以擴展類並添加所需的功能。 創建這樣的課程
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);
}
}
然后您可以使用它,並在調用setValue
或set
方法時收到通知
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);
將輸出
1
0
如果Procedure
的currentlyChosenElementIndex
表示當前正在處理的元素的索引,那么當當前沒有正在處理的元素時,將其等於0
實質上會使您的應用程序處於不一致狀態。 對於表示索引的內容,通常的約定是使用-1
表示“無值”。 因此,我認為它會更有意義初始化currentlyChosenElementIndex
至-1
,並重置到-1
的程序完成時。 (如果什么也沒有選擇,這也將與選擇模型的選擇索引保持一致。)
這的確意味着您在使用該值時必須小心,避免任何ArrayIndexOutOfBoundsException
即您必須檢查特殊值並將其分開對待。
這是一個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);
}
}
我在DoubleProperty中遇到了類似的問題,並通過以下方式解決了問題:
class DelicateSimpleDoubleProperty extends SimpleDoubleProperty{
@Override
public void set( double newValue ) {
if (get() == newValue){
super.set(newValue + 0.0000000000001);
}else {
super.set(newValue);
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.