javaFX中是否有等效的enum属性类型?

问题描述 投票:8回答:1

我想观察在某些Java bean类的某些字段中所做的更改。此字段是枚举类型。要绑定字段和相应的ui控件,我将按照PropertyChangeListener的说明使用here但似乎javaFX没有等效的enum属性类型。类似于javafx.beans.property.EnumProperty我尝试改用StringPropertyObjectProperty,但它不起作用。

UPDATE添加了一个脚本

Observable.java

package sample;

    import java.beans.PropertyChangeListener;
    import java.beans.PropertyChangeSupport;

    public class Observable {

        protected PropertyChangeSupport propertyChangeSupport = null;

        public Observable() {}

        public void setObservableObject (Observable observable) {
            propertyChangeSupport = new PropertyChangeSupport(observable);
        }

        public void addPropertyChangeListener(PropertyChangeListener listener){
            System.out.println("added PropertyChangeListener");
            propertyChangeSupport.addPropertyChangeListener(listener);
        }
    }

Item.java

package sample;


    public class Item extends Observable {

        public enum State {
            INIT, STARTED
        }

        private String name;
        private State state = State.INIT;

        public Item() {
            super();
            super.setObservableObject(this);
        }

        public Item(String name, State state) {
            this();
            setName(name);
            setState(state);
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public State getState() {
            return state;
        }

        public void setState(State state) {
            State oldState = this.state;
            this.state = state;
            System.out.println(String.format("%s: change state from %s to %s",name,oldState.name(),state));
            propertyChangeSupport.firePropertyChange("state", oldState, state);
        }


    }

Main.java

package sample;

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;

    public class Main extends Application {

        @Override
        public void start(Stage primaryStage) throws Exception {
            Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
            primaryStage.setTitle("Item View");
            primaryStage.setScene(new Scene(root, 200, 100));
            primaryStage.show();
        }


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

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.CheckBox?>
    <?import javafx.scene.control.ComboBox?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.layout.AnchorPane?>

    <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="108.0" prefWidth="225.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
             <children>
                <Label layoutX="14.0" layoutY="14.0" text="show item" />
                <ComboBox fx:id="cbxItemsSelector" layoutX="14.0" layoutY="31.0" prefWidth="150.0" />
                <Button fx:id="btnChangeState" layoutX="14.0" layoutY="65.0" mnemonicParsing="false" onAction="#changeItemState" prefHeight="25.0" prefWidth="85.0" text="change state" />
             </children>
    </AnchorPane>

Controller.java

package sample;

    import javafx.beans.Observable;
    import javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.fxml.FXML;
    import javafx.scene.control.ComboBox;
    import javafx.scene.control.ListCell;
    import javafx.scene.control.ListView;
    import javafx.util.Callback;


    import java.util.ArrayList;
    import java.util.List;

    public class Controller {

        @FXML
        private ComboBox<Item> cbxItemsSelector;

        private ObservableList<Item> items;

        public void initialize() {
            loadItems();
            customizeSelectorCellView();
        }

        private void loadItems() {
            List<Item> itemsList = new ArrayList<>();
            itemsList.add(new Item("first item", Item.State.INIT));
            itemsList.add(new Item("second item", Item.State.INIT));

            items = FXCollections.observableList(itemsList, item -> {
                try {
                    return new Observable[]{
                            new JavaBeanObjectPropertyBuilder().bean(item).name("state").build()
                    };
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                    return new Observable[]{};
                }
            });

            cbxItemsSelector.setItems(items);
        }

        private void customizeSelectorCellView() {


            cbxItemsSelector.setButtonCell(new ListCell<Item>() {
                @Override
                protected void updateItem(Item item, boolean empty) {
                    super.updateItem(item, empty);
                    if (empty) {
                        setText("");
                    } else {
                        setText(item.getName());
                    }
                }
            });
            cbxItemsSelector.setCellFactory(
                    new Callback<ListView<Item>, ListCell<Item>>() {
                        @Override
                        public ListCell<Item> call(ListView<Item> p) {
                            ListCell cell = new ListCell<Item>() {
                                @Override
                                protected void updateItem(Item item, boolean empty) {
                                    super.updateItem(item, empty);
                                    if (empty) {
                                        setText("");
                                    } else {
                                        System.out.println(String.format("update %s",item.getName()));
                                        setText(String.format("name: %s\n state: %s\n", item.getName(), item.getState().name()));
                                    }
                                }
                            };
                            return cell;
                        }
                    }
            );
        }

        @FXML
        public void changeItemState() {
            Item selectedItem = cbxItemsSelector.getSelectionModel().getSelectedItem();
            if (selectedItem == null) return;

            selectedItem.setState(Item.State.STARTED);

        }

    }

所以,当我现在运行它时,我得到下一个输出:

first item: change state from INIT to INIT
second item: change state from INIT to INIT
added PropertyChangeListener
added PropertyChangeListener
update first item
update second item
update first item
update second item
update first item
update first item
update second item
first item: change state from INIT to STARTED
second item: change state from INIT to STARTED
Process finished with exit code 0

您可以看到,更改项目状态后,不会调用更新项目。我使用:jdk中包含的jre 1.8.0_91-b15 x64

java javafx javafx-8
1个回答
0
投票

@ rvit34-重命名sample.Observable(我重命名为MyObservable),在import sample.Observable;中显式sample.Item,或更改行:

public class Item extends Observable {

至:

public class Item extends sample.Observable {

它应该可以解决您的问题。下面是:

1)从修改后的示例代码产生的输出

2)对原始源文件所做的所有更改(为简洁起见,我仅包含了这些更改,没有完整的示例代码)

3)引起问题的原因的解释和避免类似性质的未来问题的技术。

我的输出:

run:
first item: change state from INIT to INIT...
...
first item: change state from INIT to STARTED
update first item
second item: change state from INIT to STARTED
update second item
first item: change state from STARTED to INIT
update first item
second item: change state from STARTED to INIT
update second item
BUILD SUCCESSFUL (total time: 32 seconds)

MyObservable.java(从Observable.java重命名)

public class MyObservable {
...
}

Item.java

public class Item extends MyObservable {
    ...
    public MyObservable() {}

    public void setObservableObject (MyObservable observable) {
        ...
    }
}

Controller.java(无需使其工作。已修改为在状态之间循环,而不仅仅是设置为STARTED)

public void changeItemState() {
        Item selectedItem = cbxItemsSelector.getSelectionModel().getSelectedItem();
        if (selectedItem != null) {

            switch (selectedItem.getState()) {
                case STARTED:
                    selectedItem.setState(Item.State.INIT);
                    break;
                case INIT:
                    selectedItem.setState(Item.State.STARTED);
                    break;
                default:
                    break;
            }
        }

    }

问题(以及正确行为的偶然发生)来自名称空间冲突。

sample.Item源自sample.Observable。由于这两个类都是程序包sample的成员,因此您既不必导入sample.Observable,也无需在声明sample.Observable时明确声明您的意思是Item extends Observable,通常这不是问题。因为,您在javafx.beans.Observable中使用了Main,所以似乎使JRE迷惑了(实际上不应该这样做,这可能是您发现的JVM中的错误)。随机成功运行可能是由于系统负载或某些其他随机环境变量或条件导致jvm在加载javafx.beans.Observable之前挂起,直到将Item注册为sample.Observable类型或sample.Observable类型的对象之后在启动时已加载到内存中。

编辑-上面的内容实际上并没有回答帖子标题中提出的问题,但是确实可以解决帖子正文中讨论的问题。为了回答标题问题,SetProperty<E>看起来非常适合用作枚举属性。尚未尝试在提供的示例代码中实现这一点,也许不会,但是我将使用当前正在研究的项目中的一个有效示例进行重新编辑,这使我正在寻找标题问题的答案。

© www.soinside.com 2019 - 2024. All rights reserved.