How to Create Splash Screen with Transparent Background in Javafx

How to create Splash screen with transparent background in JavaFX

Try this JavaFX splash sample created for the Stackoverflow question: Designing a splash screen (java). And a follow up sample which also provides application initialization progress feedback.

JavaFX does offer the Preloader interface for smooth transfer from splash to application, but the above samples don't make use of it.

The splash samples above also don't do the transparent effect, but this dialog sample shows you how to do that and you can combine it with the previous splash samples to get the effect you want.

The transparent effect is created by:

  1. stage.initStyle(StageStyle.TRANSPARENT).
  2. scene.setFill(Color.TRANSPARENT).
  3. Ensuring your root node is not an opaque square rectangle.

Which is all demonstrated in Sergey's sample.

Related question:

  • How to use javaFX Preloader with stand-alone application in Eclipse?

Update Apr 2016 based on additional questions

the preloader image isnt in the foreground. I have tried stage.toFront(), but doesnt help.

A new API was created in Java 8u20 stage.setAlwaysOnTop(true). I updated the linked sample to use this on the initial splash screen, which helps aid in a smoother transition to the main screen.

For Java8+

For modena.css (the default JavaFX look and feel definition in Java 8), a slight shaded background was introduced for all controls (and also to panes if a control is loaded).

You can remove this by specifying that the default background is transparent. This can be done by adding the following line to your application's CSS file:

.root { -fx-background-color: transparent; }

If you wish, you can use CSS style classes and rules or a setStyle call (as demonstrated in Sergey's answer) to ensure that the setting only applies to the root of your splash screen rather than all of your app screens.

See related:

  • how to make transparent scene and stage in javafx?

Showing a PNG file with transparent background using javafx

I'm in a bit of a rush, but below is a quick example to show you how it can be done by setting the StageStyle to Transparent and the scene fill to the color "transparent".

@Override
public void start(Stage aStage) throws Exception {
Pane root = new Pane();
ImageView img = new ImageView();
img.setImage(new Image(getClass().getResource("pathToYourPngLocatedInYourResourcesFolder.png").toExternalForm()));
root.getChildren().add(img);
Scene scene = new Scene(root, 500, 500);
scene.setFill(Color.TRANSPARENT);
aStage.initStyle(StageStyle.TRANSPARENT);
aStage.setScene(scene);
aStage.show();
}

Let me know how it works out :)

Cannot set stage to transparent in Preloader

JavaFX 15 fixed this. Seems it was a bug.

How do I add a Splash screen to a main program in javafx?

I made a few changes to your code. I changed the Splash variable to Scene. I then added methods to get the Scene and the SequentialTransition. I set the SequentialTransition onFinished method in the main.
.

Main

import java.io.IOException;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
*
* @author blj0011
*/
public class JavaFXApplication266 extends Application
{

@Override
public void start(Stage stage) throws Exception
{
Splash splash = new Splash();
splash.show();
stage.setScene(splash.getSplashScene());
splash.getSequentialTransition().setOnFinished(e -> {
Timeline timeline = new Timeline();
KeyFrame key = new KeyFrame(Duration.millis(800),
new KeyValue(splash.getSplashScene().getRoot().opacityProperty(), 0));
timeline.getKeyFrames().add(key);
timeline.setOnFinished((event) -> {
try {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
//
Scene scene = new Scene(root);

stage.setScene(scene);
}
catch (IOException ex) {
ex.printStackTrace();
}
});
timeline.play();
});
//
stage.show();
}

/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}

}

Splash Class

import javafx.animation.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Font;
import javafx.util.Duration;

