【问题标题】:Property sheet example with use of a PropertyEditor (ControlsFX)使用 PropertyEditor (ControlsFX) 的属性表示例
【发布时间】:2014-06-16 07:49:14
【问题描述】:

我一直在寻找任何使用 ControlsFX PropertySheet 的好例子,但除了这个之外找不到任何东西。

https://www.google.nl/search?q=Main.java+amazonaws.com&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:nl:official&client=firefox-a&channel=sb&gfe_rd=cr&ei=d5aeU5bvBI3k-gan94HQBA#channel=sb&q=https%3A%2F%2Fbitbucket-assetroot.s3.amazonaws.com%2Fcontrolsfx%2Fcontrolsfx+Main.java&rls=org.mozilla:nl:official

在此示例中,包含 NameItem 对象的 ObservableList 项被添加到其构造函数中的 PropertySheet 对象,就像文档中所说的那样。

http://controlsfx.bitbucket.org/org/controlsfx/control/PropertySheet.html

但是,正如本文档所述,PropertySheet 的一列“提供了一个 PropertyEditor,允许最终用户操作该属性”。它甚至说有“CheckEditor、ChoiceEditor、TextEditor 和 FontEditor,在 Editors 包中提供的众多编辑器中。”。

我不想局限于我的 NameItem 示例。我还想添加复选框、选择框和其他动态编辑器元素。谁能举例说明如何使用一个或多个编辑器来构建一个简单的 PropertySheet?

【问题讨论】:

  • 你看过样品吗?特别是 HelloPropertySheet 示例。

标签: javafx


【解决方案1】:

PropertySheet 确实支持一些开箱即用的属性编辑器,具体取决于属性类型。

以下示例是 ControlsFX 示例应用程序的扩展。它显示了 String、LocalDate、Enum、Boolean 和 Integer 类型如何分别映射到 TextField、DatePicker、ChoiceBox、CheckBox 和 NumericField。

public class PropertySheetExample extends VBox {
    private static Map<String, Object> customDataMap = new LinkedHashMap<>();
    static {
        customDataMap.put("Group 1#My Text", "Same text"); // Creates a TextField in property sheet
        customDataMap.put("Group 1#My Date", LocalDate.of(2000, Month.JANUARY, 1)); // Creates a DatePicker
        customDataMap.put("Group 2#My Enum Choice", SomeEnumType.EnumValue); // Creates a ChoiceBox
        customDataMap.put("Group 2#My Boolean", false); // Creates a CheckBox
        customDataMap.put("Group 2#My Number", 500); // Creates a NumericField
    }

    class CustomPropertyItem implements PropertySheet.Item {
        private String key;
        private String category, name;

        public CustomPropertyItem(String key) {
            this.key = key;
            String[] skey = key.split("#");
            category = skey[0];
            name = skey[1];
        }

        @Override
        public Class<?> getType() {
            return customDataMap.get(key).getClass();
        }

        @Override
        public String getCategory() {
            return category;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getDescription() {
            return null;
        }

        @Override
        public Object getValue() {
            return customDataMap.get(key);
        }

        @Override
        public void setValue(Object value) {
            customDataMap.put(key, value);
        }
    }

    public PropertySheetExample {
        ObservableList<PropertySheet.Item> list = FXCollections.observableArrayList();
        for (String key : customDataMap.keySet())
            list.add(new CustomPropertyItem(key));

        PropertySheet propertySheet = new PropertySheet(list);
        VBox.setVgrow(propertySheet, Priority.ALWAYS);
        getChildren().add(propertySheet);
    }
}

可以通过两种方式进一步扩展此行为。首先,现有编辑器可用于默认属性编辑器工厂不支持的类型。以下示例设置了新的属性编辑器工厂,它将为 List 类型创建 ChoiceBox。对于其他类型,它将编辑器的创建委托给默认工厂。

SimpleObjectProperty<Callback<PropertySheet.Item, PropertyEditor<?>>> propertyEditorFactory = new SimpleObjectProperty<>(this, "propertyEditor", new DefaultPropertyEditorFactory());

propertySheet.setPropertyEditorFactory(new Callback<PropertySheet.Item, PropertyEditor<?>>() {
    @Override
    public PropertyEditor<?> call(PropertySheet.Item param) {
        if(param.getValue() instanceof List) {
            return Editors.createChoiceEditor(param, (List) param.getValue());
        }

        return propertyEditorFactory.get().call(param);
    }
});

最后,我们可以创建自定义编辑器并覆盖PropertySheet.Item 中的getPropertyEditorClass() 方法以返回自定义编辑器类型。在这种情况下,默认的属性编辑器工厂会创建编辑器,不需要重写工厂方法。

【讨论】:

