Continuing the discussion in this question,
How to make JavaFX Slider to move in discrete steps?
I added a "Play" button and a Timeline so that when the user clicks the "Play" button, the slider's knob repeatedly advances to the next tick mark until it reaches the end of the slider or the user clicks the "Stop" button. Unfortunately, the longer the animation runs, the more the knob moves between tick marks (and yes, setSnapToTicks
is true). Also, the value of the slider isn't what I expected. Ideally, it should be "-60, -59, -58, etc." but the Timeline moves the slider knob in small fractions at a time (e.g. -55.188833333333335). I've been banging my head on this problem for a long time. Here is my code:
public class SliderTest extends Application {
StringConverter<Double> stringConverter = new StringConverter<>() {
@Override
public String toString( Double object ) {
long seconds = object.longValue();
long minutes = TimeUnit.SECONDS.toMinutes( seconds );
long remainingseconds = Math.abs( seconds - TimeUnit.MINUTES.toSeconds( minutes ) );
return String.format( "%02d", minutes ) + ":" + String.format( "%02d", remainingseconds );
}
@Override
public Double fromString( String string ) {
return null;
}
};
@Override
public void start( Stage primaryStage ) throws Exception {
try {
Pane pane = new Pane();
pane.setStyle( "-fx-background-color: black;" );
Slider slider = new Slider( -60, 0, -60 );
slider.setBlockIncrement( 60 );
slider.setMajorTickUnit( 60 );
slider.setMinorTickCount( 60 );
slider.setShowTickLabels( true );
slider.setShowTickMarks( true );
slider.setSnapToTicks( true );
slider.setLabelFormatter( stringConverter );
slider.setPrefWidth( 1303 );
Button playStopButton = new Button( "Play" );
HBox buttonHBox = new HBox( playStopButton );
buttonHBox.setAlignment( Pos.CENTER );
Text sliderTime = new Text( "Time" );
sliderTime.setFont( new Font( 12 ) );
sliderTime.setFill( Color.WHITE );
sliderTime.setText( stringConverter.toString( slider.getValue() ) );
HBox hbox = new HBox( slider, sliderTime, buttonHBox );
hbox.setPrefWidth( 1428 );
hbox.setSpacing( 10 );
hbox.setAlignment( Pos.TOP_CENTER );
pane.getChildren().add( hbox );
Scene scene = new Scene( pane );
scene.getStylesheets().add( getClass().getResource( "/css/slider.css" ).toExternalForm() );
primaryStage.setScene( scene );
primaryStage.show();
Timeline timeline = new Timeline(
new KeyFrame( Duration.ZERO, new KeyValue( slider.valueProperty(), slider.getValue() ) ),
new KeyFrame( Duration.seconds( -slider.getValue() ), new KeyValue( slider.valueProperty(), 0 ) ) );
slider.valueProperty().addListener( ( observable, oldValue, newValue ) -> {
slider.setValue( newValue.intValue() );
System.out.println( slider.getValue() + " | " + newValue.doubleValue() );
// Set the text to the slider's new value.
sliderTime.setText( stringConverter.toString( slider.getValue() ) );
if ( newValue.doubleValue() == 0 ) {
// Reset the timeline and the text for the sliderButton.
timeline.stop();
timeline.getKeyFrames().clear();
playStopButton.setText( "Stop" );
}
} );
slider.setOnMousePressed( event -> {
// On mouse down, reset the timeline.
timeline.stop();
timeline.getKeyFrames().clear();
} );
slider.setOnMouseReleased( event -> {
// Set the new start position of the timeline.
timeline.getKeyFrames().add(
new KeyFrame( Duration.ZERO, new KeyValue( slider.valueProperty(), slider.getValue() ) ) );
timeline.getKeyFrames().add( new KeyFrame( Duration.seconds( -slider.getValue() ),
new KeyValue( slider.valueProperty(), 0 ) ) );
// Play the animation only if it was playing before.
if ( playStopButton.getText().equals( "Pause" ) ) {
timeline.play();
}
// Change the image for the playStopButton to stop when the thumb is moved to zero.
else if ( playStopButton.getText().equals( "Stop" ) && ( slider.getValue() < 0 ) ) {
playStopButton.setText( "Play" );
}
} );
playStopButton.setOnMouseClicked( event -> {
// Ignore mouse clicks on the playStopButton when the slider is at time zero.
if ( slider.getValue() != 0 ) {
if ( ( timeline.getStatus() == Status.STOPPED ) || ( timeline.getStatus() == Status.PAUSED ) ) {
playStopButton.setText( "Pause" );
timeline.play();
}
else if ( timeline.getStatus() == Status.RUNNING ) {
playStopButton.setText( "Play" );
timeline.pause();
}
}
} );
} catch ( Exception e ) {
e.printStackTrace();
}
}
public static void main( String[] args ) {
launch( args );
}