JavaFX - 将滑块绑定到按钮的禁用 属性

JavaFX - Binding Slider to Button's Disable Property

我试图仅在 Slider 的 Thumb 超过新值(或位置)时启用 Button。

下面是window的初始状态:

只要我开始拖动 Thumb,按钮就会被激活,即使 Thumb 还没有超过新值(即使在值之间也被激活):

如果我中止拖动操作(释放鼠标按钮)和 Thumb returns 到初始位置(或值),按钮不会被禁用。

代码:

private void configSldFontSize() {
    
    sldFontSize.valueProperty().addListener((ov, oldValue, newValue) -> {

        if (oldValue == newValue) {
            btnApply.setDisable(true);
        } else {
            btnApply.setDisable(false);
        }

    });

}

可能是配置错误?

private void setUpSlrFontSize() {

    sldFontSize.setMin(0);
    sldFontSize.setMax(FontSize.values().length - 1);
    sldFontSize.setValue(viewFactory.getFontSize().ordinal());
    sldFontSize.setMajorTickUnit(1);
    sldFontSize.setMinorTickCount(0);
    sldFontSize.setBlockIncrement(1);
    sldFontSize.setSnapToTicks(true);
    sldFontSize.setShowTickMarks(true);
    sldFontSize.setShowTickLabels(true);
    sldFontSize.setLabelFormatter(new StringConverter<Double>() {

        @Override
        public Double fromString(String arg0) {
            return null;
        }

        @Override
        public String toString(Double obj) {
            int i = obj.intValue();
            return FontSize.values()[i].toString();
        }

    });

}

======= 复制条件的小代码实现 ==========

注意:

我正在使用 Java 11(我想 Java 8 没有必要)。因此可能有必要在“运行 配置”中包含以下行。

在 Package Explorer 中:鼠标右键点击“Main.Java => 运行 As => 运行 配置...”,select “(x) = Arguments" 选项卡并将行如下图所示,正在更改您的 JAVAFX LIB 文件夹的路径

代码 Main.java

