Javafx动态加载ComboBox

时间:2017-06-02 05:54:02

标签: java javafx dynamic fxml

我正在尝试在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>

2 个答案:

答案 0 :(得分:1)

从挑剔开始,我进行了一些实验,研究如何实际实现我在对c0der's answer的评论中试图概述的内容。

基本思想是对listCell采取与对数据相同的方法,即通过名称空间(我今天的学习内容)配置内容和外观。成分:

  • 可配置为将项目转换为文本的函数的通用自定义listCell
  • 用于提供创建该单元格的cellFactory的通用“ cellFactory工厂”类

单位/工厂:

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);
        }
    }