4

In my FXML I have a simple slider:

<Slider fx:id="timeSlider" showTickLabels="true" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="4"/>

This slider is part of a desktop music application I am writing and it signifies how far into the song has been played. I have written a change event that is called everytime the the song time is changed, and this successfully accomplishes sliding the knob/thumb down.

Now I am trying to also to color the left part (already played) blue and the right part (yet to be played) white as the knob slides down. If I hard code this line into my CSS i can accomplish fading from blue/white at the 50% mark.

.slider .track {-fx-background-color: linear-gradient(to right, #90C7E0 50%, white 50%);}

However, I need this to be dynamic based on how far I am in the song. I have written the following code, but can't seem to get the CSS style to apply

song.getMediaPlayer().currentTimeProperty().addListener(new ChangeListener<Duration>() {
        @Override
        public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
            if (newValue != null && timeSlider != null) {
                timeSlider.setValue(newValue.toSeconds());
                double percentage = (newValue.toSeconds()/song.getDuration())*100;
                String css = ".slider .track{-fx-background-color: linear-gradient(to right, #90C7E0 " +
                 percentage + "%, white " + percentage + "%);}";
                timeSlider.getStyleClass().add(css);
            }
        }
    });

I believe it's something to do with how I am adding the CSS as a style, because even adding a simple non dynamic style does not work. I get no errors so I am not sure what I am doing wrong.

Any help is appreciated, and better ways to accomplish this are also appreciated. Thanks!

8
  • percentage + "%, white " + percentage + "% Is not that supposed to be percentage + "%, white " + (100-percentage )+ "%"? Commented May 27, 2018 at 13:43
  • It might be... I haven't had the chance to play with the CSS statement since I can't even get it to apply. Right now I have swapped the dynamic css with the more simple ".slider .track {-fx-background-color: linear-gradient(to right, #90C7E0 50%, white 50%);}" and it still doesn't work when I try to apply it, even though if I put that directly in the CSS file it does work. Commented May 27, 2018 at 13:47
  • Style classes don’t work like this: getStyleClass() gives a list of CSS class names that are used to determine which rules from an external css file are applied to the node. Commented May 27, 2018 at 13:49
  • I had a feeling it was something to do with this... any suggestions on how I can accomplish this instead? Commented May 27, 2018 at 13:51
  • you shuold use Node.setStyle() Commented May 27, 2018 at 13:51

2 Answers 2

8

The method getStyleClass() returns a list of CSS class names associated with the node. These class names are used to determine which rules in an external CSS file are applied to the node. You cannot pass CSS selectors and rules in here.

Instead, write an external CSS file which contains a rule for the background color for the track. You can use a "looked-up color" here, which basically works like a "CSS variable". Define the "looked-up color" for the slider, and use it in a rule for the track to set the background color:

style.css:

.slider {
    -track-color: white ;
}
.slider .track {
    -fx-background-color: -track-color ;
}

Now, in the Java code, you can update the value for the looked-up color by calling setStyle("-track-color: /* someColor */") on the slider. Here is a quick example:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class SliderTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        Slider slider = new Slider();
        slider.valueProperty().addListener((obs, oldValue, newValue) -> {
            double percentage = 100.0 * (newValue.doubleValue() - slider.getMin()) / (slider.getMax() - slider.getMin());
            slider.setStyle("-track-color: linear-gradient(to right, #90C7E0 " + percentage+"%, white "+percentage+("%);"));
        });
        StackPane root = new StackPane(slider);
        root.setPadding(new Insets(20));
        Scene scene = new Scene(root);
        scene.getStylesheets().add("style.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

In your application, you can do the same using the current time of the media player (or just register a listener on the slider's value property, as that is changing too).

Sign up to request clarification or add additional context in comments.

1 Comment

I definitely like this better than my proposed solution. Thanks for the help!
0

With the help of those in the comments I have come up with a solution,

.getStyleClass.add(...) did not do what I originally thought, and I needed to use .setStyle(...) instead however, I needed to set the style on the slider TRACK not the slider itself. Here is my code now...

public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
            if (newValue != null && timeSlider != null) {
                double percentage = (newValue.toSeconds()/song.getDuration())*100;
                String cssValue = "-fx-background-color: linear-gradient(to right, #90C7E0 " +
                 percentage + "%, white " + percentage + "%)";
                StackPane sliderTrack = (StackPane) timeSlider.lookup(".track");
                sliderTrack.setStyle(cssValue);
                timeSlider.setValue(newValue.toSeconds());
            }
        }

1 Comment

A slightly more complex solution (see other answer) avoids the use of the CSS lookup, which (my opinion, others may vary) makes it just a little more robust.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.