You can do this fairly simply with either a slider or progress bar, using CSS to change the background color of the track. For a progress bar, just make the bar transparent.
The basic idea is to use a linear "gradient". If the starting value is x%
and the current value y%
, with y > x
, you need color stops at
(default 0%, default x%, green x%, green y%, default y%, default 100%)
where default
is the default background color. (Similarly with red replacing green and x
and y
switched if y < x
.)
Here's a SSCCE:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.Slider;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BidirectionalSlider extends Application {
@Override
public void start(Stage primaryStage) {
double startingValue = 0.5 ;
Slider slider = new Slider(0, 1, startingValue);
slider.styleProperty().bind(Bindings.createStringBinding(() -> {
double min = slider.getMin();
double max = slider.getMax();
double value = slider.getValue() ;
return createSliderStyle(startingValue, min, max, value);
}, slider.valueProperty()));
ProgressBar progressBar1 = new ProgressBar(0.1);
ProgressBar progressBar2 = new ProgressBar(0.9);
progressBar1.styleProperty().bind(Bindings.createStringBinding(() ->
createSliderStyle(startingValue, 0.0, 1.0, progressBar1.getProgress()),
progressBar1.progressProperty()));
progressBar2.styleProperty().bind(Bindings.createStringBinding(() ->
createSliderStyle(startingValue, 0.0, 1.0, progressBar2.getProgress()),
progressBar2.progressProperty()));
VBox root = new VBox(5, slider, progressBar1, progressBar2);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private String createSliderStyle(double startingValue, double min, double max, double value) {
StringBuilder gradient = new StringBuilder("-slider-track-color: ");
String defaultBG = "derive(-fx-control-inner-background, -5%) " ;
gradient.append("linear-gradient(to right, ").append(defaultBG).append("0%, ") ;
double valuePercent = 100.0 * (value - min) / (max - min);
double startingValuePercent = startingValue * 100.0;
if (valuePercent > startingValuePercent) {
gradient.append(defaultBG).append(startingValuePercent).append("%, ");
gradient.append("green ").append(startingValuePercent).append("%, ");
gradient.append("green ").append(valuePercent).append("%, ");
gradient.append(defaultBG).append(valuePercent).append("%, ");
gradient.append(defaultBG).append("100%); ");
} else {
gradient.append(defaultBG).append(valuePercent).append("%, ");
gradient.append("red ").append(valuePercent).append("%, ");
gradient.append("red ").append(startingValuePercent).append("%, ");
gradient.append(defaultBG).append(startingValuePercent).append("%, ");
gradient.append(defaultBG).append("100%); ");
}
return gradient.toString() ;
}
public static void main(String[] args) {
launch(args);
}
}
and the base CSS file (style.css
):
.slider, .progress-bar {
-slider-track-color: derive(-fx-control-inner-background, -5%) ;
}
.slider .track, .progress-bar .track {
-fx-background-color:
-fx-shadow-highlight-color,
linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),
-slider-track-color ;
}
.progress-bar .bar {
-fx-background-color: transparent ;
}
