0

I want to customize a Slider and found a working example here on stackoverflow:

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

public class SliderWithLabeledThumb extends Application {

    @Override
    public void start(Stage primaryStage) {
        Slider slider = new Slider();
        StackPane root = new StackPane(slider);
        root.setPadding(new Insets(20));

        Scene scene = new Scene(root);

        slider.applyCss();
        slider.layout();
        Pane thumb = (Pane) slider.lookup(".thumb");
        Label label = new Label();
        label.textProperty().bind(slider.valueProperty().asString("%.1f"));
        thumb.getChildren().add(label);


        primaryStage.setScene(scene);
        primaryStage.show();

    }

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

Unfortunately, if I look up the thumb of the slider in my controller class for an fxml view, I will always get a NullPointerException when trying to add the Label to the thumb's children… Thumb can obviously not be looked up. Then I found another answer here that says lookup() must be called after stage.show(), so here is my question again:
How can I look up a slider thumb in a controller class that manages fxml components (that does not have a stage.show())?
I tried creating a Slider in code only and also used one from fxml, both lookups return null when calling that in the controller's initialize(…) method.
Is it somehow possible to customize a Slider's thumb in a controller class?

EDIT:

The code that produces the exception is in the controller class that gets automatically instantiated due to an entry in the corresponding xml file. No matter if I use a Slider loaded from fxml by annotation, having just a member and initialize that or create a new Slider object. As long as I do that in the controller class, I will keep getting NullPointerExceptions from the line commented in the code below:

public class MainViewController implements Initializable {

    @FXML private VBox root;
    private Slider timeSlider;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        timeSlider = new Slider();
        root.getChildren.add(timeSlider);
        timeSlider.setMax(360);
        timeSlider.setMin(0);
        timeSlider.setCursor(Cursor.OPEN_HAND);
        timeSlider.applyCss();
        timeSlider.layout();

        Pane thumb = (Pane) timeSlider.lookup(".thumb");
        Label label = new Label();
        label.textProperty().bind(timeSlider.valueProperty().asString("%.1f"));
        // the following line points to thumb, which is null then… (debugger proof)
        thumb.getChildren().add(label);
    }
}

In the Main.java I just load the fxml file:

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(getClass().getResource("MainView.fxml"));
            Parent root = loader.load();
            Scene scene = new Scene(root,800,600);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

Finally, the fxml file:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<VBox fx:id="root" maxHeight="600" maxWidth="800" prefHeight="600"
    prefWidth="800" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="application.MainViewController">
</VBox>
deHaar
  • 17,687
  • 10
  • 38
  • 51
  • Please clarify which line throws the `NullPointerException`, and include the code you used (which I assume is different than the working example you have posted). – Itai Aug 23 '17 at 10:47
  • Thanks for the suggestion, @sillyfly … I have edited my answer, please have another look ;-) – deHaar Aug 23 '17 at 11:50
  • 1
    the thumb is installed by the skin, consequently it's only available after the skin has been set (which happens at the time it's added to the scenegraph) - you can lookup it f.i. in a listener to its skin property – kleopatra Apr 01 '21 at 10:54

1 Answers1

3

Put the lookup code in the slider listener like below:

volumeBar.valueProperty().addListener((observable, oldValue, newValue) -> {
        Pane thumb = (Pane) volumeBar.lookup(".thumb");
        if(thumb.getChildren().size() == 0){
            Label label = new Label();
            label.textProperty().bind(volumeBar.valueProperty().asString("%.0f"));
            thumb.getChildren().add(label);
        }
    });
Jian
  • 31
  • 2