JavaFx:使用不同的枚举填充ComboBox,具体取决于另一个ComboBox

时间:2018-03-20 15:28:15

标签: java javafx enums combobox

我有两个ComboBox:

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

根据comboBoxMainCategory中选择的值,comboBoxSubCategory应填充相应的枚举。

    public enum MainCategory { // extra enum class
      EUROPE("Europe"),
      USA("USA");
    }

    public enum SubCategoryEurope { // extra enum class
      GERMANY("Germany"),
      FRANCE("France");
    }

    public enum SubCategoryUSA {
      COLORADO("Colorado"),
      CALIFORNIA("California");
    }

如果&#34;欧洲&#34;如果为comboBoxMainCategory选择,则应使用SubCategoryEurope填充comboBoxSubCategory。如果&#34; USA&#34;,使用SubCategoryUSA。

你是如何实现这一目标的?

这是我的代码:

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

问题是,因为comboBoxSubCategory是&#34; SubCategory&#34;,如果用“SubCategoryEurope&#39;”填充了类型错误。或者&#39; SubCategoryUSA&#39;。

解决此问题的最佳方法是什么?对不起,如果这是一个愚蠢的问题,我是JavaFx的新手。

非常感谢!

3 个答案:

答案 0 :(得分:1)

我根本不会使用枚举,因为这不允许在不重新编译的情况下进行数据操作。如果您坚持使用枚举,则需要使用<template> <div> {{propValue.value}} </div> </template> <script> export default { props: ['propFunction', 'propValue'], created(){ if (this.propValue.bool === true) { this.propFunction() } } } </script> 或使用两个子类别枚举类型实现的接口作为Object的参数类型:

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); } }); 来存储数据:

Map<String, List<String>>

答案 1 :(得分:0)

创建通用占位符界面

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

  public abstract String getDisplayValue();

  public abstract E getEnum();

}

为您的所有枚举创建一个实现。例如

public class EuropePlaceholder implements EnumPlaceHolder<Europe> {

  private final Europe value;

  public EuropePlaceholder(Europe pValue){
     value = pValue;
  }

  @Override
  public String getDisplayValue() {
    // here you create a user-friendly version of your enum for display
    return value.toString();
  }

  @Override
  public Europe getEnum() {
    return value;
  }

}

然后将ComboBox<Enum>的类型更改为ComboBox<EnumPlaceholder<?>>,您可以将任何已实施的EnumPlaceholders添加到其中。检索所选项目时,您可以通过实例检查检查包含哪一项

EnumPlaceholder<?> selectedItem = ...;
if(selectedItem instanceof EuropePlaceholder){
   Europe selectedEuropeEnum = (Europe) selectedItem.getEnum();
} else if(....){
  // check with else if for your other enums
}

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

修改 总的来说,我必须同意法比亚人的回答。你不应该使用枚举这样的结构。而是使用Map&lt;&gt;或列表&lt;&gt;具有适当的内容和结构。

答案 2 :(得分:0)

只是为了好玩(以及充实我的评论):比其他答案更通用的方法是将感兴趣的焦点从支持数据的具体性质转移到更普遍的使用解决方案 - 手头的情况。让UI实现特殊情况的缺点总是相同的 - 您必须为每个特殊UI和每种特殊数据类型反复执行。出路总是一样的:实现一个接管一般方面的模型,并在具体的UI /数据上下文中重用它。

这里的一般方面是:

  • 那里的项目列表,每个项目都有一个依赖对象列表(相同或不同类型)
  • 这个(让他们称之为root)项目列表显示在控件
  • 从根列表中,可以选择单个项目(又名:选中)
  • 另一个控件应显示根
  • 的依赖项

一般方法是建立一个

模型
  • 管理项目列表
  • 具有其中一个项目的选择(或当前或活动或..)
  • 的概念
  • 管理始终为所选项目的从属列表的依赖项列表
  • 其状态(根项目,当前项目,依赖项目)作为属性公开

这种模型的优点是

  • 可以进行正式和严格的测试,因此使用代码可以依赖其正常运行
  • 可以对任何数据上下文重复使用
  • 它可以重复用于许多控件
  • 通过绑定
  • 使用非常简单

在下面的示例中,Model名为RelationModel,它期望RelationProvider类型的根项(允许访问依赖项列表,它是一个选项,也可以使用函数来构建依赖项) )。它使用一个普通的字符串/列表映射,一次使用大陆/国家的枚举,每个都很容易实现。请注意,由此产生的UI幸福地不知道数据的性质,仅针对模型实现。

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

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

}