即使oldVaule和newValue相同,如何使JavaFX Property Listener触发事件? [英] How to make JavaFX Property Listener to fire an event even if the oldVaule and newValue are the same?

查看:39
本文介绍了即使oldVaule和newValue相同,如何使JavaFX Property Listener触发事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们考虑一个示例代码:

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 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.

在处理当前元素时,我希望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?

推荐答案

如果您的ProcedurecurrentlyChosenElementIndex表示当前正在处理的元素的索引,则当 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 ArrayIndexOutOfBoundsExceptions - 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