I'd like to create a custom loading screen for a JavaFX application. Don't want the user to see the Java coffee cup icon, I want to put my own graphic there!
I've found out how to provide a static image, or even an animated GIF, but I'm more interested in a Flash-like screen where I can specify what the state of the image looks like at certain percentages.
Any ideas?
For JavaFX2, you can set a custom preloader. You have complete control over then scene. I haven't used them personally, but this might be what you want.
http://docs.oracle.com/javafx/2/deployment/preloaders.htm
JavaFX preloader class
I have created a very simple preloader screen using native JavaFX APIs. Here it's explained how to do this: https://docs.oracle.com/javafx/2/deployment/preloaders.htm (old but workable examples) - this is newer and seems to be the same: https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/preloaders.html (Newer page and JavaFX version but I don't see the difference).
The older link is easier to read, because of page formatting.
Main class
package YOUR_PACKAGE_NAME;
import javafx.application.Application;
/**
* Minimal reproducible example (MRE) - Example of a simple JavaFX preloader.
* Java Main class for starting up the JavaFX application with a call to launch MainApplication.
* #author Remzi Cavdar - ict#remzi.info - #Remzi1993
*/
public class Main {
public static void main(String[] args) {
/*
* The following Java system property is important for JavaFX to recognize your custom preloader class.
* Which should extend javafx.application.Preloader.
*/
System.setProperty("javafx.preloader", Preloader.class.getName());
// Launch the main JavaFX application class.
Application.launch(MainApplication.class, args);
}
}
Preloader class
package YOUR_PACKAGE_NAME;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
/**
* Minimal reproducible example (MRE) - Example of a simple JavaFX preloader class.
* #author Remzi Cavdar - ict#remzi.info - #Remzi1993
*/
public class Preloader extends javafx.application.Preloader {
private ProgressBar progressBar;
private Stage stage;
private Scene createPreloaderScene() {
progressBar = new ProgressBar();
BorderPane borderPane = new BorderPane();
borderPane.setCenter(progressBar);
return new Scene(borderPane, 800, 600);
}
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
// I also recommend to set app icon: stage.getIcons().add();
stage.setTitle("YOUR TILE HERE");
stage.setScene(createPreloaderScene());
stage.show();
}
#Override
public void handleProgressNotification(ProgressNotification pn) {
progressBar.setProgress(pn.getProgress());
}
#Override
public void handleStateChangeNotification(StateChangeNotification evt) {
if (evt.getType() == StateChangeNotification.Type.BEFORE_START) {
stage.hide();
}
}
}
Testing
Tested on: 01-11-2022
Tested OS: Windows 11 - Version 21H2 (OS Build 22000.1098)
Tested with: OpenJDK 19 - Eclipse Temurin JDK with Hotspot 19.0.1+10 (x64) (See: https://adoptium.net/en-GB/temurin/releases/?version=19)
Tested with JavaFX (OpenJFX) version: OpenJFX 19 (See: https://openjfx.io and repo: https://github.com/openjdk/jfx)
If you're setting things up as shown on This blog entry, it looks like the answer would be 'no' - the loading graphic is just part of the overall options that are passed to the applet. Because this applet could be any java code (not just javaFX), there's no way to tie your custom renderer in.
you should use java timer:
Timer tm= new Timer();
Stage ilk;
int count;
public void check() {
ilk=new Stage();
TimerTask mission;
gorev = new TimerTask() {
#Override
public void run() {
Group root = new Group();
Scene scene;
scene = new Scene(root, 960, 540);
scene.setFill(Color.BLACK);
ilk.setScene(scene);
ilk.setTitle("Splash Screen");
sayac++;
if(count==5){
tm.cancel();
ilk.show();
}
}
};
tm.schedule(mission, 0, 2000);
}
For changing the coffee cup icon:
stage.getIcons().add(new Image("images/myimage.png"));
and here is a reference for a very clear preloader screen out there and awesome css too:
http://docs.oracle.com/javafx/2/best_practices/jfxpub-best_practices.htm
Related
I am using NetBeans IDE 8.2 for JavaFX. I already know that in order to change the position of a button I need to use setLayoutX/Y. I have tried this, and there is no effect on the buttons. Here is my code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javafxapplication2;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
*
* #author coolDawg1234
*/
public class JavaFXApplication2 extends Application {
#Override
public void start(Stage primaryStage) {
String x = "1";
String y = "0";
Button btn1 = new Button(x);
btn1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.print(x);
}
});
Button btn2 = new Button(y);
btn2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.print(y);
}
});
StackPane root = new StackPane();
root.getChildren().add(btn1);
btn1.setLayoutX(250);
btn1.setLayoutY(220);
root.getChildren().add(btn2);
btn1.setLayoutX(200);
btn1.setLayoutY(200);
Scene scene = new Scene(root, 1000, 1000);
primaryStage.setTitle("WHAT\'s GOOD MY MANS");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Netbeans gives me 0 errors for this, and everything other than the position of the buttons looks fine to me.
Please help me find my problem.
The container for your buttons is a StackPane. StackPane is an implementation of Pane such that it will, by default, layout its children at the center of itself. Therefore, whenever the scene needs to perform a layout, StackPane will set the layoutX and layoutY values (therefore overwriting whatever you had set) in its layoutChildren() method based on its own layout strategy. This behavior happens for most, if not all, subclasses of Pane.
If you need to manually position your child nodes, you need to use the generic Pane container. You can either choose to subclass it and provide your own layout strategy/logic, or simply set layoutX and layoutY values on the child nodes directly.
If you need the layout strategy provided by StackPane, but you would want it to be positioned slightly different from the default position, then you may be looking for translateXProperty() and translateYProperty().
30 root.getChildren().add(btn1);
31 btn1.setLayoutX(250);
32 btn1.setLayoutY(220);
33 root.getChildren().add(btn2);
34 btn1.setLayoutX(200);
35 btn1.setLayoutY(200);
Just take the right variable (consistent) btn1 gets two coordinates for the same direktion (250 for X in row 31, and 200 for X in row 34), so that button which has no directly set coordinates, has in this layout the coordinates (0,0).
I'm new to Vaadin and am still learning. Here I am trying to get a basic Vaadin project to compile. I want it to and display a modal window when the UI runs, but am having trouble. Here is what I have so far:
CaptchaUI.java -
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.UI;
public abstract class CaptchaUI extends UI {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
protected void init(VaadinRequest request) {
addWindow(new CaptchaWindow());
}
}
CaptchaWindow.java -
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
public class CaptchaWindow extends Window {
/**
*
*/
private static final long serialVersionUID = 1L;
public CaptchaWindow() {
// Some other UI content
setContent(new Label("Here's my UI"));
// Create a sub-window and set the content
Window subWindow = new Window("Sub Window");
VerticalLayout subContent = new VerticalLayout();
subContent.setMargin(true);
subWindow.setContent(subContent);
// Put some components in it
subContent.addComponent(new Label("Label"));
subContent.addComponent(new Button("Button"));
// Center it in the browser window
subWindow.center();
// Open it in the UI
addWindow(subWindow);
}
}
Could someone give me some help or recommendation to get it to display?
Thanks so much.
According to Vaadin docs it is just as easy as setting
setModal(true)
on the sub window to make it modal.
Please note that the modal feature of Vaadin is just a client side restriction. Modifying the HTML with debug tools in the browser could still make it possible to click buttons in the background.
I read that running AWT With JavaFX is a bad idea.
But we have an old application that runs on Swing and uses the AWT canvas(Cannot change due to an external library that uses the canvas)
Is it really such a horrible idea?
Is there a workaround for this?
Update
Although the code in this answer used to work on Windows with an earlier version of JavaFX, I retested the same same code on OS X 10.9.5 + JavaFX 8u72 and the code no longer works.
The line swingNode.setContent(awtInitializerTask.get()); which instructs the JavaFX thread to wait on the awt thread to initialize the awt canvas never returns, blocking execution and startup of the app.
Just put your AWT canvas in a SwingNode and watch your thread management and you'll be fine.
import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javax.swing.*;
import java.awt.*;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class AwtCanvasWrapper extends Application {
private static final int W = 200;
private static final int H = 100;
#Override public void start(final Stage stage) throws Exception {
final AwtInitializerTask awtInitializerTask = new AwtInitializerTask(() -> {
JPanel jPanel = new JPanel();
jPanel.add(new CustomAwtCanvas(W, H));
return jPanel;
});
SwingUtilities.invokeLater(awtInitializerTask);
SwingNode swingNode = new SwingNode();
swingNode.setContent(awtInitializerTask.get());
stage.setScene(new Scene(new Group(swingNode), W, H));
stage.setResizable(false);
stage.show();
}
private class AwtInitializerTask extends FutureTask<JPanel> {
public AwtInitializerTask(Callable<JPanel> callable) {
super(callable);
}
}
private class CustomAwtCanvas extends Canvas {
public CustomAwtCanvas(int width, int height) {
setSize(width, height);
}
public void paint(Graphics g) {
Graphics2D g2;
g2 = (Graphics2D) g;
g2.setColor(Color.GRAY);
g2.fillRect(
0, 0,
(int) getSize().getWidth(), (int) getSize().getHeight()
);
g2.setColor(Color.BLACK);
g2.drawString("It is a custom canvas area", 25, 50);
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
Here is the output:
Related Question
Interoperability between Graphics2D and GraphicsContext
Answering some additional questions
But that one is for swing components.
Yes, but awt components can be wrapped in Swing components.
furthermore It says in the docs that it should not be used of heavyweight components
Regardless, it seems to work for me, your mileage may vary.
performance is crucial for my app
Then try the approach with your app and check:
The painting is reliable.
The performance is acceptable.
If either of the above checks fail then you may need to use a different approach (though I do not know what that approach would be, maybe just spawn Frame as a new window in which to include the the AWT canvas content rather than embedding the canvas inside the JavaFX scene).
i was searching in google for hours and i still cant find the right answer, so i have a last chance to come here and ask.
i'm making school year JAVA FX project. I'm using NetBeans.
I have a point that i can see on the application i have. The problem is: I would like to have a big map (background) and I need to be able to move with my view. For example move by 50 to the right (x).
I have Application where I use Stage, Scene, StackPane.
I heard something about Dimensions in Java, but i can't use it in javafx application. Is there something similar, what can I use in my Application?
Thank you very much.
What I think you are asking for is a Scene with a map (represented as an Image) in the background and controls layered on top of the map to allow interaction with the map at certain positions. Your question is a little unclear, so I'm not exactly sure if that is what you are asking.
If so, here is some sample code to implement that.
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
/** Constructs a scene with a pannable Map background. */
public class PannableView extends Application {
private Image backgroundImage;
#Override public void init() {
backgroundImage = new Image("https://www.narniaweb.com/wp-content/uploads/2009/08/NarniaMap.jpg");
}
#Override public void start(Stage stage) {
stage.setTitle("Drag the mouse to pan the map");
// construct the scene contents over a stacked background.
StackPane layout = new StackPane();
layout.getChildren().setAll(
new ImageView(backgroundImage),
createKillButton()
);
// wrap the scene contents in a pannable scroll pane.
ScrollPane scroll = createScrollPane(layout);
// show the scene.
Scene scene = new Scene(scroll);
stage.setScene(scene);
stage.show();
// bind the preferred size of the scroll area to the size of the scene.
scroll.prefWidthProperty().bind(scene.widthProperty());
scroll.prefHeightProperty().bind(scene.widthProperty());
// center the scroll contents.
scroll.setHvalue(scroll.getHmin() + (scroll.getHmax() - scroll.getHmin()) / 2);
scroll.setVvalue(scroll.getVmin() + (scroll.getVmax() - scroll.getVmin()) / 2);
}
/** #return a control to place on the scene. */
private Button createKillButton() {
final Button killButton = new Button("Kill the evil witch");
killButton.setStyle("-fx-base: firebrick;");
killButton.setTranslateX(65);
killButton.setTranslateY(-130);
killButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent t) {
killButton.setStyle("-fx-base: forestgreen;");
killButton.setText("Ding-Dong! The Witch is Dead");
}
});
return killButton;
}
/** #return a ScrollPane which scrolls the layout. */
private ScrollPane createScrollPane(Pane layout) {
ScrollPane scroll = new ScrollPane();
scroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scroll.setPannable(true);
scroll.setPrefSize(800, 600);
scroll.setContent(layout);
return scroll;
}
public static void main(String[] args) { launch(args); }
}
For the example use the mouse (or probably touch commands or trackpad scroll gestures - though I haven't a touch screen or trackpad to test it) to drag the map around. Click on the button to "Kill the evil witch".
The solution works by:
Creating an ImageView to hold the background map.
Constructing the scene contents in a StackPane over the stacked background ImageView.
Wrapping the scene in a ScrollPane bound to the scene's size.
Setting properties on the ScrollPane to make it pannable.
I'm trying to make JavaFX Mnemonic work. I have some button on scene and what I want to achieve is to fire this button event by pressing Ctrl+S.
Here is a code sceleton:
#FXML
public Button btnFirst;
btnFirst.getScene().addMnemonic(new Mnemonic(btnFirst,
new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)));
Button's mnemonicParsing is false. (Well, while trying to make this work I've tried to set it to true, but no result). JavaFX documentation states that when a Mnemonic is registered on a Scene, and the KeyCombination reaches the Scene unconsumed, then the target Node will be sent an ActionEvent. But this doesn't work, probably, I'm doing wrong...
I can use the standard button's mnemonic (by setting mnemonicParsing to true and prefix 'F' letter by underscore character). But this way user have to use Alt key, that brings some strange behaviour on browsers with menu bar (if application is embedded into web page than browser's menu activated after firing button event by pressing Alt+S).
Besides, standard way makes it impossible to make shortcuts like Ctrl+Shift+F3 and so on.
So, if there some way to make this work?
For your use case, I think you actually want to use an accelerator rather than a mnemonic.
button.getScene().getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#Override public void run() {
button.fire();
}
}
);
In most cases it is recommended that you use KeyCombination.SHORTCUT_DOWN as the modifier specifier, as in the code above. A good explanation of this is in the KeyCombination documentation:
The shortcut modifier is used to represent the modifier key which is
used commonly in keyboard shortcuts on the host platform. This is for
example control on Windows and meta (command key) on Mac. By using
shortcut key modifier developers can create platform independent
shortcuts. So the "Shortcut+C" key combination is handled internally
as "Ctrl+C" on Windows and "Meta+C" on Mac.
If you wanted to specifically code to only handle a Ctrl+S key combination, they you could use:
new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)
Here is an executable example:
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SaveMe extends Application {
#Override public void start(final Stage stage) throws Exception {
final Label response = new Label();
final ImageView imageView = new ImageView(
new Image("http://icons.iconarchive.com/icons/gianni-polito/colobrush/128/software-emule-icon.png")
);
final Button button = new Button("Save Me", imageView);
button.setStyle("-fx-base: burlywood;");
button.setContentDisplay(ContentDisplay.TOP);
displayFlashMessageOnAction(button, response, "You have been saved!");
layoutScene(button, response, stage);
stage.show();
setSaveAccelerator(button);
}
// sets the save accelerator for a button to the Ctrl+S key combination.
private void setSaveAccelerator(final Button button) {
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#Override public void run() {
fireButton(button);
}
}
);
}
// fires a button from code, providing visual feedback that the button is firing.
private void fireButton(final Button button) {
button.arm();
PauseTransition pt = new PauseTransition(Duration.millis(300));
pt.setOnFinished(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
button.fire();
button.disarm();
}
});
pt.play();
}
// displays a temporary message in a label when a button is pressed,
// and gradually fades the label away after the message has been displayed.
private void displayFlashMessageOnAction(final Button button, final Label label, final String message) {
final FadeTransition ft = new FadeTransition(Duration.seconds(3), label);
ft.setInterpolator(Interpolator.EASE_BOTH);
ft.setFromValue(1);
ft.setToValue(0);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
label.setText(message);
label.setStyle("-fx-text-fill: forestgreen;");
ft.playFromStart();
}
});
}
private void layoutScene(final Button button, final Label response, final Stage stage) {
final VBox layout = new VBox(10);
layout.setPrefWidth(300);
layout.setAlignment(Pos.CENTER);
layout.getChildren().addAll(button, response);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
}
public static void main(String[] args) { launch(args); }
}
// icon license: (creative commons with attribution) http://creativecommons.org/licenses/by-nc-nd/3.0/
// icon artist attribution page: (eponas-deeway) http://eponas-deeway.deviantart.com/gallery/#/d1s7uih
Sample output:
Update Jan 2020, using the same accelerator for multiple controls
One caveat for accelerators in current and previous implementations (JavaFX 13 and prior), is that you cannot, out of the box, define the same accelerator key combination for use on multiple menus or controls within a single application.
For more information see:
JavaFX ContextMenu accelerator firing from wrong tab
and the related JDK-8088068 issue report.
The linked issue report includes a work-around you can use to allow you define and use the same accelerator within multiple places within an application (for example on two different menu items in different context menus).
Note that this only applies to trying to use the same accelerator in multiple places within an application, if you don't need try to do that, then you can ignore this information.