This question already has answers here:
How to create Splash screen with transparent background in JavaFX
(2 answers)
Closed 9 years ago.
I'm interested how I can display "Loading" message during JavaFX startup. I noticed for example that when I start SceneBuilder I have to wait ~5 seconds before the application is displayed in front of the user. Can I for example display some message during JVM start time?
UPDATE
I tested this code:
public class test extends Application
{
private Pane splashLayout;
private ProgressBar loadProgress;
private Label progressText;
private WebView webView;
private Stage mainStage;
private static final int SPLASH_WIDTH = 676;
private static final int SPLASH_HEIGHT = 227;
public static void main(String[] args) throws Exception
{
launch(args);
}
#Override
public void init()
{
ImageView splash = new ImageView(new Image("http://fxexperience.com/wp-content/uploads/2010/06/logo.png"));
loadProgress = new ProgressBar();
loadProgress.setPrefWidth(SPLASH_WIDTH - 20);
progressText = new Label("Loading System Modules ...");
splashLayout = new VBox();
splashLayout.getChildren().addAll(splash, loadProgress, progressText);
progressText.setAlignment(Pos.CENTER);
splashLayout.setStyle("-fx-padding: 5; -fx-background-color: cornsilk; -fx-border-width:5; -fx-border-color: linear-gradient(to bottom, chocolate, derive(chocolate, 50%));");
splashLayout.setEffect(new DropShadow());
}
#Override
public void start(final Stage initStage) throws Exception
{
showSplash(initStage);
showMainStage();
webView.getEngine().documentProperty().addListener(new ChangeListener<Document>()
{
#Override
public void changed(ObservableValue<? extends Document> observableValue, Document document, Document document1)
{
if (initStage.isShowing())
{
loadProgress.progressProperty().unbind();
loadProgress.setProgress(1);
progressText.setText("All Modules Are Loaded!");
mainStage.setIconified(false);
initStage.toFront();
FadeTransition fadeSplash = new FadeTransition(Duration.seconds(1.2), splashLayout);
fadeSplash.setFromValue(1.0);
fadeSplash.setToValue(0.0);
fadeSplash.setOnFinished(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
initStage.hide();
}
});
fadeSplash.play();
}
}
});
}
private void showMainStage()
{
mainStage = new Stage(StageStyle.DECORATED);
mainStage.setTitle("FX Experience");
mainStage.setIconified(true);
// create a WebView.
webView = new WebView();
webView.getEngine().load("http://fxexperience.com/");
loadProgress.progressProperty().bind(webView.getEngine().getLoadWorker().workDoneProperty().divide(100));
// layout the scene.
Scene scene = new Scene(webView, 1000, 600);
webView.prefWidthProperty().bind(scene.widthProperty());
webView.prefHeightProperty().bind(scene.heightProperty());
mainStage.setScene(scene);
mainStage.show();
}
private void showSplash(Stage initStage)
{
Scene splashScene = new Scene(splashLayout);
initStage.initStyle(StageStyle.UNDECORATED);
final Rectangle2D bounds = Screen.getPrimary().getBounds();
initStage.setScene(splashScene);
initStage.setX(bounds.getMinX() + bounds.getWidth() / 2 - SPLASH_WIDTH / 2);
initStage.setY(bounds.getMinY() + bounds.getHeight() / 2 - SPLASH_HEIGHT / 2);
initStage.show();
}
}
I noticed that the splash image is started with the main stage simultaneity but the main stage is minimized. Can I freeze the appearance of the main stage until the splash screen is completed with the loading. I want to do some internal checks before I display the main stage.
Use javax.swing.JProgressBar to achieve this.
Check the details in JProgressBar (docs)
Related
I'm creating an JavaFX application with Scene Builder. I added a video at the beginning. So I wanna play video before my application start in fullscreen mode.
The Problem is when it is stopped I see only black screeen and nothing happened, I guess it is because video is fullscreen and it is not automatically closed.
I also have a bug before the video starts, some blink of my main window .I guess it is because video is placed in the controller that begins after my application starts.
How to close video or remove it after finish?
How to place video in main class?
Main Class
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
FXMLLoader loader = new
FXMLLoader(getClass().getResource("resources/fxml/card.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root, 1600, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setResizable(true);
primaryStage.getIcons().add(new Image("src/card/resources/logo-icon.png"));
primaryStage.show();
//adding resize and drag primary stage
ResizeHelper.addResizeListener(primaryStage);
//assign ALT+ENTER to maximize window
final KeyCombination kb = new KeyCodeCombination(KeyCode.ENTER,
KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event) {
if (kb.match(event)) {
primaryStage.setMaximized(!primaryStage.isMaximized());
primaryStage.setResizable(true);
Controller cont = Context.getInstance().getController();
if (!primaryStage.isMaximized()) {
cont.getBtnFont().setPrefWidth(20);
cont.getBtnPalette().setPrefWidth(20);
cont.getBtnQuestCards().setPrefWidth(20);
cont.getBtnNonQuestCards().setPrefWidth(20);
cont.getRandomCard().setTopAnchor(cont.getRandomCard(), 80.0);
cont.getRandomCard().setBottomAnchor(cont.getRandomCard(), 70.0);
cont.getRandomCard().setLeftAnchor(cont.getRandomCard(), 300.0);
cont.getRandomCard().setRightAnchor(cont.getRandomCard(), 200.0);
cont.getRandomCardBack().setTopAnchor(cont.getRandomCardBack(), 80.0);
cont.getRandomCardBack().setBottomAnchor(cont.getRandomCardBack(), 70.0);
cont.getRandomCardBack().setLeftAnchor(cont.getRandomCardBack(), 300.0);
cont.getRandomCardBack().setRightAnchor(cont.getRandomCardBack(), 200.0);
cont.getRectRandom().setWidth(1060);
cont.getRectRandom().setHeight(410);
cont.getRectRandomBack().setWidth(1060);
cont.getRectRandomBack().setHeight(410);
cont.getRandomCard().setPrefWidth(800);
cont.getRandomCard().setPrefHeight(200);
cont.getRandomCardBack().setPrefWidth(800);
cont.getRandomCardBack().setPrefHeight(200);
} else if (primaryStage.isMaximized()){
cont.getBtnFont().setPrefWidth(50);
cont.getBtnPalette().setPrefWidth(50);
cont.getBtnQuestCards().setPrefWidth(50);
cont.getBtnNonQuestCards().setPrefWidth(50);
cont.getRandomCard().setTopAnchor(cont.getRandomCard(), 150.0);
cont.getRandomCard().setBottomAnchor(cont.getRandomCard(), 130.0);
cont.getRandomCard().setLeftAnchor(cont.getRandomCard(), 450.0);
cont.getRandomCard().setRightAnchor(cont.getRandomCard(), 270.0);
cont.getRandomCardBack().setTopAnchor(cont.getRandomCardBack(), 150.0);
cont.getRandomCardBack().setBottomAnchor(cont.getRandomCardBack(), 130.0);
cont.getRandomCardBack().setLeftAnchor(cont.getRandomCardBack(), 450.0);
cont.getRandomCardBack().setRightAnchor(cont.getRandomCardBack(), 270.0);
cont.getRectRandom().setWidth(1160);
cont.getRectRandom().setHeight(760);
cont.getRectRandomBack().setWidth(1160);
cont.getRectRandomBack().setHeight(760);
cont.getRandomCard().setPrefWidth(800);
cont.getRandomCard().setPrefHeight(400);
cont.getRandomCardBack().setPrefWidth(800);
cont.getRandomCardBack().setPrefHeight(400);
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
Controller CLass:
#FXML
public void initialize(URL location, ResourceBundle resources) {
String path = new File("src/card/resources/intro.mp4").getAbsolutePath();
me = new Media(new File(path).toURI().toString());
mp = new MediaPlayer(me);
media.setMediaPlayer(mp);
mp.setAutoPlay(true);
media.setSmooth(true);
}
I'm not sure about the blink you see, but it should be easy to react when the video stops.
The JavaFX MediaPlayer class provides a method statusProperty() which returns an object of type ReadOnlyObjectProperty<MediaPlayer.Status>.
The class ReadOnlyObjectProperty<T> implements the interface ObservableValue<T> which provides the method addListener(ChangeListener<? super T>).
The interface ChangeListener<T> is a "functional interface" which requires an implementation for the method void changed(ObservableValue<T> observable, T oldValue, T newValue).
So, putting it together, you should be able to create a listener which will react to the changing status of your MediaPlayer:
private void addStatusListener(MediaPlayer mp) {
ReadOnlyObjectProperty<MediaPlayer.Status> statusProperty = mp.
statusProperty();
statusProperty.addListener((v, o, n) -> playerStatusChanged(v, o, n));
}
private void playerStatusChanged(
ObservableValue<? extends MediaPlayer.Status> observable,
MediaPlayer.Status oldValue, MediaPlayer.Status newValue) {
if (oldValue == MediaPlayer.Status.PLAYING &&
newValue == MediaPlayer.Status.STOPPED) {
// TODO: CALL METHOD TO REACT TO VIDEO BEING STOPPED.
}
}
SOLUTION FOUND!
I made animation to opacity of MediaView:
#FXML public void initialize(URL location, ResourceBundle resources) {
mp = new MediaPlayer(new Media(this.getClass().getResource(MEDIA_URL).toExternalForm()));
media.setMediaPlayer(mp);
media.setSmooth(true);
mp.setAutoPlay(true);
Timeline tm = new Timeline(new KeyFrame(Duration.millis(3000), new KeyValue(media.opacityProperty(), 0.0)));
tm.setDelay(Duration.millis(5500));
tm.play();
}
I am working on educational klondike game project with JavaFX.
What I am trying to do is to create dynamic scaling of whole game.
I used scale class to do so. It seems almost good, as it change the scale of all interface elements, apart of the background, which is a bit smaller then the actual window.
I already tried to manipulate with BackgroundSize, but it doesn't make any change. How can I fix it?
Thanks a lot for Your help!
I add pictures to show the problem:
public class Klondike extends Application {
private static final double WINDOW_WIDTH = 1400;
private static final double WINDOW_HEIGHT = 900;
Button changeThemeButton;
Button newGame;
Button undo;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
startGame(primaryStage);
}
public void startGame(Stage primaryStage){
Card.loadCardImages();
Game game = new Game();
game.setTableBackground(new Image(Common.loadThemeSettings().get(0)));
initializeButtons(game, primaryStage);
primaryStage.setTitle("Klondike Solitaire");
primaryStage.setScene(new Scene(game, WINDOW_WIDTH, WINDOW_HEIGHT));
// SCALLING:
Scale scale = new Scale(1, 1);
scale.xProperty().bind(game.widthProperty().divide(WINDOW_WIDTH));
scale.yProperty().bind(game.heightProperty().divide(WINDOW_HEIGHT));
game.getTransforms().add(scale);
primaryStage.setResizable(true);
// SCALLING END:
primaryStage.show();
}
BACKGROUND PICTURE:
public void setTableBackground(Image tableBackground) {
setBackground(new Background(new BackgroundImage(tableBackground,
BackgroundRepeat.REPEAT, BackgroundRepeat.REPEAT,
BackgroundPosition.CENTER, BackgroundSize.DEFAULT)));
}
I'm currently trying to create a Splash Screen for my program since it takes some time to start up.
The problem is that it takes a while to create the GUI (creating dialogues, updating tables etc.). And I can't move the GUI creation to a background thread (like the "Task" class), since I'll get an "Not on FXApplication Thread" exception.
I tried using:
Platform.runLater(new Runnable() {
public void run() {
//create GUI
}
}
And the "call" method of a Task:
public class InitWorker extends Task<Void> {
private Model model;
private ViewJFX view;
public InitWorker(Model model) {
this.model = model;
}
#Override
protected Void call() throws Exception {
View view = new View();
Collection collection = new Collection();
//do stuff
}
}
When I wrote the program in Swing I could just display and update the Splash Screen on the EventDispatchThread, without any real concurreny. The code looked like this:
public void build() {
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Menus");
menuCreator = new MenuCreatorOld (model, this);
menuCreator.createMenu();
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE, "Creating Toolbar");
toolBar = menuCreator.createToolBar();
createWesternPanelToolBar();
shoppingPanel = new ShoppingListOld(model, this, collectionController, shoppingController, controller);
centerTabbedPane = new JTabbedPane();
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Collection");
collectionPanel = new CollectionOld(model, collectionController, this, controller);
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Wish List");
wishPanel = new WishListOld(model, this, collectionController, wishController, controller);
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Folders Table");
//and so on
}
public static void updateProgressBar(int progressValue, String text) {
System.out.println("Loading Bar Value:"+progressValue);
progressBar.setValue(progressValue);
loadingLabel.setText(text);
progressBar.setString(text);
}
Is there any way to create the GUI in the background while displaying a Splash Screen with a loading bar?
Edit:
I had a look at my code and was able to decrease the startup time by 5 seconds. Most of the dialogs pull data from the database when they are created. So I moved the creation of the dialogs into their getter methods. That resulted in an improvement of 3 seconds. But I would still like to know if there is in a way to create the GUI on a background thread.
Edit:
As suggested, I also tried using "RunLater" in a "Task".
This way I can create the GUI and display the SplashScreen, but I can't update the progress bar and progress label, since the GUI creation blocks the JavaFX application thread. The progress bar and label are only updated, after the GUI has been fully created.
Here's an example you guys can run (I removed the splash screen and only kept the progress bar and progress label):
public class InitWorker extends Task<Void> {
private static ProgressBar progressBar;
private static Label progressLabel;
private static double PROGRESS_MAX = 5;
private double loadingValue;
public InitWorker() {
loadingValue = 0;
}
#Override
protected void succeeded() {
System.out.println("Succeeded");
}
#Override
protected void failed() {
System.out.println("Failed");
}
#Override
protected Void call() throws Exception {
System.out.println("RUNNING");
Platform.runLater(new Runnable() {
public void run() {
displaySplashScreen();
for(int i=0; i<10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
updateProgressBar(loadingValue++, "Label "+i);
Stage stage = new Stage();
Label label = new Label("Label " + i);
VBox panel = new VBox();
panel.getChildren().add(label);
Scene scene = new Scene(panel);
stage.setScene(scene);
stage.centerOnScreen();
stage.show();
}
// updateProgressBar(1, "Initializing...");
}});
return null;
}
public void updateProgressBar(double loadingValue, String text) {
progressBar.setProgress(loadingValue / PROGRESS_MAX);
progressLabel.setText(text);
}
public static void displaySplashScreen() {
Stage progressBarStage = new Stage();
progressBar = new ProgressBar();
Scene progressBarScene = new Scene(progressBar);
progressBarStage.setScene(progressBarScene);
Stage progressLabelStage = new Stage();
progressLabel = new Label("Loading...");
progressLabel.setPadding(new Insets(5));
progressLabel.setStyle("-fx-background-color: red");
Scene progressLabelScene = new Scene(progressLabel);
progressLabelStage.setScene(progressLabelScene);
double progressBarWidth = 500;
double progressBarHeight = 75;
//muss angezeigt werden, um sie abhängig von Größe zu positionieren
progressBarStage.show();
progressLabelStage.show();
//
progressBarStage.setWidth(progressBarWidth);
progressBarStage.setHeight(progressBarHeight);
progressBarStage.centerOnScreen();
progressBarStage.centerOnScreen();
progressLabelStage.setY(progressLabelStage.getY() + 25);
}
}
See Task documentation titled "A Task Which Modifies The Scene Graph", which provides an example:
final Group group = new Group();
Task<Void> task = new Task<Void>() {
#Override protected Void call() throws Exception {
for (int i=0; i<100; i++) {
if (isCancelled()) break;
final Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
Platform.runLater(new Runnable() {
#Override public void run() {
group.getChildren().add(r);
}
});
}
return null;
}
};
The above example add the rectangles to the scene graph via a 100 runLater calls. A more efficient way to do this would be to add the rectangles to a group not attached to the active scene graph, then only add the group to the active scene graph in the runLater call. For example:
final Group groupInSceneGraph = new Group();
Task<Void> task = new Task<Void>() {
#Override protected Void call() throws Exception {
final Group localGroup = new Group();
for (int i=0; i<100; i++) {
if (isCancelled()) break;
final Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
localGroup.getChildren().add(r);
}
Platform.runLater(new Runnable() {
#Override public void run() {
groupInSceneGraph.add(localGroup);
}
});
return null;
}
};
You can create and modify most scene graph objects off of the JavaFX application thread (including loading FXML), as long as the objects aren't attached to the active scene graph. By active scene graph I mean a scene graph which is currently attached as a scene to a displayed stage. (A complicated control such as a WebView may be an exception to this rule and may require creation on the JavaFX application thread).
You must only attach the scene graph objects created off of the JavaFX application thread to the active scene graph on the JavaFX application thread (for example using Platform.runLater()). And, you must work with them on the JavaFX application thread as long they continue to be attached to the active scene graph.
I am crazy about the feature of JavaFX, in Swing, I could do,
#Override
public void onPluginRegistered(final GamePlugin plugin) {
JRadioButtonMenuItem gameMenuItem = new JRadioButtonMenuItem(plugin.getGameName());
gameMenuItem.setSelected(false);
gameMenuItem.addActionListener(event -> {
if (core.getPlayers().isEmpty()) {
// Can't start a game with no players.
showErrorDialog(frame, ERROR_NO_PLAYERS_TITLE, ERROR_NO_PLAYERS_MSG);
gameGroup.clearSelection();
} else {
core.startNewGame(plugin);
}
});
gameGroup.add(gameMenuItem);
newGameMenu.add(gameMenuItem);
}
if I want to add a radio item whenever a plugin has registered.
However in JavaFX, it seems, you can't declare any global item of JavaFX, because once the start() is called, it starts a new constructor and everything you've done before is nothing (there is no variable share to me).
Here is my Javafx code.
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 500, 500);
scene.getStylesheets().add("./Buttons.css");
Region spacer = new Region();
spacer.setMinWidth(10);
primaryStage.setScene(scene);
primaryStage.show();
TabPane tabPane = new TabPane();
Tab tabData = new Tab("Get your data");
tabPane.getTabs().add(tabData);
Tab tabDisplay = new Tab("Visualize your data");
tabPane.getTabs().add(tabDisplay);
pluginGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>(){
#Override
public void changed(ObservableValue<? extends Toggle> ov,
Toggle old_toggle, Toggle new_toggle) {
if (pluginGroup.getSelectedToggle() != null) {
RadioButton chk = (RadioButton) new_toggle.getToggleGroup().getSelectedToggle();
chk.getText();
}
}
});
root.setCenter(tabPane);
FlowPane inputPanel = new FlowPane();
TextField source = new TextField ();
Button confirmButton = new Button("Get Your Resource!");
confirmButton.getStyleClass().add("GREEN");
inputPanel.getChildren().addAll(new Label("Input your source:"),
spacer, source, confirmButton);
root.setBottom(inputPanel);
RadioButton defaultBtn = new RadioButton("No data plugin are registered");
FlowPane pane = new FlowPane();
pane.getChildren().addAll(new Label("Select your data source"), spacer);
if (radioButtonBox != null) {
pane.getChildren().add(radioButtonBox);
}
tabData.setContent(pane);
}
#Override
public void onPluginRegistered(DataPlugin plugin) {
RadioButton button = new RadioButton(plugin.getName());
button.setToggleGroup(pluginGroup);
radioButtonBox.getChildren().add(button);
}
public void caller(String[] args) {
launch(args);
}
I want to initialize the javafx program from,
public static void main(String[] args) throws Exception {
DataFramework core = new ConcreteDataFramework();
GuiFramework gui = new GuiFramework(core);
core.addGuiListener(gui);
gui.caller(args);
core.registerPlugin(new CsvData());
}
It is weird that I can't add any radio button to the existing radioButtonBox every time I call onPluginRegistered(DataPlugin plugin) (The new radiobutton does not show up)
You should consider the start() method as the replacement for the main method. If your application needs access to some kind of service or model, create it in the start() (or init()) method. I would actually recommend making the Application subclass (which is inherently not reusable) as minimal as possible - it should just do the startup work - and factoring the remaining GUI code into a separate class. (If you use FXML, the FXML file can define the UI, and the Application subclass is then already pretty minimal: it just loads and displays the FXML.)
You haven't really provided enough context to make it clear what's going on here, but I'm guessing GuiFramework is the Application subclass you've shown part of, and DataFramework is an interface of some kind. I also assume GuiFramework is implementing some interface that defines the onPluginRegistered method.
So I would do:
public class GuiFramework implements PluginAware {
private final BorderPane root ;
private final DataFramework dataFramework ;
public GuiFramework(DataFramework dataFramework) {
this.dataframework = dataFramework ;
this.root = new BorderPane();
TabPane tabPane = new TabPane();
Tab tabData = new Tab("Get your data");
tabPane.getTabs().add(tabData);
// etc etc (remaining code from your start() method)
}
public Parent getView() {
return root ;
}
#Override
public void onPluginRegistered(DataPlugin plugin) {
RadioButton button = new RadioButton(plugin.getName());
button.setToggleGroup(pluginGroup);
radioButtonBox.getChildren().add(button);
}
}
and define a Main class for starting the application:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
DataFramework core = new ConcreteDataFramework();
GuiFramework gui = new GuiFramework(core);
core.addGuiListener(gui);
Scene scene = new Scene(gui.getView(), 500, 500);
scene.getStylesheets().add("./Buttons.css");
primaryStage.setScene(scene);
primaryStage.show();
core.registerPlugin(new CsvData());
}
// for environments not supporting JavaFX launch automatically:
public static void main(String[] args) {
launch(args);
}
}
I want to handle click on ProgressBar like on slider. and learn a percent of track.
I would use slider instead progressbar but it doesn't have a highlighted track until thumb.
I need create something like a progress in a music player of playing song, and possibility to seek with a click on progress.
Do anybody have a tips how can i do it?
Here is another approach. Real hybrid of slider and progress bar :). Meet SlidoProgressBar!
public class SlidoProgressBarDemo extends Application {
#Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root);
scene.getStylesheets().add(this.getClass().getResource("style.css").toExternalForm());
stage.setScene(scene);
stage.setTitle("Progress Controls");
double sliderWidth = 200;
final Slider slider = new Slider();
slider.setMin(0);
slider.setMax(50);
slider.setMinWidth(sliderWidth);
slider.setMaxWidth(sliderWidth);
final ProgressBar pb = new ProgressBar(0);
pb.setMinWidth(sliderWidth);
pb.setMaxWidth(sliderWidth);
final ProgressIndicator pi = new ProgressIndicator(0);
slider.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
pb.setProgress(new_val.doubleValue() / 50);
pi.setProgress(new_val.doubleValue() / 50);
}
});
StackPane pane = new StackPane();
pane.getChildren().addAll(pb, slider);
final HBox hb = new HBox();
hb.setSpacing(5);
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(pane, pi);
scene.setRoot(hb);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
with style.css:
.slider .track {
-fx-background-color:null; /* Hide the track */
-fx-background-insets: 1 0 -1 0, 0, 1;
-fx-background-radius: 2.5, 2.5, 1.5;
-fx-padding: 0.208333em; /* 2.5 */
}
The basic logic is to put slider and progress into stackpane. Give them the same width. Bind the progress values of them. Hide the track of the slider.
Output:
i solved this problem with code :
progress.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY){
Bounds b1 = progress.getLayoutBounds();
double mouseX = event.getSceneX();
double percent = (((b1.getMinX() + mouseX ) * 100) / b1.getMaxX());
//correcting a percent, i don't know when it need
percent -= 2;
progress.setProgress((percent) / 100);
//do something with progress in percent
}
}
});
Just a tornadoFX example:
package bj
import javafx.application.Application
import javafx.geometry.Insets
import javafx.scene.control.ProgressBar
import javafx.scene.control.Slider
import javafx.scene.paint.Color
import tornadofx.*
/**
* Created by BaiJiFeiLong#gmail.com at 18-12-13 下午9:28
*/
class MainView : View() {
private lateinit var progressBar: ProgressBar
private lateinit var slider: Slider
override val root = vbox {
stackpane {
padding = Insets(100.0)
progressbar(initialValue = .0) {
progressBar = this
maxWidth = Double.MAX_VALUE
}
slider(max = 1, value = .0) {
slider = this
}
}
}
init {
progressBar.progressProperty().bind(slider.valueProperty())
progressBar.paddingLeftProperty.bind(progressBar.heightProperty().divide(2))
progressBar.paddingRightProperty.bind(progressBar.heightProperty().divide(2))
}
}
class MainStylesheet : Stylesheet() {
init {
slider {
track {
backgroundColor = MultiValue(arrayOf(Color.TRANSPARENT))
}
}
}
}
class App : tornadofx.App(MainView::class, MainStylesheet::class)
fun main(args: Array<String>) {
Application.launch(App::class.java, *args)
}