代码之家  ›  专栏  ›  技术社区  ›  kleopatra Aji kattacherry

StyleableProperty:如何在运行时通过编程更改值?

  •  2
  • kleopatra Aji kattacherry  · 技术社区  · 6 年前

    我的用例:

    • 控件上的自定义属性,该属性应可通过CSS进行配置
    • 属性必须在运行时可更改
    • 对于控件的给定实例,在重新应用CSS时,不能恢复编程更改。

    风俗习惯 StyleableProperty 看起来是实现需求的完美匹配。下面是一个实现的示例(从类javadoc StyleablePropertyFactory )

    除了最后一个要求,一切都很好:在applycs上,样式表的默认值被重新应用。复制:

    • 运行该示例,注意mybutton的初始“选定”状态(选中的复选框已绑定它)为true。
    • 单击自定义按钮,注意“选定”不会更改为false(尽管actionhandler会更改它)。
    • 单击第二个(“切换”)按钮,注意自定义按钮的选定状态更改为“假”
    • 将鼠标悬停在自定义按钮上,注意所选状态将恢复为“真”。

    返回真(通过样式设置的值)的原因可以追溯到在状态更改时发生的applycs…这是可以理解的,在大多数情况下,这可能是正确的做法,但在我的语境中却不是。

    所以问题是:

    • 使用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 回复  |  直到 6 年前
        1
  •  2
  •   fabian    6 年前

    您走的是正确的道路,但由于您需要覆盖样式源的默认优先级(用户代理样式表<以编程方式分配<css样式表< 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;
        }
    
    }