【问题标题】:Selected row gets cleared when data in table is updated更新表中的数据时清除所选行
【发布时间】:2019-11-23 20:13:11
【问题描述】:

我在 JavaFX 中创建了一个 TableView,并注意到当我选择一行时,一旦表中的项目更新,即存储在项目中的数据得到更新,选择就会被清除。我创建了一个可以从您的工作区运行的最小示例。在代码中,我有 Main 类,它创建 UI,将数据添加到表中,然后生成一个随机字符串,用于根据表行的 ID 替换旧值。我还有包含表格视图模型的 Person 类。

我已尝试存储选定的行,然后在选择清除后再次选择它,但这感觉不是一个可行的解决方案,而更像是一个 hack。

主类:

public class Main extends Application
{

  private TableView<Person> table = new TableView<Person>();

  @Override
  public void start(Stage primaryStage) throws Exception
  {
    Scene scene = new Scene(new Group());
    primaryStage.setTitle("Table View Sample");
    primaryStage.setWidth(450);
    primaryStage.setHeight(500);

    final Label label = new Label("Address Book");
    label.setFont(new Font("Arial", 20));

    table.setEditable(true);

    TableColumn<Person, Integer> IDCol = new TableColumn<>("ID");
    IDCol.setMinWidth(100);
    IDCol.setCellValueFactory(new PropertyValueFactory<Person, Integer>("ID"));

    TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
    lastNameCol.setMinWidth(100);
    lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));

    table.getColumns().add(IDCol);
    table.getColumns().add(lastNameCol);

    final VBox vbox = new VBox();
    vbox.setSpacing(5);
    vbox.setPadding(new Insets(10, 0, 0, 10));
    vbox.getChildren().addAll(label, table);

    ((Group) scene.getRoot()).getChildren().addAll(vbox);

    primaryStage.setScene(scene);
    primaryStage.show();

    addDataToTable();
  }

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

  private void addDataToTable()
  {    
    //If the table has data in it, then we must check if the ID already exist, so that we can replace it.
    //else, we just add the items to the table in the first run.
    Runnable runnable = new Runnable()
    {

      @Override
      public void run()
      {
        while(true)
        {
          ObservableList<Person> data = generateData();
          try
          {
            Thread.sleep(2000);
          } catch (InterruptedException e)
          {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }

          if(table.getItems().size() > 0)
          {
            //first we cycle through the data in the generated list
            //then we cycle through the data in the table itself.
            //if the data is found, we replace it, and break from the loop.
            for(int i = 0; i < data.size(); i++)
            {
              for(int j = 0; j < table.getItems().size(); j++)
              {
                Person newPerson = data.get(i);
                Person currentPerson = table.getItems().get(j);

                if(newPerson.getID() == currentPerson.getID())
                {
                  final int J = j;
                  //now we replace it if it is the same
                  Platform.runLater(new Runnable()
                  {

                    @Override
                    public void run()
                    {
                      table.getItems().set(J, newPerson);
                    }
                  });

                  break;
                }
              }
            }
          }
          else
          {
            //When modifying the data on the table,w e do it on the platform thread, 
            //to avoid any concurrent modification exceptions.
            Platform.runLater(new Runnable()
            {

              @Override
              public void run()
              {
                table.getItems().addAll(data);
              }
            });
          }
        }
      }
    };

    Thread thread = new Thread(runnable);
    thread.setDaemon(true);
    thread.start();
  }

  private ObservableList<Person> generateData()
  {
    ObservableList<Person> values = FXCollections.observableArrayList();

    for(int i = 0; i < 100; i++)
    {
      Person oPerson = new Person(i, randomStringGeneerator());
      values.add(oPerson);
    }

    return values;
  }

  private String randomStringGeneerator()
  {
      String[] sample = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
      String result = "";
      Random oRandom = new Random();

      int length = oRandom.nextInt(10) + 5;
      for(int i = 0 ; i < length; i++)
      {
        result += sample[oRandom.nextInt(sample.length)];
      }

      return result;
  }  
}

Person 类:

public class Person
{
  private final SimpleIntegerProperty ID;
  private final SimpleStringProperty lastName;

  public Person(int ID, String lName)
  {
    this.ID = new SimpleIntegerProperty(ID);
    this.lastName = new SimpleStringProperty(lName);
  }

  public int getID()
  {
    return this.ID.get();
  }

  public void setID(int ID)
  {
    this.ID.set(ID);
  }

  public String getLastName()
  {
    return this.lastName.get();
  }

  public void setLastName(String lName)
  {
    this.lastName.set(lName);
  }

  @Override
  public String toString()
  {
    return "ID: " + ID + " value: " + lastName;
  }
}

我期望该行应该保持选中状态,即使在更新表行中的值之后也是如此。

【问题讨论】:

  • 很高兴您提供了minimal reproducible example,尽管在 M 上有一些余地:) 不需要线程,只需 f.i.更改所选项目的按钮。此外,看起来这段代码似乎替换了所有(至少很多人),所以选择可能会混淆。也就是说:在修改数据时,所有具体的 selectionModel 都存在大量错误。不幸的是,在替换项目时不保留所选索引只是其中之一。没什么可做的:大多数错误都在 MultipleSelectionModel 中路由,这是一个包私有的超级...
  • clear-on-replace 被bugs.openjdk.java.net/browse/JDK-8186908 覆盖(针对多种模式进行报告,但也适用于单选)
  • 感谢您的快速回复@kleopatra :)。我想知道它是否可能是Java中的错误。不过很遗憾,我认为它不会很快得到修复。我找到了解决这个问题的方法,这比重新选择行更像是一个 hack,我将在下面发布。

标签: java javafx tableview


【解决方案1】:

所以看起来这个问题有一个解决方案,它比尝试重新选择行更像是一个 hack。 替换:

table.getItems().set(J, newPerson);

与:

table.getItems().get(J).setLastName(newPerson.getLastName());
table.refresh();

所以设置属性的值,更新模型中的数据,然后我们只是进行刷新,然后重新绘制 UI。这不是最好的解决方案,但仍然是一个解决方案。

【讨论】:

  • 如果数据设置正确,则无需刷新
猜你喜欢
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-19
  • 1970-01-01
相关资源
最近更新 更多