JavaFx:根据另一个组合框使用不同的枚举填充组合框

JavaFx:根据另一个组合框使用不同的枚举填充组合框,java,javafx,enums,combobox,Java,Javafx,Enums,Combobox,我有两个组合框: final ComboBox<MainCategory> comboBoxMainCategory = new ComboBox<>(); final ComboBox<SubCategory> comboBoxSubCategory = new ComboBox<>(); 如果comboBoxMainCategory选择了“Europe”,则comboBoxSubCategory应填充SubCategory E

我有两个组合框:

    final ComboBox<MainCategory> comboBoxMainCategory = new ComboBox<>();
    final ComboBox<SubCategory> comboBoxSubCategory = new ComboBox<>();
如果comboBoxMainCategory选择了“Europe”,则comboBoxSubCategory应填充SubCategory Europe。如果为“美国”,则带有子类别USA

你是如何做到这一点的

这是我的密码:

    final ComboBox<MainCategory> comboBoxMainCategory = new ComboBox<();
    final ComboBox<SubCategory> comboBoxSubCategory = new ComboBox<>(); 

    comboBoxMainCategory.valueProperty().addListener((obs, oldValue, 
    newValue) ->
          {
            if (newValue == null) { // newValue: Europe || USA
              comboBoxSubCategory.getItems().clear();
              comboBoxSubCategory.setDisable(true);
            } else if (newValue.equals(MainCategory.EUROPE)) {

 comboBoxSubCategory.setItems(FXCollections.observableArrayList(SubCategoryEurope.values())); 
            comboBoxSubCategory.setDisable(false);
        } else {
             comboBoxSubCategory.setItems(FXCollections.observableArrayList(SubCategoryUSA.values())); 
            comboBoxSubCategory.setDisable(false);}
        });
最终组合框comboBoxMainCategory=新组合框
{
如果(newValue==null){//newValue:Europe | | USA
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
}else if(newValue.equals(maincegory.EUROPE)){
comboBoxSubCategory.setItems(FXCollections.observableArrayList(subcategory Europe.values());
comboBoxSubCategory.setDisable(false);
}否则{
comboBoxSubCategory.setItems(FXCollections.observableArrayList(subcategory.usa.values());
comboBoxSubCategory.setDisable(false);}
});
问题是,因为comboBoxSubCategory是“SubCategory”,如果它填充了“SubCategory Europe”或“SubCategory USA”,则会出现类型错误

解决这个问题的最好办法是什么?抱歉,如果这是一个愚蠢的问题,我是JavaFx新手


非常感谢

创建通用占位符界面

public interface EnumPlaceHolder<E extends Enum<?>> {

  public abstract String getDisplayValue();

  public abstract E getEnum();

}
要在组合框中显示枚举,可以调用EnumPlaceholder的
getDisplayValue()
,并在单元格中显示返回的
字符串:)

编辑
但总的来说,我不得不同意费边的回答。您不应该对这样的构造使用枚举。而是使用具有适当内容和结构的地图或列表

创建通用占位符界面

public interface EnumPlaceHolder<E extends Enum<?>> {

  public abstract String getDisplayValue();

  public abstract E getEnum();

}
要在组合框中显示枚举,可以调用EnumPlaceholder的
getDisplayValue()
,并在单元格中显示返回的
字符串:)

编辑
但总的来说,我不得不同意费边的回答。您不应该对这样的构造使用枚举。而是使用具有适当内容和结构的地图或列表

我根本不会使用枚举,因为这不允许在不重新编译的情况下进行数据操作。但如果坚持使用枚举,则需要使用
对象
或使用两个子类别枚举类型实现的接口作为
comboBoxSubCategory
的参数类型:

comboBoxMainCategory.valueProperty().addListener((obs, oldValue, newValue) -> {
        if (newValue == null) { // newValue: Europe || USA
            comboBoxSubCategory.getItems().clear();
            comboBoxSubCategory.setDisable(true);
        } else {
            comboBoxSubCategory.setDisable(false);
            List<? extends Object> list;
            switch (newValue) {
                case EUROPE:
                    list = Arrays.asList(SubCategoryEurope.values());
                    break;
                default:
                    list = Arrays.asList(SubCategoryUSA.values());
                    break;
            }
            comboBoxSubCategory.getItems().setAll(list);
        }
    });
