I am working on embedding a JavaFX login screen inside a Swing application.
I added a JFXPanel to a JPanel's content frame. Upon loading the application, all is well and smooth until I move my mouse inside the content pane (see link below for a video of this happening).
While my mouse is actively moving in the pane, the rendering becomes very laggy:
Turns laggy when mouse enters pane
Desired behaviour:
Use JavaFX designed UI in Swing through means of a JFXPanel without suffering a significant decrease in performance
The code below is a minimal, reproducible example of this problem.
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.*;
import javax.swing.*;
import java.io.IOException;
import java.net.URL;
public class TestJFXPanel {
public static void main(String[] args) throws IOException {
/*
Create pane with as background an animated image (GIF)
*/
final Pane pane = new Pane();
final URL url = new URL("https://i.stack.imgur.com/AvkzQ.gif");
pane.setBackground(new Background(new BackgroundImage(
new Image(url.openStream()),
BackgroundRepeat.NO_REPEAT,
BackgroundRepeat.NO_REPEAT,
BackgroundPosition.CENTER,
BackgroundSize.DEFAULT)));
/*
Set the pane as root of a Scene
*/
final JFXPanel panel = new JFXPanel();
panel.setScene(new Scene(pane, 500, 500));
/*
Invoke JFrame on AWT thread
*/
SwingUtilities.invokeLater(() -> {
final JFrame jFrame = new JFrame();
// Add JFXPanel to content pane of JFrame
jFrame.getContentPane().add(panel);
jFrame.pack();
jFrame.setVisible(true);
});
}
}
My thoughts:
I suspect this has something to do with the mouse motion being redirected to Swing from JavaFX causing a delay in communication between the FX and Swing threads. I think this is especially costly because I use a GIF as background image of the login screen.
I am not sure whether this is caused by a delay in communication of FX and Swing, if it is the case, would handling all my FX UI in Swing resolve this (assuming this is more feasible than porting the entire design to Swing)?
However, if someone is more familiar with integrating JavaFX with Swing and thinks my suspicions are wrong. Some advice on how to proceed are much appreciated!
Update:
When I override the processMouseMotionEvent method of the JFXPanel class, so that no MouseMotionEvents get consumed, the problem does not occur. I think this might be a compatibility issue between JavaFX and MacOS.
Related
I have an assignment to show JFileChooser as part of a JFrame. So showing it as a dialog box is out.
I'm doing the most basic approach to adding it as a component to a yet invisible frame, and then the setVisible() call freezes instead of showing the frame.
What irks me the most is that one time out of ten the frame appears with the FileChooser just fine. This makes me think this is a concurrency issue.
Here's the minimal source code that still has the issue.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
class ApplicationFrame extends JFrame {
JFileChooser fileChooser;
public ApplicationFrame(String frameName) {
super(frameName);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
fileChooser = new JFileChooser();
fileChooser.setControlButtonsAreShown(false);
panel.add(fileChooser, BorderLayout.CENTER);
getContentPane().add(panel);
}
}
public class lab7{
public static void main(String args[])
{
ApplicationFrame windowForApplication = new ApplicationFrame("lab7");
windowForApplication.setSize(600,600);
windowForApplication.setVisible(true);
}
}
If you put a println after the final setVisible, it doesn't get called.
If you comment out panel.add(), the frame displays just fine.
What else should I do to display the file chooser?
What irks me the most is that one time out of ten the frame appears with the FileChooser just fine.
All Swing component should be created on the Event Dispatch Thread. So the GUI creating code should be wrapped in a SwingUtilities.invokeLater(...).
Read the section from the Swing tutorial on Concurrency for more information and an example of how this is done.
Your code (as is) actually works for me without problem. I'm using JDK7 on Windows 7, so it could be a version/platform issue. Again make sure the code executes on the EDT.
Also, class names ("lab7") should start with an upper case character. Doesn't matter if this is a SSCCE or not, be consistent.
I'd like a Stage that is the same size as the screen which is fully transparent and receives mouse events anywhere. In the example below I get mouse events only when the mouse is over the circle. I see this issue on both Windows XP and Windows 7 using Java 8u11
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TransparentTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage ignored) throws Exception {
Stage stage = new Stage(StageStyle.TRANSPARENT);
stage.setTitle("Transparent app test");
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
stage.setX(0);
stage.setY(0);
stage.setWidth(screenBounds.getWidth());
stage.setHeight(screenBounds.getHeight());
Circle circle = new Circle(100);
circle.setFill(Color.RED);
Rectangle rectangle = new Rectangle(screenBounds.getWidth(),
screenBounds.getHeight());
rectangle.setFill(Color.TRANSPARENT);
Scene scene = new Scene(new StackPane(circle, rectangle));
scene.setFill(null);
stage.setScene(scene);
scene.setOnMouseMoved((e) -> {
System.out.println("Mouse over rectangle " + e);
});
stage.show();
}
}
Interestingly if I set the alpha part of the fill color to its absolute minimum then I get mouse events. However I'd prefer not to use this workaround and actually get to the bottom of the issue. My conclusion is somewhere in JavaFX or a Windows library there is some hit-detection code that filters mouse events based on the pixel value of the mouse event.
rectangle.setFill(Color.rgb(0, 0, 0, 1d / 255d)); // receives mouse events
rectangle.setFill(Color.rgb(0, 0, 0, 0)); // does not receive mouse events
Research
JavaFx Transparent window - yes please. Mouse transparent - no thanks describes a similar problem, however it does not address the issue of mouse events in completely transparent areas
Debugging - using a breakpoint in the setOnMouseMoved() I've examined the preceding stackframes to try to find the hit-detection code.
Used JNA to test different styles such as WS_EX_TRANSPARENT and WS_EX_LAYERED. Interestingly WS_EX_TRANSPARENT made the window fully mouse transparent - no mouse events over the painted pixels.
Tried putting the mouse listener on the rectangle/StackPane instead - no difference
MSDN article Layered Windows hints at this functionality being part of Windows rather than JavaFX. If this is true is there any workaround?
Hit testing of a layered window is based on the shape and transparency
of the window. This means that the areas of the window that are
color-keyed or whose alpha value is zero will let the mouse messages
through. If the layered window has the WS_EX_TRANSPARENT extended
window style, the shape of the layered window will be ignored and the
mouse events will be passed to the other windows underneath the
layered window.
In summary only known solution is to set the background to be "not quite" transparent to fool JavaFX into sending events.
rectangle.setFill(Color.rgb(0, 0, 0, 1d / 255d)); // receives mouse events
Our maven/Netbeans platform application uses a custom image on startup, by replacing
Nbm-branding > core.jar > org.netbeans.core.startup > splash.gif
I tried making it an animated .gif, but only the first frame is displayed.
How would one possibly go about implementing an animated splash screen, maybe by running some JavaFX window animations?
I've seen another other SO question, but it wasn't really answered - please notice I'm asking about how to integrate a custom splash screen with my Netbeans Platform application, and not how to actually build it.
Surprisingly enough, I found out how to plug in a custom splash screen based on this post about user authentication and authorization.
Basically, one needs to write another start-up class, instead of the platform's default:
import java.lang.reflect.Method;
public class CustomStartup {
private static final String NB_MAIN_CLASS = "org.netbeans.core.startup.Main";
public static void main(String[] args) throws Exception {
// do whatever you need here (e.g. show a custom login form)
System.out.println("Hello world! I am a custom startup class");
JWindow splash = initSplash();
// once you're done with that, hand control back to NetBeans
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
Class<?> mainClass = Class.forName(NB_MAIN_CLASS, true, classloader);
Object mainObject = mainClass.newInstance();
Method mainMethod = mainClass.getDeclaredMethod("main", new Class[]{String[].class});
mainMethod.invoke(mainObject, (Object) args);
splash.setVisible(false);
}
}
In that class, one can create a JavaFX stage, embed it into a JWindow, and show it:
public JWindow initSplash(){
JWindow window = new JWindow();
final JFXPanel fxPanel = new JFXPanel();
window.add(fxPanel);
window.setVisible(true);
window.setLocationRelativeTo(null);
Platform.runLater(new Runnable() {
#Override
public void run() {
Scene scene = new Scene(new CustomFxSplash(), 475, 300, true);
fxPanel.setScene(scene);
}
}
return window;
}
Other things to remember are:
Suppress the original NetBeans splash screen by running your app with the --nosplash parameter.
Call your custom initialization class by running your app with the -J-Dnetbeans.mainclass=com.package.splash.CustomStartup parameter
As the link suggests this custom class has to be on the platform's initialization classpath, meaning inside the platform/core folder.
The current version of the NetBeans class that is responsible for rendering the splash screen can be viewed online here: org.netbeans.core.startup.
The culprit code that prevents the gif from animating is this line (line 546)
graphics.drawImage(image, 0, 0, null);
In order for the gif to animate the ImageObserver will have to be specified instead of being set to null and then repaint must be called when imageUpdate() is called on the ImageObserver.
An example of displaying an animated gif can be viewed here: Relationship Between Animated Gif and Image Observer
So as far as I can see you will either have to change the above NetBeans platform code and rebuild it for your application or you will have to create your own splash screen from scratch to use instead of the NetBeans one.
Hope you find this useful!
This is my sample code. I am trying to embed a JInternalFrame without titlebar display into a JFrame.
import javax.swing.*;
import javax.swing.plaf.basic.BasicInternalFrameUI;
class A{
public void doThis(){
JFrame fr = new JFrame();
fr.setSize(300,300);
JInternalFrame f = new JInternalFrame();
f.setSize(200,200);
BasicInternalFrameUI ui = (BasicInternalFrameUI) f.getUI();
ui.setNorthPane(null);
f.setVisible(true);
fr.add(f);
fr.setVisible(true);
}
}
class MainA{
public static void main(String a[]){
A obj = new A();
obj.doThis();
}
}
The code works fine and displays a JInternalFrame within a JFrame without titlebar as per the requirement as shown below.
I still have this UI running and at the same time when I try to change my XP theme (via Properties>>Appearance>>Theme), the UI automatically repaints itself to show the JInternalFrame with a titlebar again as shown below.
I just can't understand this bizarre behavior. I have no clue if this is an issue with Java Swing or if it is something related to the OS. Please help me with this!
Why is the UI repainting upon theme change with an enabled titlebar when I explicitly code for the titleBar to be set as null?
PS: OS used is Windows XP and I am not sure if the same behavior is observed in Linux or other versions of Windows
'do' is a keyword in Java, so that code does not compile for me. This code does.
import javax.swing.*;
import javax.swing.plaf.basic.BasicInternalFrameUI;
class A{
public void doIt(){
JFrame fr = new JFrame();
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fr.setSize(300,300);
JInternalFrame f = new JInternalFrame();
f.setSize(200,200);
fr.add(f);
BasicInternalFrameUI ui = (BasicInternalFrameUI) f.getUI();
ui.setNorthPane(null);
f.setVisible(true);
fr.setVisible(true);
}
public static void main(String a[]){
A obj = new A();
obj.doIt();
}
}
Some notes/questions:
Swing GUIs should be constructed & altered on the EDT.
Why does the code add a JInternalFrame directly to anything other than a JDesktopPane?
There are slight issues with sizing of the JInternalFrame when changing themes. I suspect it has to do with the lack of validate()/pack() in the code. Since that was not the question, I could not be bothered investigating it further.
Results
I got a 'null result' here using Windows 7. The title bar of the JInternalFrame did not re-appear at any time when changing through (in order):
Forbidden Planet (a custom, simple theme)
Windows 7 (Aero)
Architecture (Aero)
Windows 7 Basic (Basic & High Contrast)
Windows Classic (Basic & High Contrast)
Forbidden Planet
New to NetBeans and just noticed that in the File >> Project Properties >> Application dialog there is a text field labeled Splash Screen that allows you to specify a path to an image that you would like displayed when your program is launching.
I want to customize the way my splash screen works (adding a progress bar, etc.) and would like to code it from the ground up but don't know where to start. What are the best practices for Java/Swing-based splash screens?
Thanks for any and all input!
The project properties -> Application -> Splash Screen allows you to add an image to an application. This property sets a value in the MANIFEST.MF called SplashScreen-Image: e.g. SplashScreen-Image: META-INF/GlassFish316x159.jpg This property will automatically cause the image to display as a splash screen. It does not work inside NetBeans, and must be run outside the IDE.
There is a tutorial Splash Screen Beginner Tutorial that details how to use it more detail. The tutorial was done for NetBeans 6.8, but will work on 7.2.1 which is the latest at the time of this post.
I'm not sure how NetBeans does it, but Splash Screens are supported by the JRE since version 6. See http://java.sun.com/developer/technicalArticles/J2SE/Desktop/javase6/splashscreen/
Splash screen is just a instance of java.awt.Window or undecorated javax.swing.JFrame.
To create window just say new Window(null), then set size and position (using tookit you can calculate where the screen center is) and then say window.setVisible(true)
Due to this is your own window you can do what you want: set layout, image, add process bar to the SOUTH etc.
You can also use JFrame: new JFrame().setUndecorated(true)`
There are a couple of ways to do this.
To do a simple splash screen (an image) you can specify this in the command line of you java application.
Here is a simple example
java -splash:<file name> <class name>
However, if you want a progress bar, you are going to have to do something a little more complicated, and write some code yourself. This is done in the following way.
Create a JWindow (or Window or undecorated JFrame) component with your splash screen elements
Set it to visible
Do the rest of your Swing GUI startup code
Set your JFrame to visible, then immediately follow with setting the JWindow to visible(false)
This should show the splash almost immediately, and then hide once the your application is fully loaded.
To see some splash screen code, take a look here. The implementation in the link only shows how to achieve what you can with the -splash command, but it will give you a good start to also include the progress bar that you requested.
I hope this helps you, it is a small example of how to create yourself a simple splash screen using a dummy Progress Bar:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class SplashScreen extends JWindow
{
private static JProgressBar progressBar = new JProgressBar();
private static SplashScreen execute;
private static int count;
private static Timer timer1;
public SplashScreen()
{
Container container = getContentPane();
container.setLayout(null);
JPanel panel = new JPanel();
panel.setBorder(new javax.swing.border.EtchedBorder());
panel.setBackground(new Color(255,255,255));
panel.setBounds(10,10,348,150);
panel.setLayout(null);
container.add(panel);
JLabel label = new JLabel("Hello World!");
label.setFont(new Font("Verdana",Font.BOLD,14));
label.setBounds(85,25,280,30);
panel.add(label);
progressBar.setMaximum(50);
progressBar.setBounds(55, 180, 250, 15);
container.add(progressBar);
loadProgressBar();
setSize(370,215);
setLocationRelativeTo(null);
setVisible(true);
}
public void loadProgressBar()
{
ActionListener al = new ActionListener()
{
public void actionPerformed(java.awt.event.ActionEvent evt)
{
count++;
progressBar.setValue(count);
if (count == 50){
timer1.stop();
execute.setVisible(false);
//load the rest of your application
}
}};
timer1 = new Timer(50, al);
timer1.start();
}
public static void main (String args[]){
execute = new SplashScreen();
}
}
Cheers!
Also consider to build your application on top of the NetBeans Platform (a Swing-based RCP). One of the many benefits: it comes with a customizable splash screen with progress bar.
Sample progress bar:
http://platform.netbeans.org/tutorials/nbm-paintapp.html#wrappingUp
Port a Swing application to the NetBeans Platform:
http://platform.netbeans.org/tutorials/60/nbm-porting-basic.html
Further links:
http://netbeans.org/features/platform/index.html
http://netbeans.org/features/platform/all-docs.html
If your application is build using NetBeans Platform, then here's a tutorial about splash screen customisation: http://wiki.netbeans.org/Splash_Screen_Beginner_Tutorial
There is a sample Javafx equivalent of Splash screen. However this splash screen is basically a java swing applet that is called from javafx to be displayed to the user and simulates more or less eclipse and netbeans splash screen using progress bar and titles for the loaded contents. This is the link.
You must be able to get the code and separate out the splash screen code written in java swings and use it for yourself.
This is a custom java swings splash screen. and hence to center the splash screen it uses the traditional
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension labelSize = l.getPreferredSize();
setLocation(screenSize.width / 2 - (labelSize.width / 2),
screenSize.height / 2 - (labelSize.height / 2));