StyleableProperty:如何在运行时以编程方式更改值?

时间:2019-01-11 16:48:41

标签: java css javafx

我的用例:

  • 控件上的自定义属性,应该可以通过css进行配置
  • 该属性必须在运行时可以更改
  • 对于给定的控件实例,不得在重新应用css时还原程序更改

自定义StyleableProperty看起来很符合要求。下面是实现的示例(取自StyleablePropertyFactory类javadoc的不变)。

除最后一个要求外,其他都很好:在applyCss上,重新应用样式表中的默认值。复制:

  • 运行示例,请注意MyButton的初始“选定”状态(选中的复选框已绑定)为
  • 单击自定义按钮,请注意,“ selected”不会更改为false(尽管actionHandler对其进行了更改)
  • 单击第二个(“ toggle”)按钮,请注意,自定义按钮的选定状态变为false
  • 将鼠标悬停在自定义按钮上,请注意,所选状态会恢复为真

回落为true的原因(通过样式设置的值)可以追溯到applyCss,它发生在状态更改上……这是可以理解的,并且在大多数情况下可能是正确的事情,但是不以我为背景。

所以问题:

  • 我使用StyleableProperty是否在正确的轨道上?
  • 如果是这样,如何进行调整以使其在手动更改发生后不再重新应用?
  • 如果没有,还有什么要做?
  • 或者可能完全问错了一个问题:也许通过CSS可以设置的属性不是要(永久地)通过代码更改吗?

示例:

public class StyleableButtonDriver extends Application {

    /**
     * example code from class doc of StyleablePropertyFactory.
     */
    private static class MyButton extends Button {

        private static final StyleablePropertyFactory<MyButton> FACTORY 
            = new StyleablePropertyFactory<>(Button.getClassCssMetaData());

        MyButton(String labelText) {
            super(labelText);
            getStyleClass().add("my-button");
            setStyle("-my-selected: true");
        }

        // Typical JavaFX property implementation
        public ObservableValue<Boolean> selectedProperty() { return (ObservableValue<Boolean>)selected; }
        public final boolean isSelected() { return selected.getValue(); }
        public final void setSelected(boolean isSelected) { selected.setValue(isSelected); }

        // StyleableProperty implementation reduced to one line
        private final StyleableProperty<Boolean> selected =
             FACTORY.createStyleableBooleanProperty(
                    this, "selected", "-my-selected", s -> s.selected);

        @Override
        public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
            return FACTORY.getCssMetaData();
        }

        public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
            return FACTORY.getCssMetaData();
        }

    }
    private Parent createContent() {
        MyButton button = new MyButton("styleable button");
        button.setOnAction(e ->  {
            // does not work: reset on applyCss
            boolean isSelected = button.isSelected();
            button.setSelected(!isSelected);
        });

        CheckBox box = new CheckBox("button selected");
        box.selectedProperty().bind(button.selectedProperty());

        Button toggle = new Button("toggle button");
        toggle.setOnAction(e -> {
            boolean isSelected = button.isSelected();
            button.setSelected(!isSelected);
        });


        BorderPane content = new BorderPane(button);
        content.setBottom(new HBox(10, box, toggle));
        return content;
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent(), 300, 200));
        //same behavior as setting the style directly
//        URL uri = getClass().getResource("xstyleable.css");
//        stage.getScene().getStylesheets().add(uri.toExternalForm());
        // not useful: would have to override all
//        Application.setUserAgentStylesheet(uri.toExternalForm());
        stage.setTitle(FXUtils.version());
        stage.show();
    }

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

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

}

1 个答案:

答案 0 :(得分:2)

您处在正确的轨道上,但是由于您需要覆盖样式来源的默认优先级(用户代理样式表<以编程方式分配的Node.style属性),因此不能使用SyleablePropertyFactory用于创建此属性。如果属性是通过编程分配的,则需要创建一个CssMetaData对象,该对象将某个属性指示为不可设置。

private static class MyButton extends Button {

    private static final List<CssMetaData<? extends Styleable, ?>> CLASS_CSS_METADATA;
    private static final CssMetaData<MyButton, Boolean> SELECTED;

    static {
        SELECTED = new CssMetaData<MyButton, Boolean>("-my-selected", StyleConverter.getBooleanConverter()) {

            @Override
            public boolean isSettable(MyButton styleable) {
                // not setable, if bound or set by user
                return styleable.selected.getStyleOrigin() != StyleOrigin.USER  && !styleable.selected.isBound();
            }

            @Override
            public StyleableProperty<Boolean> getStyleableProperty(MyButton styleable) {
                return styleable.selected;
            }

        };

        // copy list of button css metadata to list and add new metadata object
        List<CssMetaData<? extends Styleable, ?>> buttonData = Button.getClassCssMetaData();
        List<CssMetaData<? extends Styleable, ?>> mybuttonData = new ArrayList<>(buttonData.size()+1);
        mybuttonData.addAll(buttonData);
        mybuttonData.add(SELECTED);
        CLASS_CSS_METADATA = Collections.unmodifiableList(mybuttonData);
    }

    MyButton(String labelText) {
        super(labelText);
        getStyleClass().add("my-button");
        setStyle("-my-selected: true");
    }

    // Typical JavaFX property implementation
    public ObservableValue<Boolean> selectedProperty() { return selected; }
    public final boolean isSelected() { return selected.get(); }
    public final void setSelected(boolean isSelected) { selected.set(isSelected); }

    // StyleableProperty implementation reduced to one line
    private final SimpleStyleableBooleanProperty selected = new SimpleStyleableBooleanProperty(SELECTED, this, "selected");

    @Override
    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
        return CLASS_CSS_METADATA;
    }

    public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
        return CLASS_CSS_METADATA;
    }

}