如何将两个项目添加到此可观察列表中?
How are two items being added to this observable list?
我有 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();
两行,这就是我看到的:
并且 Thread
包裹了两个列表元素的添加,我看到了这个,它渲染了两次单元格,即使线程只执行一次:
谁能帮忙指出这是怎么回事?
非常感谢。
在 updateItem
中,当项目为空时(即 item == null
或更好的是 empty
为真时,您应该有一个 else
分支) 并清除单元格,即 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 应用程序线程上。
我有 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();
两行,这就是我看到的:
并且 Thread
包裹了两个列表元素的添加,我看到了这个,它渲染了两次单元格,即使线程只执行一次:
谁能帮忙指出这是怎么回事?
非常感谢。
在 updateItem
中,当项目为空时(即 item == null
或更好的是 empty
为真时,您应该有一个 else
分支) 并清除单元格,即 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 应用程序线程上。