【问题标题】:JavaFX - Set Slider value after dragging mouse buttonJavaFX - 拖动鼠标按钮后设置滑块值
【发布时间】:2014-12-20 13:28:52
【问题描述】:

我正在编写音乐播放器,但我不知道如何编写滑块拖动处理程序以在用户释放鼠标按钮后设置值。当我编写简单的 MouseDragged 方法时,拖动会带来非审美的“倒带”声音,因为每次滑块移动时媒体播放器都会改变值。在播放滑块时,媒体播放器侦听器会自动更改值以与轨道持续时间同步。这是我目前得到的。

ChangeListener<Duration> timeListener =  new ChangeListener<Duration>() {
    @Override
    public void changed(
            ObservableValue<? extends Duration> observableValue,
            Duration duration,
            Duration current) {
        durSlider
                .setValue(current
                        .toSeconds());
    }
};

durSlider.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {

                    mediaPlayer.seek(Duration.seconds(durSlider.getValue()));

                    }

                });

【问题讨论】:

    标签: user-interface javafx slider


    【解决方案1】:

    valueChanging property of the slider 表示滑块是否正在更改。它是一个可观察的属性,因此您可以直接为其附加一个侦听器,并在值停止更改时做出响应:

    durSlider.valueChangingProperty().addListener(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> obs, Boolean wasChanging, Boolean isNowChanging) {
            if (! isNowChanging) {
                mediaPlayer.seek(Duration.seconds(durSlider.getValue()));
            }
        }
    });
    

    如果用户点击滑块上的“轨道”或使用键盘移动它,这不会改变播放器的位置。为此,您可以使用 value 属性注册一个侦听器。您需要在这里小心,因为该值也会通过您的时间侦听器发生变化。理论上,时间监听器应该设置滑块的值,然后这应该会导致尝试将播放器的当前时间设置为它已经拥有的确切值(这将导致无操作)。但是,舍入误差可能会导致很多小的调整,从而导致您正在观察的“静态”。要解决此问题,请仅在更改超过某个最小量时才移动媒体播放器:

    private static double MIN_CHANGE = 0.5 ; //seconds
    
    // ...
    
    durSlider.valueProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> obs, Number oldValue, Number newValue) {
            if (! durSlider.isValueChanging()) {
                double currentTime = mediaPlayer.getCurrentTime().toSeconds();
                double sliderTime = newValue.doubleValue();
                if (Math.abs(currentTime - sliderTime) > 0.5) {
                    mediaPlayer.seek(newValue.doubleValue());
                }
            }
        }
    });
    

    最后,如果用户试图拖动滑块,您不希望时间监听器移动滑块:

    ChangeListener<Duration> timeListener =  new ChangeListener<Duration>() {
        @Override
        public void changed(
                ObservableValue<? extends Duration> observableValue,
                Duration duration,
                Duration current) {
            if (! durSlider.isValueChanging()) {
                durSlider.setValue(current.toSeconds());
            }
        }
    };
    

    这是一个完整的例子(为了简洁起见,使用 lambdas):

    import javafx.application.Application;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Slider;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.HBox;
    import javafx.scene.media.Media;
    import javafx.scene.media.MediaPlayer;
    import javafx.scene.media.MediaView;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    public class VideoPlayerTest extends Application {
    
        private static final String MEDIA_URL =
                "http://download.oracle.com/otndocs/products/javafx/oow2010-2.flv";
    
        private static final double MIN_CHANGE = 0.5 ;
    
        @Override
        public void start(Stage primaryStage) {
            MediaPlayer player = new MediaPlayer(new Media(MEDIA_URL));
            MediaView mediaView = new MediaView(player);
    
            Slider slider = new Slider();
            player.totalDurationProperty().addListener((obs, oldDuration, newDuration) -> slider.setMax(newDuration.toSeconds()));
    
            BorderPane root = new BorderPane(mediaView, null, null, slider, null);
    
            slider.valueChangingProperty().addListener((obs, wasChanging, isChanging) -> {
                if (! isChanging) {
                    player.seek(Duration.seconds(slider.getValue()));
                }
            });
    
            slider.valueProperty().addListener((obs, oldValue, newValue) -> {
                if (! slider.isValueChanging()) {
                    double currentTime = player.getCurrentTime().toSeconds();
                    if (Math.abs(currentTime - newValue.doubleValue()) > MIN_CHANGE) {
                        player.seek(Duration.seconds(newValue.doubleValue()));
                    }
                }
            });
    
            player.currentTimeProperty().addListener((obs, oldTime, newTime) -> {
                if (! slider.isValueChanging()) {
                    slider.setValue(newTime.toSeconds());
                }
            });
    
            Scene scene = new Scene(root, 540, 280);
            primaryStage.setScene(scene);
            primaryStage.show();
    
            player.play();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

    • 感谢您的回答,但您显然没有注意到我的 timeListener 属性。您的解决方案只能在不戴上它的情况下工作,但滑块不会与轨道持续时间同步。当我让你的听众使用它时,整个播放过程都会出错,因为滑块的值每秒都在变化,而不仅仅是当用户点击它时。
    • 啊,我认为它无论如何都会起作用,因为它应该只是寻找它已经在的相同位置。我想某处有一些舍入导致它反复进行非常小的调整。查看更新的答案。
    • 谢谢!现在它工作正常。对我来说看起来像是死路一条,但你的静态双变量想法很棒
    • 我已经有了几乎相同的代码,但想知道如何使关键调整生效?仍然不适用于您的版本...
    猜你喜欢
    • 2019-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-04
    • 2023-03-12
    • 1970-01-01
    • 2019-09-08
    • 2013-06-06
    相关资源
    最近更新 更多