我正在尝试为ListView实现自定义cellFactory。 我的应用程序基于我从另一个主题中获取的这个示例。
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SO extends Application {
static class XCell extends ListCell<String> {
HBox hbox = new HBox();
Label label = new Label("(empty)");
Pane pane = new Pane();
Button button = new Button("(>)");
String lastItem;
public XCell() {
super();
hbox.getChildren().addAll(label, pane, button);
HBox.setHgrow(pane, Priority.ALWAYS);
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println(lastItem + " : " + event);
}
});
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(null); // No text in label of super class
if (empty) {
lastItem = null;
setGraphic(null);
} else {
lastItem = item;
label.setText(item!=null ? item : "<null>");
setGraphic(hbox);
}
}
}
@Override
public void start(Stage primaryStage) throws Exception {
StackPane pane = new StackPane();
Scene scene = new Scene(pane, 300, 150);
primaryStage.setScene(scene);
ObservableList<String> list = FXCollections.observableArrayList(
"Item 1", "Item 2", "Item 3", "Item 4");
ListView<String> lv = new ListView<>(list);
lv.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> param) {
return new XCell();
}
});
pane.getChildren().add(lv);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
因此,对于此列表的每一行,将创建一个新的XCell对象,hBox将从该对象中出现。 但是,尽管有文档,我还是不知道如何发生以下事情:
这是一个小问题,但我可以猜测hBox是因为ListCell.setgraphics(继承自Labeled类)而出现的。
在XCell.updateItem(String item,boolean empty)中,参数如何传递给该方法? 对ListView中包含的每个字符串进行调用,该字符串返回一个新的XCell(),但没有参数传递给该构造函数。
有人可以向我解释一下吗?
答案 0 :(得分:2)
这是一个障碍,但我可以猜测hBox出现是因为 ListCell.setgraphics(继承自Labeled类)。
是的,这是正确的。来自documentation for Cell
:
因为到目前为止,单元格最常见的用例是向a显示文本 用户,此用例专门针对Cell内部进行了优化。这是 由Labled扩展的Cell完成。这意味着子类 Cell只需要设置text属性,而不是单独创建 在Cell中标记并设置它。但是,对于那些情况 不仅仅需要纯文本,还有可能 将任何Node放在Cell图形属性中。尽管有这个词,a graphic可以是任何Node,并且将是完全交互式的。例如,a ListCell可能配置了Button作为其图形。按钮 然后可以将文本绑定到单元格项属性。通过这种方式, 每当Cell中的项目发生变化时,Button文本就会变化 自动更新。
你的下一个问题:
在XCell.updateItem(String item,boolean empty)中,是怎么回事 传递给该方法的参数?每个字符串都会进行一次调用 包含在ListView中,它返回一个新的XCell()但没有 参数传递给该构造函数。
您正在ListView
上设置工厂。工厂本质上是一个为ListCell
创建ListView
的函数。因此,call(...)
方法只需返回ListCell
:在这种情况下,您将返回XCell
子类的实例。
ListView
的工作方式(概念上至少)如下:
最初,它使用单元工厂为每个可见单元格创建ListCell
,并将这些单元格放出。然后它确定要在每个单元格中显示哪个值,并在每个单元格上调用updateItem(...)
方法,并传入该项目。如果列表中的项目数量超出了所需的空间,则会使用其他单元格填充它们,并调用updateItem(null, true)
以指示这些单元格为空。
当要显示的项目发生变化时:或者通过更改列表的内容,或者通过用户滚动(或通过其他机制,例如ListView
更改大小等),{{1将重新计算要显示的项目,并将调用ListView
传入新项目。这样,当用户滚动时可以重用单元,并且最小化创建新单元实例的需要。
因此,当您实现updateItem(...)
时,重要的是要尊重可能多次调用Cell
方法的事实,而创建新的updateItem(...)
实例则相对较少。所以Cell
方法应该快速执行;任何更重量级的东西都应该在构造函数中完成一次。请注意,在您显示的updateItem(...)
实施中,如何创建所有XCell
s Node
,HBox
,Label
和Pane
)和布局,是在施工时完成的。只需更新标签文本,Button
即可完成这项工作量最小的工作。