public class Main extends Application {
@Override
public void start(Stage primaryStage) {
    try {
        VBox root = new VBox();
        
        Button btnApply = new Button("Apply");
        btnApply.setDisable(true);
        
        Slider sldFontSize = new Slider();
        sldFontSize.setMin(0);
        sldFontSize.setMax(2);
        sldFontSize.setValue(1);
        sldFontSize.setMajorTickUnit(1);
        sldFontSize.setMinorTickCount(0);
        sldFontSize.setBlockIncrement(1);
        sldFontSize.setSnapToTicks(true);
        sldFontSize.setShowTickMarks(true);
        sldFontSize.setShowTickLabels(true);
        
        sldFontSize.valueProperty().addListener((ov, oldValue, newValue) -> {

            if (oldValue == newValue) {
                btnApply.setDisable(true);
            } else {
                btnApply.setDisable(false);
            }

        });
        
        root.getChildren().addAll(sldFontSize, btnApply);
        
        Scene scene = new Scene(root,200,100);
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        
        primaryStage.setScene(scene);
        primaryStage.show();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

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

}

当您移动滑块时,每次鼠标移动时值都会发生变化。 所以比较 oldValuenewValue 来自 listener 是行不通的。你需要另一个策略。

您可以在初始化时保存默认值,并在释放鼠标时将当前值与默认值进行比较:

sldFontSize.setOnMouseReleased(event -> btnApply.setDisable(sldFontSize.getValue() == defaultValue));

完整代码:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Sample extends Application {

    @Override
    public void start(Stage primaryStage) {
        try {
            VBox root = new VBox();

            Button btnApply = new Button("Apply");
            btnApply.setDisable(true);

            Slider sldFontSize = new Slider();
            sldFontSize.setMin(0);
            sldFontSize.setMax(2);
            sldFontSize.setValue(1);
            sldFontSize.setMajorTickUnit(1);
            sldFontSize.setMinorTickCount(0);
            sldFontSize.setBlockIncrement(1);
            sldFontSize.setSnapToTicks(true);
            sldFontSize.setShowTickMarks(true);
            sldFontSize.setShowTickLabels(true);

            double defaultValue = sldFontSize.getValue();
            sldFontSize.setOnMouseReleased(event -> btnApply.setDisable(sldFontSize.getValue() == defaultValue));

            root.getChildren().addAll(sldFontSize, btnApply);
            Scene scene = new Scene(root, 200, 100);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

一般来说,您可以创建一个单独的 属性 来表示您希望从滑块获得的实际值,然后向该 属性 注册一个侦听器。请注意,更改侦听器仅在值实际更改时才会收到通知,因此侦听器中的 if (oldValue==newValue) 将始终为 false。

在这种情况下,由于感兴趣的值只是将滑块值转换为 int 的结果,您可以将 IntegerProperty 绑定到滑块值:

        IntegerProperty intValue = new SimpleIntegerProperty();
        intValue.bind(sldFontSize.valueProperty());

        intValue.addListener((ov, oldValue, newValue) -> btnApply.setDisable(false));

更一般地说,创建一个自定义绑定或向 sldFontSize.valueProperty() 注册一个侦听器来更新您的另一个 属性。例如。使用你的 FontSize,我认为它是 enum:

ObjectProperty<FontSize> fontSizeProperty = new SimpleObjectProperty<>();
fontSizeProperty.bind(Bindings.createObjectBinding(
    () -> FontSize.value()[(int) sldFontSize.getValue()],
    sldFontSize.valueProperty()));
fontSizeProperty.addListener((obs, oldFontSize, newFontSize) -> btnApply.setDisable(false));

为了更精确,您可以实现舍入而不是简单地转换等。您可能还想保存“最后应用的值”并在决定是否 disable/enable 按钮时与它进行比较,等等

将所有这些放在一起你会得到类似的东西:

public class Main extends Application {
    
    @Override
    public void start(Stage primaryStage) {
        try {
            VBox root = new VBox();
            
            IntegerProperty lastSavedFontSize = new SimpleIntegerProperty(1);
            IntegerProperty sldIntValue = new SimpleIntegerProperty();

            Button btnApply = new Button("Apply");
            btnApply.setOnAction(e -> lastSavedFontSize.set(sldIntValue.get()));
            btnApply.disableProperty().bind(sldIntValue.isEqualTo(lastSavedFontSize));

            Slider sldFontSize = new Slider();
            sldFontSize.setMin(0);
            sldFontSize.setMax(2);
            sldFontSize.setValue(lastSavedFontSize.get());
            sldFontSize.setMajorTickUnit(1);
            sldFontSize.setMinorTickCount(0);
            sldFontSize.setBlockIncrement(1);
            sldFontSize.setSnapToTicks(true);
            sldFontSize.setShowTickMarks(true);
            sldFontSize.setShowTickLabels(true);
            
            sldIntValue.bind(Bindings.createIntegerBinding(
                    () ->(int) Math.round(sldFontSize.getValue()), 
                    sldFontSize.valueProperty()));


            root.getChildren().addAll(sldFontSize, btnApply);

            Scene scene = new Scene(root, 200, 100);

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

另一种方法完全是利用滑块的valueChangingProperty()。当且仅当用户当前正在更改值时,此布尔值 属性 为真。请注意,在某些情况下,valueChangingProperty() 可能会在 设置滑块的最终值之前变为假 ,因此最安全的方法是同时监听这两个属性。这是使用这种方法的版本:

public class Main extends Application {
    
    @Override
    public void start(Stage primaryStage) {
        try {
            VBox root = new VBox();
            
            IntegerProperty lastSavedFontSize = new SimpleIntegerProperty(1);

            Button btnApply = new Button("Apply");
            btnApply.setDisable(true);  


            Slider sldFontSize = new Slider();
            sldFontSize.setMin(0);
            sldFontSize.setMax(2);
            sldFontSize.setValue(lastSavedFontSize.get());
            sldFontSize.setMajorTickUnit(1);
            sldFontSize.setMinorTickCount(0);
            sldFontSize.setBlockIncrement(1);
            sldFontSize.setSnapToTicks(true);
            sldFontSize.setShowTickMarks(true);
            sldFontSize.setShowTickLabels(true);

            btnApply.setOnAction(e -> {
                // apply change, and do
                lastSavedFontSize.set((int) sldFontSize.getValue());
            });
            
            ChangeListener<Object> sliderListener = (obs, oldV, newV) -> {
                if (!sldFontSize.isValueChanging()) {
                    btnApply.setDisable((int)sldFontSize.getValue() == lastSavedFontSize.get());
                }
            };

            sldFontSize.valueProperty().addListener(sliderListener);
            sldFontSize.valueChangingProperty().addListener(sliderListener);
            lastSavedFontSize.addListener(sliderListener);

            root.getChildren().addAll(sldFontSize, btnApply);

            Scene scene = new Scene(root, 200, 100);

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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