快速而肮脏的答案
要直接回答您的愿望“我可以将其他项目设为 Text,但 ChangeListener 需要一个字符串。我只是希望异常消失。”,您可以将 ChangeListener 的类型更改为接受 Object 而不是 String:
new ChangeListener<Object>() {
@Override
public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
if(!(newvalue instanceof Text)) {
String string = ((String) newvalue).split(" - ")[0];
System.out.println(string);
}
}
}
但是,我不推荐上述方法,因为您会失去 Java 环境提供的一些固有类型安全优势。
编码建议
一些基于你的代码 sn-p 的建议。
- 使用 cell factories 从 ListView 的底层项模型数据生成 ListView 单元节点。
- 不要将Text等节点直接放入ListView项列表中。 ListView 项目列表用于表示不是节点的模型对象。我知道ListView javadoc 没有明确说明这一点,但是,这仍然是我最好的建议。每当您认为某个节点非常适合 ListView 时,通常最好的选择是使用单元工厂。
- 当您将items 放入 ListView 时,它们应该与 ListView 的项目类型相匹配(ListView 的 javadoc 确实说明了这一点)。
- 对于像这里这样的非平凡用例,使用更复杂的数据结构来表示列表中的项目,以便您可以将语义数据(例如项目是否为标题)直接编码到项目而不是硬编码字符串,例如“完成的任务”。
- 不要使用 Builder 类,例如 TextBuilder,它们已被弃用,并将从未来的 JavaFX 运行时发行版中删除。
- 使用外部样式表并通过设置和取消设置样式类来操作样式,而不是在代码中内联样式定义。
样本合并建议
根据建议,您可以将您的问题编码为类似于下面的应用程序(请注意,它仍然没有将样式信息提取到外部样式表,我将把这部分作为练习):
import javafx.application.Application;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class ListDisplay extends Application {
@Override
public void start(Stage stage) {
ListView<TaskItem> listView = new ListView<>(
FXCollections.observableArrayList(
new TaskItem("Todo Tasks", null, true, true),
new TaskItem("Senility", "The Autumn Years", false, false),
new TaskItem("Death", "But I didn't eat the salmon mousse", false, false),
new TaskItem("Done Tasks", null, true, true),
new TaskItem("Birth", "The Miracle of Life", true, false),
new TaskItem("School", "Growth and Learning", true, false),
new TaskItem("Middle Age", "Stagnation", true, false),
new TaskItem("Live Organ Transplants", "The Machine that goes 'Ping'", true, false)
)
);
listView.setCellFactory(param -> new ListCell<TaskItem>() {
@Override
protected void updateItem(TaskItem item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
if (item.isHeader()) {
setText(item.getName());
setStyle("-fx-font-weight: bold;");
} else {
setText(item.getName() + " - " + item.getDescription());
setStyle(null);
}
} else {
setText(null);
}
}
});
listView.setPrefHeight(200);
listView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.isHeader()) {
System.out.println("Selected: " + newValue.getName());
}
});
stage.setTitle("Monty Python's Meaning of Life");
stage.setScene(new Scene(listView));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private static class TaskItem {
private final String name;
private final String description;
private final SimpleBooleanProperty completed;
private final boolean header;
public TaskItem(String name, String description, boolean completed, boolean header) {
this.name = name;
this.description = description;
this.completed = new SimpleBooleanProperty(completed);
this.header = header;
}
public boolean isHeader() {
return header;
}
public boolean isCompleted() {
return completed.get();
}
public SimpleBooleanProperty completedProperty() {
return completed;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}
}