4

不久前我创建了一个 TableView 并为每个 TableColumns 注册了属性。内部数据的编辑可以很好地反映在 TableView 中。

然而,对于 ListView,情况就不同了。除非我关闭框架并再次打开它,否则不会立即显示更改。

我的 ListView 由 ActionSteps 组成。请注意,我使用了 Javafx bean 属性。

package application.objects;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.function.IntPredicate;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class ActionStep {

   private StringProperty actionStepID;
   private ObjectProperty<LocalDate> dateSet, dateFinished;
   private StringProperty stepName;
   private IntegerProperty completion;
   private ArrayList<StepComment> comments;



   public ActionStep(String name) {
      actionStepID = new SimpleStringProperty();
      stepName = new SimpleStringProperty();
      dateSet = new SimpleObjectProperty<LocalDate>();
      dateFinished = new SimpleObjectProperty<LocalDate>();
      completion = new SimpleIntegerProperty();
      stepName.setValue(name);
   }



   public void setName(String name) {
      stepName.setValue(name);
   }



   public String getName() {
      return stepName.getValue();
   }



   public StringProperty stepNameProperty() {
      return actionStepID;
   }



   public void setID(String id) {
      actionStepID.setValue(id);
   }



   public String getID() {
      return actionStepID.get();
   }



   public StringProperty actionStepIDProperty() {
      return actionStepID;
   }



   public void setCompletion(int percent) {
      if (percent < 0 || percent > 100)
         return;
      completion.set(percent);
   }



   public int getCompletion() {
      return completion.get();
   }



   public IntegerProperty completionProperty() {
      return completion;
   }



   public void setDateSet(LocalDate date) {
      dateSet.set(date);
   }



   public LocalDate getDateSet() {
      return dateSet.get();
   }



   public ObjectProperty<LocalDate> dateSetProperty() {
      return dateSet;
   }



   public void setDateFinished(LocalDate date) {
      dateFinished.set(date);
   }



   public LocalDate getDateFinished() {
      return dateFinished.get();
   }



   public ObjectProperty<LocalDate> dateFinishedProperty() {
      return dateFinished;
   }



   public String toString() {
      return stepNameProperty().get();
   }

}

我的 ListView 也使用 ObservableList。

  @FXML
  private ListView<ActionStep> actionStepsListView;
  private ObservableList<ActionStep> listOfSteps;

  listOfSteps = FXCollections.observableArrayList();
  actionStepsListView.setItems(listOfSteps);
  if (plan != null) {
     ArrayList<ActionStep> arrayOfSteps = plan.getStepsArrayList();
     for (int i = 0; i < arrayOfSteps.size(); i++)
        listOfSteps.add(arrayOfSteps.get(i));
  } else
     plan = new ActionPlan();

为什么对 ObservableList 所做的更改不会反映在 ListView 中?我注意到 ListView 调用每个对象的 toString() 以在 ListView 中显示它们的值,而不是将其绑定到它们的属性。

我究竟做错了什么?我应该覆盖一个细胞工厂还是什么?

4

2 回答 2

8

请注意,您正在尝试对您的单元格执行ListView比对TableView. 在 中TableView,单元格中显示的对象在变化,因此单元格很容易观察到这一点。在 中ListView,您希望单元格在属于单元格中显示的对象的属性发生更改时注意到;这是进一步删除的步骤,因此您必须进行一些额外的编码(尽管不多,正如您将看到的那样)。

您可以创建一个自定义单元工厂来绑定到stepNameProperty(),但这很棘手(您必须确保从updateItem()方法中的旧项目中取消绑定/删除侦听器)。

但是,没有很好记录的更简单的方法是使用ObservableList定义了提取器的 an 。

首先,修复您的方法名称:您发布的代码中有一些奇怪的不匹配。getX/setX/xProperty 方法名称应该都正确匹配。即代替

   public void setName(String name) {
      stepName.setValue(name);
   }



   public String getName() {
      return stepName.getValue();
   }



   public StringProperty stepNameProperty() {
      return actionStepID;
   }

你应该有

   public final void setName(String name) {
      stepName.setValue(name);
   }



   public final String getName() {
      return stepName.getValue();
   }


   public StringProperty nameProperty() {
      return stepName;
   }

对于其他属性访问器方法也是如此。(显然,字段的名称可以是您喜欢的任何名称,因为它们是私有的。)使 get/set 方法最终化是一种很好的做法。

然后,使用提取器创建列表。提取器是一个函数,它将列表中的每个元素映射到Observable列表将观察到的 s 数组。如果这些值发生变化,它将向列表的观察者触发列表更新。由于您ActionSteptoString()方法引用了nameProperty(),我假设您希望在更改ListView时更新nameProperty()。所以你想做

  listOfSteps = FXCollections.observableArrayList(
    actionStep -> new Observable[] { actionStep.nameProperty() } // the "extractor"
  );
  actionStepsListView.setItems(listOfSteps);

请注意,在 JavaFX 2.2 的早期版本中,ListView没有正确观察更新事件列表;这是在 Java 8 发布前不久修复的(如果我没记错的话)。(由于您标记了 JavaFX8 问题,我假设您使用的是 Java 8,所以您在这里应该没问题。)

如果您不使用 Java 8,则可以使用以下(等效但更详细)代码:

listOfSteps = FXCollections.observableArrayList(
    new Callback<ActionStep, Observable[]>() {
        @Override
        public Observable[] call(ActionStep actionStep) {
            return new Observable[] { actionStep.nameProperty() } ;
        }
    });
actionStepListView.setItems(listOfSteps);
于 2014-05-23T11:57:22.727 回答
1

以下是如何使用自定义对象制作列表视图的示例:

public class JavaFX_ListView extends Application {

    class MyObject {
        String day;
        int number;

        MyObject(String d, int n) {
            day = d;
            number = n;
        }

        String getDay() {
            return day;
        }

        int getNumber() {
            return number;
        }

        @Override
        public String toString() {

            return number + " " + day;
        }
    }

    ObservableList<MyObject> myList;

    // Create dummy list of MyObject
    private void prepareMyList() {
        myList = FXCollections.observableArrayList();
        myList.add(new MyObject("Sunday", 50));
        myList.add(new MyObject("Monday", 60));
        myList.add(new MyObject("Tuesday", 20));

    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("sample");
        prepareMyList();
        ListView<MyObject> listView = new ListView<>();
        listView.setItems(myList);

        Pane root = new Pane();
        root.getChildren().add(listView);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
        // testing
        Timer timer = new Timer();
        timer.schedule(new UpdateListTask(), 1000, 1000);
    }

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

    // testing
    public class UpdateListTask extends TimerTask {
        @Override
        public void run() {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    myList.add(new MyObject("sample", Calendar.getInstance()
                            .getTime().getSeconds()));
                }
            });
        }
    }

}
于 2014-05-23T10:00:55.573 回答