/**
* This is my own splash screen, that I made myself.
*
*/
public class Splash
{

static Scene splash;
static Rectangle rect = new Rectangle();
final private Pane pane;
final private SequentialTransition seqT;

public Splash()
{
pane = new Pane();
pane.setStyle("-fx-background-color:black");

splash = new Scene(pane);
seqT = new SequentialTransition();
}

public void show()
{
/*
* Part 1:
* This is the rolling square animation.
* This animation looks cool for a loading screen,
* so I made this. Only the lines of code for fading
* from Stack Overflow.
*/
//rectangle insertion
int scale = 30;
int dur = 800;
rect = new Rectangle(100 - 2 * scale, 20, scale, scale);
rect.setFill(Color.AQUAMARINE);

//actual animations
//initialising the sequentialTranslation
//umm, ignore this, just some configs that work magic
int[] rotins = {scale, 2 * scale, 3 * scale, 4 * scale, 5 * scale, -6 * scale, -5 * scale, -4 * scale, -3 * scale, -2 * scale};
int x, y;
for (int i : rotins) {
//rotating the square
RotateTransition rt = new RotateTransition(Duration.millis(dur), rect);
rt.setByAngle(i / Math.abs(i) * 90);
rt.setCycleCount(1);
//moving the square horizontally
TranslateTransition pt = new TranslateTransition(Duration.millis(dur), rect);
x = (int) (rect.getX() + Math.abs(i));
y = (int) (rect.getX() + Math.abs(i) + (Math.abs(i) / i) * scale);
pt.setFromX(x);
pt.setToX(y);
//parallelly execute them and you get a rolling square
ParallelTransition pat = new ParallelTransition();
pat.getChildren().addAll(pt, rt);
pat.setCycleCount(1);
seqT.getChildren().add(pat);
}
//playing the animation
seqT.play();
//lambda code sourced from StackOverflow, fades away stage
seqT.setNode(rect);
//The text part
Label label = new Label("Flight");
label.setFont(new Font("Verdana", 40));
label.setStyle("-fx-text-fill:white");
label.setLayoutX(140);
label.setLayoutY(70);
Label lab = new Label("Launching...");
lab.setFont(new Font("Times New Roman", 10));
lab.setStyle("-fx-text-fill:white");
lab.setLayoutX(170);
lab.setLayoutY(180);
//A complimentary image

Image image = new Image("https://s3.amazonaws.com/media.eremedia.com/uploads/2012/08/24111405/stackoverflow-logo-700x467.png");
ImageView iv = new ImageView(image);
iv.setFitWidth(32);
iv.setFitHeight(32);
iv.setX(174);
iv.setY(130);
//now adding everything to position, opening the stage, start the animation
pane.getChildren().addAll(rect, label, lab, iv);

seqT.play();
}

public Scene getSplashScene()
{
return splash;
}

public SequentialTransition getSequentialTransition()
{
return seqT;
}
}

After Splash Controller

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

/**
*
* @author blj0011
*/
public class FXMLDocumentController implements Initializable
{

@FXML
private Label label;

@FXML
private void handleButtonAction(ActionEvent event)
{
System.out.println("You clicked me!");
label.setText("Hello World!");
}

@Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
}

}

After Splash FXML

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication266.FXMLDocumentController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>

How to show a splash-screen, load datas in the background, and hide the splash-screen after that?

The most significant issue is you're blocking the Event Dispatching Thread, meaning that it can't display/update anything while it's blocked. The same problem applies to JavaFX.

You should, also, never update either from anything other then they respective event queues.

Now, there are any number of ways you might be able to go about this, but SwingWorker is probably the simplest for the time been.

Sample Image

I apologise, this is the entire exposure to JavaFX I've had...

public class TestJavaFXLoader extends JApplet {

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

public TestJavaFXLoader() throws HeadlessException {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
Loader loader = new Loader();
loader.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state") && evt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
System.out.println("Load main app here :D");
}
}
});
loader.load();
}
});
}

public class Loader extends SwingWorker<Object, String> {

private JWindow splash;
private JLabel subMessage;

public Loader() {
}

protected void loadSplashScreen() {
try {
splash = new JWindow();
JLabel content = new JLabel(new ImageIcon(ImageIO.read(...))));
content.setLayout(new GridBagLayout());
splash.setContentPane(content);

GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;

subMessage = createLabel("");

splash.add(createLabel("Loading, please wait"), gbc);
splash.add(subMessage, gbc);
splash.pack();
splash.setLocationRelativeTo(null);
splash.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}

protected JLabel createLabel(String msg) {
JLabel message = new JLabel("Loading, please wait");
message.setForeground(Color.CYAN);
Font font = message.getFont();
message.setFont(font.deriveFont(Font.BOLD, 24));
return message;
}

public void load() {
if (!EventQueue.isDispatchThread()) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
loadSplashScreen();
}
});
} catch (Exception exp) {
exp.printStackTrace();
}
} else {
loadSplashScreen();
}
execute();
}

