Search code examples
cssjavafxprogress-barbackground-size

How to set specific css value for javaFX ProgressBar during Runtime?


I have usual CSS for my ProgressBar:

.progress-bar > .track {
  -fx-background-color: transparent;
}
.progress-bar > .bar {
  -fx-background-color: transparent;
  -fx-background-image: url('/images/progress_bar.png');
  -fx-background-size: 1600px 50px;
  -fx-background-repeat: no-repeat;
}

Image progress_bar.png is 3-colored bar that changes from green to red as it reaches it's end. It works nicely when application is run.

My problem are those hard coded values for background size. This work on full HD displays perfectly but on 1366x768 most of yellow/red parts are cut out. I would like to set this property during run-time to match screen size. Is this possible?

I tried to use setStyle() method in ProgressBar but without success. Maybe I am just doing it wrong so if anyone has working example...

I also tried to set width to 100% (instead of px) but that does not work either. It just shows compressed bar and then it stretches it to 100% width.

Here is image: progress_bar.png


Solution

  • I don't think this is possible using CSS alone. However a customized Skin could be used that replaces the bar Node with a ImageView and uses the bar as clip instead:

    progress.BarBackgroundProgressBarSkin

    public class BarBackgroundProgressBarSkin extends ProgressBarSkin {
    
        public BarBackgroundProgressBarSkin(ProgressBar control) {
            super(control);
        }
    
        private ImageView barBackground;
    
        @Override
        protected void initialize() {
            super.initialize();
    
            barBackground = new ImageView();
    
            barBackground.setManaged(false);
    
            barBackground.setPreserveRatio(false);
            barBackground.getStyleClass().setAll("bar-background");
            int barIndex;
    
            List<Node> children = getChildren();
            int size = children.size();
    
            for (barIndex = 0; barIndex < size && !children.get(barIndex).getStyleClass().contains("bar"); barIndex++) {
            }
    
            Region bar = (Region) children.set(barIndex, barBackground);
            barBackground.setClip(bar);
    
            // fill the bar for clipping
            bar.setBackground(new Background(new BackgroundFill(Color.WHITE, new CornerRadii(2), new Insets(3, 3, 4, 3))));
        }
    
        @Override
        protected void layoutChildren(double x, double y, double w, double h) {
            // set pos
            barBackground.setLayoutX(x);
            barBackground.setLayoutY(y);
    
            // set size
            barBackground.setFitHeight(h);
            barBackground.setFitWidth(w);
    
            super.layoutChildren(x, y, w, h);
        }
    
    }
    

    style.css

    .progress-bar {
        -fx-skin: 'progress.BarBackgroundProgressBarSkin';
    }
    
    .progress-bar > .bar-background {
        /* your image goes here */
        -fx-image: url('https://i.sstatic.net/glUAd.png');
    }
    
    .progress-bar > .track {
        -fx-background-color: transparent;
    }
    

    Demo

    @Override
    public void start(Stage primaryStage) {
    
        ProgressBar progressBar = new ProgressBar();
    
        StackPane root = new StackPane();
        root.getChildren().add(progressBar);
        progressBar.setPrefSize(300, 18);
    
        Scene scene = new Scene(root, 200, 200);
    
        scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
    
        Timeline tl = new Timeline(
                new KeyFrame(Duration.ZERO, new KeyValue(progressBar.progressProperty(), 0d)),
                new KeyFrame(Duration.seconds(10), new KeyValue(progressBar.progressProperty(), 1d, Interpolator.EASE_OUT))
        );
    
        tl.setCycleCount(Animation.INDEFINITE);
        tl.play();
    
        primaryStage.setScene(scene);
        primaryStage.show();
    }