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

Java 如何将两个项目添加到此可观察列表中?,java,multithreading,javafx-8,Java,Multithreading,Javafx 8,我来自Swing背景,试图学习JavaFx 此observebleList正在用字符串填充,并添加到列表视图中 当我在同一线程中将一个项目添加到可观察列表中时,一切正常 然而,当我试图从不同的线程向可观察列表添加一个项目时,这些项目被添加了两次。就我的一生而言,我不知道为什么。调试语句显示线程实际上只执行一次 下面是一个完全有效的示例: import javafx.application.Application; import javafx.application.Platform; impor

我来自Swing背景,试图学习JavaFx

observebleList
正在用字符串填充,并添加到
列表视图中

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

然而,当我试图从不同的线程向可观察列表添加一个项目时,这些项目被添加了两次。就我的一生而言,我不知道为什么。调试语句显示线程实际上只执行一次

下面是一个完全有效的示例:

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);
                    }
                }

            }
        }
    }
}
导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.scene.scene;
导入javafx.scene.control.ListCell;
导入javafx.scene.control.ListView;
导入javafx.scene.paint.Color;
导入javafx.scene.web.WebEngine;
导入javafx.scene.web.WebView;
导入javafx.stage.stage;
导入javafx.util.Callback;
公共类FeedPanelViewer扩展了应用程序{
公共静态void main(字符串[]args){
发射(args);
}
字符串问候语=“你好,你会的””;
@凌驾
公众假期开始(阶段){
ObservableList name=FXCollections.observableArrayList(“Matthew”、“Hannah”、“Stephan”、“Denise”);
ListView ListView=新的ListView(名称);
stage.setScene(新场景(listView));
stage.show();
setCellFactory(新回调(){
@凌驾
公共ListCell调用(ListView列表){
返回新的HtmlFormatCell();
}
});
//此线程肯定只添加一次项目
新线程(()->{
试一试{
睡眠(1000);
}捕捉(中断异常e){
抛出新的运行时异常(e);
}
Platform.runLater(()->{
System.out.println(“到达这里”);
姓名。添加(问候语);
名称。添加(“安德鲁”);
});
}).start();
}
公共类HtmlFormatCell扩展了ListCell{
公共HtmlFormatCell(){
}
@凌驾
受保护的void updateItem(字符串项,布尔值为空){
super.updateItem(项,空);
如果(项!=null){
如果(项包含(“”){
Platform.runLater(()->{
WebView web=新建WebView();
WebEngine=web.getEngine();
发动机装载量(项目);
web.setPrefHeight(50);
web.setPrefWidth(300);
autosize();
setText(“”);
设置图形(网络);
});
}否则{
setText(项==null?”:“-”+项);
setTextFill(颜色:蓝色);
if(isSelected()){
setTextFill(颜色为绿色);
}
}
}
}
}
}
如果我注释掉两行
新线程(()->{
}),这是我看到的:

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

有人能帮忙指出发生了什么事吗


非常感谢。

updateItem
中,当项目为空时(即
item==null
,或者更好的是,当
empty
为真时),您应该有一个
else
分支,并清除单元格,即
setText(null);设置图形(空)

您的
updateItem
方法应该是这样的

if(!empty) {
    // populate the cell with graphic and/or text
} else {
    setText(null);
    setGraphic(null);
}
在您的示例中,最后两个单元格可能为空,但尚未清除

注意1:ListView分配和填充单元格的方式(似乎)不可预测,它可以进行大量冗余项更新

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

注意3:
updateItem
中,您不必在
平台中包装调用。稍后运行
,因为
updateItem
已经在JavaFX应用程序线程上执行。

Wow——这绝对是解决方案。非常感谢!因为代码中没有显式设置空单元格,所以我要花很长时间才能弄清楚这一点。