  • 你的回答很好,但我找不到把这个列表放在财产上的地方。当我的数据被选中时,由于某种原因它又变成了文本。
  • 不确定这是否会因为属性表而发生在您身上 - 如果您在创建窗口时看到 ComboBox,它应该始终保持 ComboBox。我一般扩展 PropertySheet 类并在构造函数中调用 setPropertyEditorFactory() 方法。然后我把我的类放到窗口中,仅此而已。
【解决方案2】:

最近我一直在用PropertySheet 研究Javafx,下面是简单快速的练习: 首先我们需要模型类来反映 gui:

public class Person {
    int age;
    boolean isLive = true;
    String name = "Yimkong";
    // ... getter and setter method
}

然后是主类:

public class PropertiesSheetTest extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Person bean = new Person();
        ObservableList<PropertySheet.Item> properties = BeanPropertyUtils.getProperties(bean);
        PropertySheet propertySheet = new PropertySheet(properties);
        propertySheet.setSearchBoxVisible(false);
        propertySheet.setModeSwitcherVisible(false);
        DefaultPropertyEditorFactory defaultPropertyEditorFactory = new DefaultPropertyEditorFactory();
        propertySheet.setPropertyEditorFactory(new Callback<PropertySheet.Item, PropertyEditor<?>>() {
            @Override
            public PropertyEditor<?> call(PropertySheet.Item param) {
                if(param.getName().equals("age")){
                    List<Integer> ageList = new ArrayList<>();
                    ageList.add(3);
                    ageList.add(5);
                    ageList.add(10);
                    return Editors.createChoiceEditor(param,ageList);
                }
                return defaultPropertyEditorFactory.call(param);
            }
        });
        VBox vBox = new VBox(propertySheet);
        Scene scene = new Scene(vBox);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

结果如下:

优点:

  • 通过 BeanPropertyUtils 轻松开发,反射到 java bean
  • 如果我们不希望用户更改 GUI 上的任何内容,我们可以删除模型类中的一些 setter 方法
  • 如果我们对默认的 PropertyEditor 不满意,我们可以用自己的进行覆盖。

这基本满足了大部分场景。顺便说一下,它还可以支持模型类中的Enum字段,默认选择ChoiceEditor。?

【讨论】:

  • 不错的答案。此外,如果您希望模型中的属性更改是可观察的,则使用 JavaFX 属性来表示它们而不是原始类型。
【解决方案3】:

即使在查看示例并尝试了解更多信息之后,我也对如何更改属性表中的用户输入类型感到非常困惑。我花了一段时间才弄清楚,但事实证明答案相对简单。

关键是 PropertyEditorFactory 可以使用setPropertyEditorFactory(Callback&lt;PropertySheet.Item,PropertyEditor&lt;?&gt;&gt; factory) 直接设置到 PropertySheet 在此回调中,该方法返回包含在 Editors 包中的 PropertyEditor 类型。由于调用提供了 PropertySheet.Item 参数,您可以检索设置的值,在下面的示例中,我检查了返回对象的类型以提供相关的编辑器。

propertySheet.setPropertyEditorFactory(new Callback<PropertySheet.Item, PropertyEditor<?>>() {
    @Override
    public PropertyEditor<?> call(Item param) {
        if(param.getValue() instanceof String[]) {   
            return Editors.createChoiceEditor(param, choices);
         } else if (param.getValue() instanceof Boolean) {
            return Editors.createCheckEditor(param);
         } else if (param.getValue() instanceof Integer) {
            return Editors.createNumericEditor(param);
         } else {
            return Editors.createTextEditor(param);
         }
     }
});

然而,一旦你重写了这个编辑器工厂,你必须为你需要的每种类型的设置提供一个 PropertyEditor,例如,如果你只返回 Editors.createNumericEditor(param); 但有字符串选项,你会得到一个例外。也不要从 PropertySheet.Item 覆盖 getPropertyEditorClass(),这是我浪费大部分时间的地方。希望这可以帮助其他尝试这样做的人!

【讨论】:

  • 我应该在哪里输入这个,因为每当我从选择编辑器中选择一些东西时,它就会再次变成一个文本字段和字符串
【解决方案4】:

我找不到任何关于 PropertySheet 的自定义 Editor 的示例,但我想我现在想通了。就我而言,我只是想使用Slider 作为Number 的编辑器。这可能不切实际,因为您无法查看 Slider 的值,但我想 Slider 可以简单地替换为带有滑块和标签的窗格。

首先,我们需要实现Item,这里是controlsfx-samples 的略微修改版本。 Map 已移至此类,但可以在任何地方。不过,您可能根本不想使用地图,因为将类别和名称结合起来已经有些不切实际了;此外,没有空间为项目创建描述。

public class CustomPropertyItem implements PropertySheet.Item {
    public static Map<String, Object> customDataMap = new LinkedHashMap<>();
    static {
        customDataMap.put("basic.My Text", "Same text"); // Creates a TextField in property sheet
        customDataMap.put("basic.My Date", LocalDate.of(2016, Month.JANUARY, 1)); // Creates a DatePicker
        customDataMap.put("misc.My Enum", SomeEnum.ALPHA); // Creates a ChoiceBox
        customDataMap.put("misc.My Boolean", false); // Creates a CheckBox
        customDataMap.put("misc.My Number", 500); // Creates a NumericField
        customDataMap.put("misc.My Color", Color.ALICEBLUE); // Creates a ColorPicker
    }

    private String key;
    private String category, name;

    public CustomPropertyItem(String key) 
    {
        this.key = key;
        String[] skey = key.split("\\.", 2);
        category = skey[0];
        name = skey[1];
    }

    @Override
    public Class<?> getType() {
        return customDataMap.get(key).getClass();
    }

    @Override
    public String getCategory() {
        return category;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        // doesn't really fit into the map
        return null;
    }

    @Override
    public Object getValue() {
        return customDataMap.get(key);
    }

    @Override
    public void setValue(Object value) {
        customDataMap.put(key, value);
    }

    @Override
    public Optional<ObservableValue<? extends Object>> getObservableValue() {
        return Optional.empty();
    }

    @Override
    public Optional<Class<? extends PropertyEditor<?>>> getPropertyEditorClass() {
        // for an item of type number, specify the type of editor to use
        if (Number.class.isAssignableFrom(getType())) return Optional.of(NumberSliderEditor.class);

        // ... return other editors for other types

        return Optional.empty();
    }
}

接下来是PropertyEditor 实现,它是Node 和一组将属性项值与控件连接起来的方法。第一个构造函数对于实现是必需的,因为方法Editors.createCustomEditor(Item) 使用反射来查找这个构造函数。您不必使用此方法,但默认的 PropertyEditorFactory 很可能依赖于此。 如果您出于某种原因想避免反射,则不需要在 your Item 中覆盖 getPropertyEditorClass() 并且可以使用 setPropertyEditorFactory(Callback) 并在其中创建 PropertyEditor 的新实例那里。 请注意,您根本不需要使用 setPropertyEditorFactory(Callback)(在此示例中)。

public class NumberSliderEditor extends AbstractPropertyEditor<Number, Slider> {

    public NumberSliderEditor(Item property, Slider control) 
    {
        super(property, control);
    }

    public NumberSliderEditor(Item item)
    {
        this(item, new Slider());
    }

    @Override
    public void setValue(Number n) {
        this.getEditor().setValue(n.doubleValue());
    }

    @Override
    protected ObservableValue<Number> getObservableValue() {
        return this.getEditor().valueProperty();
    }

}

从这里开始,您只需创建您的 PropertySheet 并添加所有地图条目。

PropertySheet propertySheet = new PropertySheet();
for (String key : CustomPropertyItem.customDataMap.keySet()) propertySheet.getItems().add(new CustomPropertyItem(key));

我希望这对任何人都有帮助,因为似乎没有任何示例显示自定义 Editor 的使用。唯一的提示是Item.getPropertyEditorClass() 的javadoc,它有一个默认的接口实现,所以你通常不会看它。这里有一个答案说不要看这种方法的事实也没有帮助:/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 2011-03-20
    相关资源
    最近更新 更多