comboBoxMainCategory.valueProperty().addListener((obs、oldValue、newValue)->{
如果(newValue==null){//newValue:Europe | | USA
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
}否则{
comboBoxSubCategory.setDisable(false);

List我根本不会使用枚举,因为这不允许在不重新编译的情况下进行数据操作。如果您坚持使用枚举,则需要使用
对象
或使用两个子类别枚举类型实现的接口作为
comboBoxSubCategory
的参数类型:

comboBoxMainCategory.valueProperty().addListener((obs, oldValue, newValue) -> {
        if (newValue == null) { // newValue: Europe || USA
            comboBoxSubCategory.getItems().clear();
            comboBoxSubCategory.setDisable(true);
        } else {
            comboBoxSubCategory.setDisable(false);
            List<? extends Object> list;
            switch (newValue) {
                case EUROPE:
                    list = Arrays.asList(SubCategoryEurope.values());
                    break;
                default:
                    list = Arrays.asList(SubCategoryUSA.values());
                    break;
            }
            comboBoxSubCategory.getItems().setAll(list);
        }
    });
comboBoxMainCategory.valueProperty().addListener((obs、oldValue、newValue)->{
如果(newValue==null){//newValue:Europe | | USA
comboBoxSubCategory.getItems().clear();
comboBoxSubCategory.setDisable(true);
}否则{
comboBoxSubCategory.setDisable(false);
列出只是为了好玩(并充实我的评论):与其他答案中的方法相比,一种更通用的方法是将关注的焦点从支持数据的具体性质转移到手边用例的更通用的解决方案。让UI实现特殊情况的缺点总是一样的-您必须为每个特殊UI和每个特殊数据反复执行一种类型。出路也总是一样的:实现一个接管一般方面的模型,并在具体的UI/数据上下文中重用它

这里的一般方面是:

  • 有一个项目列表,每个项目都有一个依赖对象列表(相同或不同类型)
  • 这个(我们称之为root)项列表显示在控件中
  • 从根列表中,可以选择单个项目(aka:selected)
  • 另一个控件应显示根的从属项
一般的方法是建立一个

  • 管理项目列表
  • 将这些项目中的一个视为已选择(或当前或活动或…)
  • 管理从属项列表,该列表始终是选定项的从属列表
  • 其状态(根项、当前项、依赖项)作为属性公开
这种模式的优点是

  • 可以进行正式和严格的测试,因此使用代码可以依赖于其正确的功能
  • 它可用于任何数据上下文
  • 它可用于许多控件
  • 通过绑定,使用非常简单
在下面的示例中,该模型名为RelationModel,它需要RelationProvider类型的根项(允许访问依赖项列表,这是一个选项,也可以使用f.i.函数来构建依赖项)。它一次用于字符串/列表的普通映射,一次用于大陆/国家的枚举,每个都很容易实现。请注意,生成的UI完全不知道数据的性质,仅针对模型实现

当然,不是生产级的,尤其是没有经过正式测试的,而且模型只具有最基本的功能:)

公共类CombosWithCategories扩展应用程序{
公共接口关系提供者{
默认ObservableList getRelations(){
返回emptyObservableList();
};
}
/**
*管理RelationProviders列表并具有以下概念的模型
*具有关系的当前relationProvider(它是一种selectionModel)。
* 
*关系列表中元素的类型
*/
公共静态类关系模型{
/**
*所有关系提供者
comboBoxMainCategory.valueProperty().addListener((obs, oldValue, newValue) -> {
        if (newValue == null) { // newValue: Europe || USA
            comboBoxSubCategory.getItems().clear();
            comboBoxSubCategory.setDisable(true);
        } else {
            comboBoxSubCategory.setDisable(false);
            List<? extends Object> list;
            switch (newValue) {
                case EUROPE:
                    list = Arrays.asList(SubCategoryEurope.values());
                    break;
                default:
                    list = Arrays.asList(SubCategoryUSA.values());
                    break;
            }
            comboBoxSubCategory.getItems().setAll(list);
        }
    });
Map<String, List<String>> data = new HashMap<>();
data.put("EUROPE", Arrays.asList("GERMANY", "FRANCE"));
data.put("USA", Arrays.asList("COLORADO", "CALIFORNIA"));

comboBoxMainCategory.valueProperty().addListener((obs, oldValue, newValue) -> {
        List<String> list = data.get(newValue);
        if (list != null) {
            comboBoxSubCategory.setDisable(false);
            comboBoxSubCategory.getItems().setAll(list);
        } else {
            comboBoxSubCategory.getItems().clear();
            comboBoxSubCategory.setDisable(true);
        }
    });
public class CombosWithCategories extends Application {

    public interface RelationProvider<T> {
        default ObservableList<T> getRelations() {
            return emptyObservableList();
        };
    }

    /**
     * A model that manages a list of RelationProviders and has the notion
     * of a current relationProvider with relations (it's a kind-of selectionModel).
     * 
     * <T> the type of elements in the list of relations 
     */
    public static class RelationModel<T> {

        /**
         * all relationProviders managed by this model
         */
        private ListProperty<RelationProvider<T>> relationProviders;
        /**
         * The owner of the relations. Must be contained in the providers managed
         * by this model.
         */
        private ObjectProperty<RelationProvider<T>> relationProvider;
        private ListProperty<T> relations;

        public RelationModel() {
            initProperties();
        }

        /**
         * The RelationProviders managed by the model.
         */
        public ListProperty<RelationProvider<T>> relationProvidersProperty() {
            return relationProviders;
        }

        /**
         * The RelationProvider that manages the current relations.
         */
        public ObjectProperty<RelationProvider<T>> relationProviderProperty() {
            return relationProvider;
        }

        public RelationProvider<T> getRelationProvider() {
            return relationProviderProperty().get();
        }

        public ListProperty<T> relations() {
            return relations;
        }

        /**
         * Callback from invalidation of current relationProvider.
         * Implemented to update relations.
         */
        protected void relationProviderInvalidated() {
            RelationProvider<T> value = getRelationProvider();
            relations().set(value != null ? value.getRelations() : emptyObservableList());
        }

        /**
         * Creates and wires all properties.
         */
        private void initProperties() {
            relationProviders = new SimpleListProperty<>(this, "relationProviders", observableArrayList());
            relationProvider = new SimpleObjectProperty<>(this, "relationProvider") {

                @Override
                protected void invalidated() {
                    // todo: don't accept providers that are not in the list
                    relationProviderInvalidated();
                }

            };
            relations = new SimpleListProperty<>(this, "relations");
            relationProviderInvalidated();

        }

    }

    /**
     * Implement the ui against a RelationModel. Here we create
     * the same UI with a model backed by enums or a Map, respectively
     */
    private Parent createContent() {
        TabPane tabPane = new TabPane(
                new Tab("Enums", createRelationUI(createEnumRelationModel())),
                new Tab("Manual map", createRelationUI(createMapRelationModel()))
                );

        return new BorderPane(tabPane);
    }

    /**
     * Common factory for UI: creates and returns a Parent that
     * contains two combo's configured to use the model.
     */
    protected <T> Parent createRelationUI(RelationModel<T> model) {
        ComboBox<RelationProvider<T>> providers = new ComboBox<>();
        providers.itemsProperty().bind(model.relationProvidersProperty());
        providers.valueProperty().bindBidirectional(model.relationProviderProperty());

        ComboBox<T> relations = new ComboBox<>();
        relations.itemsProperty().bind(model.relations());
        relations.valueProperty().addListener((src, ov, nv) -> {
            LOG.info("relation changed: " + nv); 
        });

        return new VBox(10, providers, relations);
    }


    // ------------- manual with maps

    /**
     * On-the-fly creation of a RelationModel using a backing map.
     */
    protected RelationModel<String> createMapRelationModel() {
        RelationModel<String> model = new RelationModel<>();
        Map<String, ObservableList<String>> data = new HashMap<>();
        data.put("EUROPE", observableArrayList("GERMANY", "FRANCE"));
        data.put("AMERICA", observableArrayList("MEXICO", "USA"));
        for (String key: data.keySet()) {
            model.relationProvidersProperty().add(new RelationProvider<String>() {

                @Override
                public ObservableList<String> getRelations() {
                    return data.get(key);
                }

                @Override
                public String toString() {
                    return key;
                }


            });
        }
        return model;
    }
    //-------------------- enum
    /**
     * RelationModel using Enums.
     */
    protected RelationModel<Object> createEnumRelationModel() {
        RelationModel<Object> model = new RelationModel<Object>();
        model.relationProvidersProperty().setAll(Continent.values());
        return model;
    }

    public enum EuropeanCountry {
        FRANCE, GERMANY;
    }

    public enum AmericanCountry {
        MEXICO, CANADA, USA;
    }

    public enum Continent implements RelationProvider<Object> {
        AMERICA(AmericanCountry.values()),
        EUROPE(EuropeanCountry.values())
        ;

        ObservableList<Object> subs;
        private Continent(Object[] subs) {
            this.subs = FXCollections.observableArrayList(subs);
        }
        @Override
        public ObservableList<Object> getRelations() {
            return FXCollections.unmodifiableObservableList(subs);
        }
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.setTitle(FXUtils.version());
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(CombosWithCategories.class.getName());

}