如何使JavaFX属性监听器在oldVaule和newValue相同的情况下触发一个事件?

 2023-02-16    373  

问题描述

让我们考虑示例代码:

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

当我使用setValue()方法设置新值时,如果旧值和newValue相同,则不会触发事件.只有当它们不同时.

如何使JavaFX属性监听器在oldVaule和newValue相同的情况下触发一个事件?

一个示例:

  • 我有一个ListView<Element> ObservableList<Element>包含一些”元素”.
  • 我可以在我的应用程序的不同位置添加更多元素.
  • 有一个按钮”启动”启动一个过程 – 它
    遍布整个列表,并对每个元素执行一些操作.
  • a Procedure是一个不同的类.它用元素做一些事情,还包含SimpleIntegerPorperty – currentlyChosenElementIndex表示当前选择的元素的索引.

进行当前元素进行操作时,我希望ListView显示出来.现在,在过程中,GUI被阻止,并且在进行ListView上选择了当前元素.过程结束后,应用程序将currentlyChosenElementIndex重置为零,这是我有问题的索引.当该过程启动时,未选择第一个元素,因为应用程序setValue()是以前是同一元素.

有什么方法可以改变吗?

推荐答案

如果您的Procedure’s currentlyChosenElementIndex表示当前正在处理的元素的索引,则在当前正在处理 no element 时,将其等于0您的申请处于不一致的状态.代表索引的事物的常规约定是使用-1表示”无值”.因此,我认为将currentlyChosenElementIndex初始化为-1,然后将其重置为-1,当该过程完成后. (这也将与选择模型的选定索引一致.)

这确实意味着您必须在使用该值时要小心,以避免任何ArrayIndexOutOfBoundsException s-即,您必须检查特殊值并分别对其进行处理.

这是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);
    }
}

其他推荐答案

您不能简单地使用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

其他推荐答案

我在DoubleProperty上也有类似的问题,并以此方式解决了:

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

以上所述是小编给大家介绍的如何使JavaFX属性监听器在oldVaule和newValue相同的情况下触发一个事件?,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/34083.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。