8

我试图捕捉 JavaFX Slider上的事件,尤其是表示拖动停止并被释放的事件。起初我用这样的valueProperty模型代码

slider.valueProperty().addListener(new ChangeListener<Number>() {
    @Override
    public void changed(ObservableValue<? extends Number> ov, Number oldValue, Number newValue) {
        log.fine(newValue.toString());
    }
});

但它更新得太频繁了。所以我在 SceneBuilder 和 API 中进行了搜索,发现了一些有趣的东西,比如

slider.setOnMouseDragReleased(new EventHandler<MouseDragEvent>() {
    @Override
    public void handle(MouseDragEvent event) {
        System.out.println("setOnMouseDragReleased");
    }
});

但他们永远不会被解雇。只有一些像setOnMouseReleased我一样得到一些输出,但例如,这对整个节点(如标签等)都有影响。

所以我的问题是,这是知道值不再变化的正确钩子(如果可能在释放鼠标后,如拖放手势),也许还有一个小例子来查看它的界面是否正常工作。

4

3 回答 3

9

向滑块的valueChangingProperty添加一个更改侦听器,以了解滑块的值何时发生更改,并对值更改采取您想要的任何操作。

下面的示例将在滑块开始更改时记录滑块的值,并在完成更改时再次记录。

滑块值更改记录器

import javafx.application.Application;
import javafx.beans.value.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class SliderChangeLog extends Application {
    private final ListView<String> startLog = new ListView<>();
    private final ListView<String> endLog   = new ListView<>();

    @Override public void start(Stage stage) throws Exception {
        Pane logsPane = createLogsPane();
        Slider slider = createMonitoredSlider();

        VBox layout = new VBox(10);
        layout.setAlignment(Pos.CENTER);
        layout.setPadding(new Insets(10));
        layout.getChildren().setAll(
                slider,
                logsPane
        );
        VBox.setVgrow(logsPane, Priority.ALWAYS);

        stage.setTitle("Slider Value Change Logger");
        stage.setScene(new Scene(layout));
        stage.show();
    }

    private Slider createMonitoredSlider() {
        final Slider slider = new Slider(0, 1, 0.5);
        slider.setMajorTickUnit(0.5);
        slider.setMinorTickCount(0);
        slider.setShowTickMarks(true);
        slider.setShowTickLabels(true);
        slider.setMinHeight(Slider.USE_PREF_SIZE);

        slider.valueChangingProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(
                    ObservableValue<? extends Boolean> observableValue,
                    Boolean wasChanging,
                    Boolean changing) {
                String valueString = String.format("%1$.3f", slider.getValue());

                if (changing) {
                    startLog.getItems().add(
                            valueString
                    );
                } else {
                    endLog.getItems().add(
                            valueString
                    );
                }
            }
        });
        return slider;
    }

    private HBox createLogsPane() {
        HBox logs = new HBox(10);
        logs.getChildren().addAll(
                createLabeledLog("Start", startLog),
                createLabeledLog("End",   endLog)
        );
        return logs;
    }

    public Pane createLabeledLog(String logName, ListView<String> log) {
        Label label = new Label(logName);
        label.setLabelFor(log);

        VBox logPane = new VBox(5);
        logPane.getChildren().setAll(
                label,
                log
        );

        logPane.setAlignment(Pos.TOP_LEFT);

        return logPane;
    }

    public static void main(String[] args) { launch(args); }
}
于 2013-09-19T17:40:38.490 回答
2

有时您可能想知道用户何时移动滑块,而滑块值由于绑定到属性而发生变化。一个示例是在媒体播放器视图上使用的滑块以显示媒体时间线。滑块不仅显示时间,还允许用户快进或快退。滑块绑定到媒体播放器的当前时间,这会触发滑块上的更改值。如果用户移动滑块,您可能想要检测拖动以停止媒体播放器,让媒体播放器寻找新的时间并继续播放。不幸的是,似乎在滑块上触发的唯一拖动事件是 setOnDragDetected 事件。所以我使用以下两种方法来检查滑块拖动。

    slider.setOnDragDetected(new EventHandler<Event>() {
        @Override
        public void handle(Event event) {
            currentPlayer.pause();
            isDragged=true;     
        }
    });

slider.setOnMouseReleased(new EventHandler<Event>() {
        @Override
        public void handle(Event event) {
            if(isDragged){
                currentPlayer.seek(Duration.seconds((double) slider.getValue()));
                currentPlayer.play();
                isDragged=false;
            }       
        }
    });
于 2019-02-10T06:13:59.867 回答
0

Jewelsea 的回答对于让我走上正轨非常有帮助,但是如果“snapToTicks”处于打开状态,则会导致不良行为。Jewelsea 的侦听器捕获的“结束”值是在快照发生之前,而快照后的值永远不会被捕获

我的解决方案设置了一个监听器,valuevalueChanging用作哨兵。就像是:

slider.valueProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(
                ObservableValue<? extends Number> observableValue,
                Number previous,
                Number now) {
            if (!slider.isValueChanging()
                  || now.doubleValue() == slider.getMax()
                  || now.doubleValue() == slider.getMin()) {
                // This only fires when we're done
                // or when the slider is dragged to its max/min.
            }
        }
    });

我发现检查最大值和最小值对于捕捉用户在放开鼠标之前将滑块一直拖动到其左侧或右侧边界的极端情况是必要的。出于某种原因,这不会像我期望的那样触发事件,所以这似乎是一个不错的解决方法。

注意:与jewelsea 不同,为了简单起见,我忽略了起始值。

注 2:我实际上使用的是 ScalaFX 2,所以我不确定这个 Java 翻译是否按原样编译。

于 2015-03-06T22:22:06.990 回答