Question

I have a StackPane with the size of (15px width, 400px height). I want to but a "Vertical ProgressBar" to that StackPane. What I was doing is to rotate the progressbar by 90 degree. However, the progressBar cannot fit in the stackpane with that rotation. It just shows as a small squared progressbar at the center of StackPane.

How can I fixed that?

Was it helpful?

Solution

Sample vertical progress bar.

class UpwardProgress {
    private ProgressBar progressBar    = new ProgressBar();
    private Group       progressHolder = new Group(progressBar);

    public UpwardProgress(double width, double height) {
        progressBar.setMinSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
        progressBar.setPrefSize(height, width);
        progressBar.setMaxSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
        progressBar.getTransforms().setAll(
                new Translate(0, height),
                new Rotate(-90, 0, 0)
        );
    }

    public ProgressBar getProgressBar() {
        return progressBar;
    }

    public Group getProgressHolder() {
        return progressHolder;
    }
}

Used in a sample app.

lookatthestars

import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.*;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.image.PixelWriter;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.transform.*;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.Random;

class UpwardProgress {
    private ProgressBar progressBar    = new ProgressBar();
    private Group       progressHolder = new Group(progressBar);

    public UpwardProgress(double width, double height) {
        progressBar.setMinSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
        progressBar.setPrefSize(height, width);
        progressBar.setMaxSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
        progressBar.getTransforms().setAll(
                new Translate(0, height),
                new Rotate(-90, 0, 0)
        );
    }

    public ProgressBar getProgressBar() {
        return progressBar;
    }

    public Group getProgressHolder() {
        return progressHolder;
    }
}

public class StarCounter extends Application {

    public static final Color INDIA_INK = Color.rgb(35, 39, 50);

    private static final int CANVAS_SIZE      = 400;
    private static final int N_STARS          = 1_000;

    private final Canvas canvas = new Canvas(CANVAS_SIZE, CANVAS_SIZE);
    private final Random random = new Random(42);
    private final IntegerProperty visibleStars = new SimpleIntegerProperty(0);
    private       Timeline timeline;

    @Override
    public void start(final Stage stage) {
        Group root = initProgress();
        clearCanvas();

        visibleStars.addListener((observable, oldValue, newValue) -> {
            if (newValue.intValue() > oldValue.intValue()) {
                addStars(newValue.intValue() - oldValue.intValue());
            }
        });

        stage.setScene(
            new Scene(
                new HBox(canvas, root),
                INDIA_INK
            )
        );
        stage.show();

        runSimulation();

        stage.getScene().setOnMouseClicked(event -> {
            resetSimulation();
            runSimulation();
        });
    }

    private Group initProgress() {
        UpwardProgress upwardProgress = new UpwardProgress(15, 400);

        ProgressIndicator bar = upwardProgress.getProgressBar();
        bar.setStyle("-fx-base: skyblue; -fx-accent: gold;");
        bar.progressProperty().bind(visibleStars.divide(N_STARS * 1.0));

        return upwardProgress.getProgressHolder();
    }

    private void resetSimulation() {
        clearCanvas();
        if (timeline != null) {
            timeline.stop();
            timeline = null;
        }
    }

    private void runSimulation() {
        timeline = new Timeline(
            new KeyFrame(
                Duration.seconds(0),
                new KeyValue(visibleStars, 0)
            ),
            new KeyFrame(
                Duration.seconds(10),
                new KeyValue(visibleStars, N_STARS)
            )
        );
        timeline.play();
    }

    private void clearCanvas() {
        canvas.getGraphicsContext2D().setFill(INDIA_INK);
        canvas.getGraphicsContext2D().fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
    }

    private void addStars(int nStarsToAdd) {
        GraphicsContext context = canvas.getGraphicsContext2D();
        PixelWriter writer = context.getPixelWriter();
        for (int i = 0; i < nStarsToAdd; i++) {
            writer.setColor(random.nextInt(CANVAS_SIZE), random.nextInt(CANVAS_SIZE), Color.GOLD);
        }
    }

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

OTHER TIPS

There's another alternative to this, which is, to make your own custom vertical progress-bar. Sounds too much but isn't. The advantage of this approach is that this is more consistent in UI and more dynamically approachable. I used the above answer by @jewel but struggled with UI consistency and dynamic behavior of the progress-bar.

The approach being use a vbox for the progress-bar and inside it another vbox for bar.

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.device.ui.VerticalProgressBarController">
    <children>
        <VBox fx:id="progress_bar" alignment="BOTTOM_CENTER" prefHeight="450.0" prefWidth="20.0" style="-fx-border-color: black; -fx-border-radius: 2 2 2 2;">
         <children>
            <VBox fx:id="bar" prefHeight="0.0" prefWidth="20.0" style="-fx-border-radius: 2 2 2 2;"/>
         </children>
        </VBox>
    </children>
</VBox>

One can adjust the prefHeight of progress-bar and bar dynamically in controller or statically in .fxml file. Since here, only bar was the one I needed to adjust dynamically, so have set its prefHeight as 0 and adjust it appropriately in corresponding controller.

public class VerticalProgressBarController implements Initializable {

    @FXML
    VBox progress_bar;
    @FXML
    VBox bar;

    private double progress_bar,fixed_capacity;

    public void initialize(URL location, ResourceBundle resources) {

    progressBarHeight = progress_bar.getPrefHeight();
    bar.setMaxHeight(progressBarHeight);
    // initial bar color
    setGreenBar();
    // set the max capacity of the progress bar
    fixed_capacity = 100;
    // pass in the proportion; here wanted to show 15 on a scale of 100
    updateProgressBar(15 / fixed_capacity);
    }

    public void setGreenBar(){
        bar.setStyle("-fx-background-color: green");
    }

    public void setYellowBar(){
        bar.setStyle("-fx-background-color: yellow");
    }

    public void setRedBar(){
        bar.setStyle("-fx-background-color: red");
    }

    public void updateProgressBar(double progress){

        bar.setPrefHeight(progressBarHeight * progress);

        if(progress <= .60){
            setGreenBar();
        } else if(progress > .60 &&
                progress <= .75){
            setYellowBar();
        }else {
            setRedBar();
        }
    }

enter image description here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top