Отредактированы следующие комментарии kleopatra:
Загрузка combo.fxml
, указанных в вопросе со строками, может быть выполнена с использованием следующего загрузчика:
//load observable list with strings
public class ComboStringLoader {
private final ObservableList<String> items;
public ComboStringLoader() {
items = FXCollections.observableArrayList(createStrings());
}
private List<String> createStrings() {
return IntStream.rangeClosed(0, 5)
.mapToObj(i -> "String "+i)
.map(String::new)
.collect(Collectors.toList());
}
//name of this method corresponds to itemLoader.items in xml.
//if xml name was itemLoader.a this method should have been getA().
public ObservableList<String> getItems(){
return items;
}
}
Загрузка комбо с экземплярами Item
аналогичным образом просто означает, что Item#toString
для текста в комбо:
//load observable list with Item#toString
public class ComboObjectLoader1 {
public ObservableList<Item> items;
public ComboObjectLoader1() {
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;
}
}
Где Item определяется как:
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();
}
}
Лучший подход — загрузить комбо с пользовательским ListCell<item>
:
//load observable list with custom ListCell
public class ComboObjectLoader2 {
private final ObservableList<ItemListCell> items;
public ComboObjectLoader2() {
items =FXCollections.observableArrayList (createCells());
}
private List<ItemListCell> createCells() {
return IntStream.rangeClosed(0, 5)
.mapToObj(i -> "Item "+i)
.map(Item::new)
.map(ItemListCell::new)
.collect(Collectors.toList());
}
public ObservableList<ItemListCell> getItems(){
return items;
}
}
class ItemListCell extends ListCell<Item> {
private final Label text;
public ItemListCell(Item item) {
text = new Label(item.nameProperty().get());
setGraphic(new Pane(text));
}
@Override
public void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
text.setText(item.nameProperty().get());
}
}
}
Последней, но не менее важной альтернативой является установка пользовательского ListCell<Item>
в качестве фабрики ячеек для комбо.
Это можно сделать, добавив контроллер в файл fxml:
combo2.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"
xmlns="http://javafx.com/javafx/10.0.1" fx:controller="test.ComboObjectLoaderAndController">
</ComboBox>
Где ComboObjectLoaderAndController
— это и загрузчик, и контроллер:
//loads observable list with Items and serves as controller to set cell factory
public class ComboObjectLoaderAndController {
public ObservableList<Item> items;
@FXML ComboBox<Item> combo1;
public ComboObjectLoaderAndController() {
items = FXCollections.observableArrayList(createItems());
}
@FXML
public void initialize() {
combo1.setCellFactory(l->new ItemListCell());
}
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;
}
class ItemListCell extends ListCell<Item>{
@Override
public void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(item.nameProperty().get());
}
}
}
}
Редактировать:
после ответа Клеопатры я добавил общий пользовательский ListCell
public class ObjectListCell<T> extends ListCell<T> {
Function<T,String> textSupplier;
public ObjectListCell(Function<T,String> textSupplier) {
this.textSupplier = textSupplier;
}
public Callback<ListView<T>, ListCell<T>> getFactory() {
return cc -> new ObjectListCell<>(textSupplier);
}
public ListCell<T> getButtonCell() {
return getFactory().call(null);
}
@Override
public void updateItem(T t, boolean empty) {
super.updateItem(t, empty);
if (t== null || empty) {
setText(null);
setGraphic(null);
} else {
setText(textSupplier.apply(t));
}
}
}
Фабрика задается в файле fxml:
combo3.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<ComboBox fx:id="combo1" items="${itemLoader.items}" cellFactory="${cellFactoryProvider.factory}"
buttonCell = "${cellFactoryProvider.buttonCell}"
prefWidth="150.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/10.0.1">
</ComboBox>
Тестовый класс:
public class ComboTest extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Populate combo from custom builder");
//Combo of Strings
FXMLLoader loader = new FXMLLoader(getClass().getResource("combo.fxml"));
loader.getNamespace().put("itemLoader", new ComboStringLoader());
ComboBox<String>stringCombo = loader.load();
//Combo of Item
loader = new FXMLLoader(getClass().getResource("combo.fxml"));
loader.getNamespace().put("itemLoader", new ComboObjectLoader1());
ComboBox<Item>objectsCombo1 = loader.load();
//Combo of custom ListCell
loader = new FXMLLoader(getClass().getResource("combo.fxml"));
loader.getNamespace().put("itemLoader", new ComboObjectLoader2());
ComboBox<ItemListCell>objectsCombo2 = loader.load();
//Combo of Item with custom ListCell factory
loader = new FXMLLoader(getClass().getResource("combo2.fxml"));
loader.getNamespace().put("itemLoader", new ComboObjectLoaderAndController());
ComboBox<Item>objectsCombo3 = loader.load();
//Combo of Item with custom ListCell factory. Factory is set in FXML
loader = new FXMLLoader(getClass().getResource("combo3.fxml"));
loader.getNamespace().put("itemLoader", new ComboObjectLoader1());
loader.getNamespace().put("cellFactoryProvider", new ObjectListCell<Item>(t -> t.nameProperty().get()));
ComboBox<Item>objectsCombo4 = loader.load();
HBox pane = new HBox(25, stringCombo, objectsCombo1,objectsCombo2, objectsCombo3, objectsCombo4);
pane.setPadding(new Insets(25, 25, 25, 25));
Scene scene = new Scene(pane, 550, 175);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
person
c0der
schedule
02.06.2017