@Override
protected void done() {
splash.dispose();
}

@Override
protected void process(List<String> chunks) {
subMessage.setText(chunks.get(chunks.size() - 1));
}

@Override
protected Object doInBackground() throws Exception {

publish("Preparing to load application");
try {
Thread.sleep(2500);
} catch (InterruptedException interruptedException) {
}
publish("Loading JavaFX...");

runAndWait(new Runnable() {
@Override
public void run() {
new JFXPanel();
}
});

try {
Thread.sleep(2500);
} catch (InterruptedException interruptedException) {
}
return null;
}

public void runAndWait(final Runnable run)
throws InterruptedException, ExecutionException {
if (Platform.isFxApplicationThread()) {
try {
run.run();
} catch (Exception e) {
throw new ExecutionException(e);
}
} else {
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
lock.lock();
try {
Platform.runLater(new Runnable() {
@Override
public void run() {
lock.lock();
try {
run.run();
} catch (Throwable e) {
e.printStackTrace();
} finally {
try {
condition.signal();
} finally {
lock.unlock();
}
}
}
});
condition.await();
// if (throwableWrapper.t != null) {
// throw new ExecutionException(throwableWrapper.t);
// }
} finally {
lock.unlock();
}
}
}
}
}

I found the runAndWait code here

I can't get a transparent stage in JavaFX

The grey background is your "mainStage" since you are showing splash and main stages at the same time. At the beginning while showing the splash stage you can just init (not show) the main stage and show it later when the animation finishes:

public class ModifiedMenu extends Application
{

private Pane splashLayout;
private Stage mainStage;
private ImageView splash;

// Creating a static root to pass to ScreenControl
private static BorderPane root = new BorderPane();

public void start(Stage splashStage) throws IOException {

final Dimension2D screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.splash = new ImageView(new Image(getClass().getResource("/splash.png").toString()));

splashStage.initStyle(StageStyle.TRANSPARENT);
showSplash(splashStage, screenSize);

// Constructing our scene using the static root
root.setCenter(new ScrollPane());
Scene scene = new Scene(root, screenSize.getWidth(), screenSize.getHeight());
initMainStage(scene);

if (splashStage.isShowing()) {
splashStage.toFront();
FadeTransition fadeSplash = new FadeTransition(Duration.seconds(1.5), splashLayout);
fadeSplash.setDelay(Duration.seconds(3.5));
fadeSplash.setFromValue(1.0);
fadeSplash.setToValue(0.0);
fadeSplash.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
splashStage.hide();
mainStage.show();
}
});
fadeSplash.play();
}
}

private void initMainStage(Scene scene) {
mainStage = new Stage(StageStyle.DECORATED);
mainStage.setTitle("book-depot");
mainStage.getIcons().add(new Image(getClass().getResourceAsStream("/icon.png")));
mainStage.setScene(scene);
}

private void showSplash(Stage splashStage, Dimension2D screenSize) {
splashLayout = new StackPane();
splashLayout.setStyle("-fx-background-color: transparent;");
splashLayout.getChildren().add(splash);
Scene splashScene = new Scene(splashLayout, 690, 590);
splashScene.setFill(Color.TRANSPARENT);
splashStage.setScene(splashScene);
splashStage.show();
}

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

}

JAVAFX SplashScreen with an animated gif

Thanks to the @jewelsea comment, I ended up modifying the main class to do tasks in a background thread and then, when everything is ready, I show the stage for the main application.

[...]

try {
final Task<Integer> task = new Task<Integer>() {
@Override
protected Integer call() throws Exception {
Thread.sleep(5000);
Platform.runLater(new Runnable() {
public void run() {
showStage();
}
});
return 0;
}
};

Thread thread = new Thread(task);
thread.start();
} catch (final Exception e) {
e.printStackTrace();
}
}

public void showStage() {
stage.show();
notifyPreloader(new StateChangeNotification(StateChangeNotification.Type.BEFORE_START));
}

The GIF is animated and the splash screen will load after all background tasks are done.



Related Topics



Leave a reply



Submit