【问题标题】:ComboBox does not observe changes made in SimpleObjectPropertyComboBox 不观察 SimpleObjectProperty 中所做的更改
【发布时间】:2016-10-27 22:18:17
【问题描述】:

假设我有这样简单的域 POJO 类:

package sample;

    public class Item {

        private String name;
        private String state;

        public Item() {}

        public Item(String name, String state) {
            this.name = name;
            this.state = state;
        }

        public String getName() {
            return name;
        }

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

        public String getState() {
            return state;
        }

        public void setState(String state) {
            this.state = state;
        }
  }

并假设我有一个限制:我不能对这个类进行任何更改。所以我需要写一个包装器,对吧?

package sample;

    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;

    public class ItemWrapper {

        private ObjectProperty<Item> itemProperty = new SimpleObjectProperty<>();

        private Item item;


        public ItemWrapper(Item item) {
            setItem(item);
        }

        public Item getItem() {
            return itemProperty.get();
        }

        public void setItem(Item item) {
            this.item = item;
            itemProperty.set(item);
        }

        public ObjectProperty<Item> itemProperty(){
            return itemProperty;
        }

    }

然后假设我有一个小的 JavaFX 应用程序,它可以显示项目列表并有可能更改项目的状态。来看看吧。

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.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.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;
    import java.util.stream.Collectors;

    public class Controller {

        @FXML
        private ComboBox<ItemWrapper> cbxItemsSelector;

        private ObservableList<ItemWrapper> items;

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

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

            List<ItemWrapper> itemWrappers = itemsList.stream().map(ItemWrapper::new).collect(Collectors.toList());
            items = FXCollections.observableList(itemWrappers,wrapper -> new Observable[]{wrapper.itemProperty()});

            cbxItemsSelector.setItems(items);
        }

        private void customizeSelectorCellView(){


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

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

            Item item = selectedItemWrapper.getItem();
            item.setState("started");

        }
    }

问题是组合框在某些项目的状态更新后看不到应用的更改。问题的原因是我只更改了对象(即项目)的字段,我没有通过setItem 方法设置新项目。 所以看起来字段的变化是不可观察的。 我也知道如果我在ItemWrapper 类中将SimpleObjectProperty 替换为两个StringProperty(用于名称和状态字段),它将起作用。但我不想重复字段。如果Item 类中有 100 个字段怎么办?

我有两个问题:
1) 如何让 ComboBox 在没有字段重复的情况下让 ComboBox 在Item 类的任何字段中看到其他外部方所做的更改(不创建额外的字段属性)?也许我应该使用一些特殊的绑定?
2) 有没有办法在保持域Item 类未修改的情况下摆脱 Wrapper 类?

【问题讨论】:

    标签: java javafx javafx-8


    【解决方案1】:

    据我所知,PropertyChangeListener 需要创建和注册。看看这个:https://docs.oracle.com/javase/tutorial/uiswing/events/propertychangelistener.html

    我希望我有所帮助。

    祝你有美好的一天。 :)

    【讨论】:

    • 这是关于 Java Beans 的。我正在使用 javaFX。后者应该通过观​​察者模式做得更优雅
    • 我找到了类似的解决方案 - stackoverflow.com/questions/23522130/… 但它需要修改域类
    猜你喜欢
    • 2014-04-24
    • 2017-06-16
    • 2019-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多