我正在尝试在Dan Nicks的评论中向this question提出自定义构建器。
想法是在构建组合之前设置组合的数据。
fxml文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<ComboBox fx:id="combo1" items="${itemLoader.items}" prefWidth="150.0"
xmlns:fx="http://javafx.com/fxml/1">
</ComboBox>
提供数据的类:
public class ComboLoader {
public ObservableList<Item> items;
public ComboLoader() {
items = FXCollections.observableArrayList(createItems());
}
private List<Item> createItems() {
return IntStream.rangeClosed(0, 5)
.mapToObj(i -> "Item "+i)
.map(Item::new)
.collect(Collectors.toList());
}
public ObservableList<Item> getItems(){
return items;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
this.name.set(name);
}
public final StringProperty nameProperty() {
return name;
}
}
}
测试:
public class ComboTest extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Populate combo from custom builder");
Group group = new Group();
GridPane grid = new GridPane();
grid.setPadding(new Insets(25, 25, 25, 25));
group.getChildren().add(grid);
FXMLLoader loader = new FXMLLoader();
ComboBox combo = loader.load(getClass().getResource("combo.fxml"));
loader.getNamespace().put("itemLoader", new ComboLoader());
grid.add(combo, 0, 0);
Scene scene = new Scene(group, 450, 175);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
未产生错误,但未填充组合。
缺少什么?
BTW:类似的解决方案:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<TableView items="${itemLoader.items}" xmlns:fx="http://javafx.com/fxml/1">
<columns>
<TableColumn text="Item">
<cellValueFactory><PropertyValueFactory property="name" /></cellValueFactory>
</TableColumn>
</columns>
</TableView>
答案 0 :(得分:1)
从挑剔开始,我进行了一些实验,研究如何实际实现我在对c0der's answer的评论中试图概述的内容。
基本思想是对listCell采取与对数据相同的方法,即通过名称空间(我今天的学习内容)配置内容和外观。成分:
单位/工厂:
public class ListCellFactory<T> {
private Function<T, String> textProvider;
public ListCellFactory(Function<T, String> provider) {
this.textProvider = provider;
}
public Callback<ListView<T>, ListCell<T>> getCellFactory() {
return cc -> new CListCell<>(textProvider);
}
public ListCell<T> getButtonCell() {
return getCellFactory().call(null);
}
public static class CListCell<T> extends ListCell<T> {
private Function<T, String> converter;
public CListCell(Function<T, String> converter) {
this.converter = Objects.requireNonNull(converter, "converter must not be null");
}
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText(converter.apply(item));
}
}
}
}
用于创建和配置组合的fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<ComboBox fx:id="combo1" items="${itemLoader.items}"
cellFactory="${cellFactoryProvider.cellFactory}"
buttonCell = "${cellFactoryProvider.buttonCell}"
prefWidth="150.0"
xmlns:fx="http://javafx.com/fxml/1">
</ComboBox>
使用示例:
public class LocaleLoaderApp extends Application {
private ComboBox<Locale> loadCombo(Object itemLoader, Function<Locale, String> extractor) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("comboloader.fxml"));
loader.getNamespace().put("itemLoader", itemLoader);
loader.getNamespace().put("cellFactoryProvider", new ListCellFactory<Locale>(extractor));
ComboBox<Locale> combo = loader.load();
return combo;
}
@Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Populate combo from custom builder");
Group group = new Group();
GridPane grid = new GridPane();
grid.setPadding(new Insets(25, 25, 25, 25));
group.getChildren().add(grid);
LocaleProvider provider = new LocaleProvider();
grid.add(loadCombo(provider, Locale::getDisplayName), 0, 0);
grid.add(loadCombo(provider, Locale::getLanguage), 1, 0);
Scene scene = new Scene(group, 450, 175);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class LocaleProvider {
ObservableList<Locale> locales = FXCollections.observableArrayList(Locale.getAvailableLocales());
public ObservableList<Locale> getItems() {
return locales;
}
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
修正错误的加载并添加toString
方法后,它可以正常工作:
public class ComboLoader {
private ObservableList<Item> obsItems;
public ComboLoader() {
obsItems = FXCollections.observableArrayList(createItems());
}
private List<Item> createItems() {
return IntStream.rangeClosed(0, 5)
.mapToObj(i -> "Item "+i)
.map(Item::new)
.collect(Collectors.toList());
}
//name of this methods corresponds to itemLoader.items in fxml.
//if xml name was itemLoader.a this method should have been
//getA(). A bit odd
public ObservableList<Item> getItems(){
return obsItems;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
this.name.set(name);
}
public final StringProperty nameProperty() {
return name;
}
@Override
public String toString() {
return name.getValue();
}
}
}
public class ComboTest extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Populate combo from custom builder");
Group group = new Group();
GridPane grid = new GridPane();
grid.setPadding(new Insets(25, 25, 25, 25));
group.getChildren().add(grid);
FXMLLoader loader = new FXMLLoader(getClass().getResource("combo.fxml"));
loader.getNamespace().put("itemLoader", new ComboLoader());
ComboBox<String>combo = loader.load();
grid.add(combo, 0, 0);
Scene scene = new Scene(group, 450, 175);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}