JavaFX - set slider value after mouse button drag

I am writing a music player and I have no idea how to handle the code slider drag and drop to set the value after the user releases the mouse button. When I write a simple MouseDragged method, dragging results in an unaesthetic "rewind" of the sound because the media player changes the value every time the slider moves. When you play the slider, the listener value of the media player is automatically changed to synchronize with the length of the track. This is what I got so far.

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()));

                    }

                });

      

+3


source to share


2 answers


The valueChanging

slider
property indicates whether the slider is in the process of being changed. This is an observable property, so you can directly attach a listener to it and respond when the value stops changing:

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()));
        }
    }
});

      

This will not change the position of the player if the user clicks on the "track" on the slider or uses the keyboard to move. To do this, you can register a listener with a value property. You have to be careful here because the value will also change through your time listener. In theory, a time listener should set the value of the slider, and then this should trigger an attempt to set the player's current time to the exact value it already has (which would result in a missing-op). Round off errors, however, are likely to result in a lot of small adjustments, causing the "static" you see. To fix this, only move the media player if the change exceeds some small minimum amount:

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());
            }
        }
    }
});

      



Finally, you don't want your time listener to move the slider if the user tries to drag it:

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());
        }
    }
};

      

Here's a complete example (using lambdas for brevity):

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);
    }
}

      

+11


source


@James_D Could you please explain to me what exactly the change, oldValue and obs meant for me. What exactly is requested in the if statement?



    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());
        }
    });

      

0


source







All Articles