java - 如何将两个项目添加到此可观察列表中?

标签 java multithreading javafx-8

我有 Swing 背景,正在尝试学习 JavaFx。

ObservableList 正在填充字符串,并添加到 ListView 中。

当我将一个项目添加到同一线程中的可观察列表时,一切正常。

但是,当我尝试从不同线程将项目添加到可观察列表时,这些项目将被添加两次。对于我的一生,我无法弄清楚为什么。调试语句显示线程实际上只执行一次。

这是一个完整的示例:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.util.Callback;

public class FeedPanelViewer extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    String greeting = "<html><body><p><strong>hi ya'll</strong></p></body></html>";

    @Override
    public void start(Stage stage) {

        ObservableList<String> names = FXCollections.observableArrayList("Matthew", "Hannah", "Stephan", "Denise");

        ListView<String> listView = new ListView<String>(names);
        stage.setScene(new Scene(listView));
        stage.show();

        listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
            @Override
            public ListCell<String> call(ListView<String> list) {
                return new HtmlFormatCell();
            }
        });

        // This thread is definitely only adding items once
        new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Platform.runLater(() -> {
                System.out.println("Got here");
                names.add(greeting);
                names.add("andrew");
            });
        }).start();
    }

    public class HtmlFormatCell extends ListCell<String> {

        public HtmlFormatCell() {
        }

        @Override
        protected void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);

            if (item != null) {
                if (item.contains("<p>")) {
                    Platform.runLater(() -> {
                        WebView web = new WebView();
                        WebEngine engine = web.getEngine();
                        engine.loadContent(item);
                        web.setPrefHeight(50);
                        web.setPrefWidth(300);
                        web.autosize();
                        setText("");
                        setGraphic(web);
                    });
                } else {
                    setText(item == null ? "" : "-" + item);
                    setTextFill(Color.BLUE);
                    if (isSelected()) {
                        setTextFill(Color.GREEN);
                    }
                }

            }
        }
    }
}

如果我注释掉两行 new Thread(() -> {}).start();,这就是我看到的:

desired screen shot

随着 Thread 包裹着两个列表元素的添加,我看到了这一点,即使线程只执行一次,它也会渲染单元格两次:

undesired screen shot

谁能帮忙指出这是怎么回事吗?

非常感谢。

最佳答案

updateItem中,当项目为空时(即当item == null,或者更好的情况下),您应该有一个else分支然而,当 empty 为 true 时)并清除单元格,即 setText(null); setGraphic(null);.

你的updateItem方法应该是这样的

if(!empty) {
    // populate the cell with graphic and/or text
} else {
    setText(null);
    setGraphic(null);
}

在您的示例中,最后两个单元格可能为空,但尚未清除。

注释 1:ListView 分配和填充单元格的方式(看似)是不可预测的,并且它可以执行大量冗余项目更新。

注释 2: 这本身并不能解释两个版本的行为差异。我的猜测是,如果没有 Thread 包装器,调用会在 ListView 的第一个布局传递之前执行,而使用 Thread 包装器,它会布局初始项目,然后更新添加项目的布局。这与前面的注释一起可以解释结果的差异。

注释 3:updateItem 中,您不必将调用包装在 Platform.runLater 中,因为 updateItem 已在 JavaFX 应用程序线程上执行。

关于java - 如何将两个项目添加到此可观察列表中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28228128/

相关文章:

java - 这个 Java HashMap 声明有什么问题?轻松点

ruby - 并发可以提高性能吗?

css - 如何在 Java FX 中使用 CSS 模拟失去焦点样式?

java - 在 Alert 上调用 initOwner 时的 NPE (JavaFX)

java - 在调整框架大小之前 Swing 组件不会出现

java - 如何将公共(public)元素组合到一个数组中?

java - java中删除以.csv、.html、.doc结尾的文件

C# 多线程与 SQL Server 数据库调用

java - 多线程读取文件

java - TableView setRowFactory CSS 选择器