I have a Java project that plays a .flv media file through JavaFX Media Player, and it's working fine. Recently, I've been wanting to experiment by adding GUI components to this Project (JPanel, JLabel). However, I've failed in all my attempts and after doing some research turns out it's not as simple as i first thought.. I've tried borderPane.setTop(JLabel) but I get a "Cannot convert Jlabel to Node" error.. I feel that I'm missing something
If anyone has any idea why this isnt working for me, I would greatly appreciate any form of explanation or examples.. :)
Here is the code if it might be of use to you!
#Override
public void start(Stage stage){
String path = "Data/Video/Clip.flv";
Media media = new Media(new File(path).toURI().toString());
MediaPlayer mediaPlayer = new MediaPlayer(media);
MediaView mediaView = new MediaView(mediaPlayer);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(mediaView);
//borderPane.add(logoPanel); <<<<<<< Error
Scene scene = new Scene(borderPane, 1024, 800);
scene.setFill(javafx.scene.paint.Color.BLACK);
stage.setTitle("Media Player");
stage.setScene(scene);
stage.show();
mediaPlayer.setAutoPlay(true);
BorderPane is a JavaFX Node whereas JPanel is a Java Swing Component.
You cannot add a JPanel to a BorderPane, what you are looking for instead is the JavaFX equivalent of the JPanel which is the Pane class.
If you are developing using JavaFX it is easier to just use JavaFX components. If you must use Swing components then you can use the SwingNode class.
What you basically want to achieve: add some Swing components to an JavaFX application.
You have to use SwingNode class to "wrap" a JComponent.
SwingNode Class
JavaFX 8 introduces the SwingNode class, which is located in the
javafx.embed.swing package. This class enables you to embed Swing
content in a JavaFX application. To specify the content of the
SwingNode object, call the setContent method, which accepts an
instance of the javax.swing.JComponent class.
You can check this tutorial how embed Swing component into JavaFX application.
Small example to put a Swing Button into the center of a BorderPane:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
SwingNode swingNode = new SwingNode();
JButton jButton = new JButton("I am a Swing button");
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Message from Swing");
}
});
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
swingNode.setContent(jButton);
}
});
root.setCenter(swingNode);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
By the way, BorderPane has no method "add" (as in your commented line):
You can use setCenter, setTop, setBottom, setLeft and setRight to add Nodes into it (as you used to fill the center).
If your goal was not to embed Swing into JavaFX, just to use JavaFX controls, you can check this article what controls JavaFX has by default.
Related
I would like to change the cursor to use the drag and drop "move" and "move and copy" cursors in my application on a panel when I perform certain actions. The area is using swing inside of a swing node so a swing (Apply to panel) or JavaFx (Applying to swing node) solution would work.
I did find swing cursors DragSource.DefaultMoveDrop and DragSource.DefaultCopyDrop but when I apply them my cursor doesn't change. (And I know I am applying the cursor correctly as applying other cursors work fine)
EDIT: Note that when not nested in a SwingNode and in a JFrame instead the cursor does change to what I want. I have amended the code example to emulate the environment I am in where it doesn't work:
public class Temp extends Application {
public static void main(String[] args) {
Application.launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
SwingNode swingNode = new SwingNode();
SwingUtilities.invokeLater(() -> {
JPanel panel = new JPanel();
panel.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); //Change this
swingNode.setContent(panel);
});
Scene scene = new Scene(new BorderPane(swingNode));
primaryStage.setScene(scene);
primaryStage.show();
}
}
Ok turns out it works DragSource.DefaultMoveDrop and DragSource.DefaultCopyDrop actually work in a normal swing application, just not on JPanel inside a SwingNode which the environment I am working in uses. (I can't change this)
Therefore if you run into this issue and you are NOT mixing the frameworks this forms an answer.
I was assigned in school to build a small JAVAFX program with buttons that travels through different Scenes (layouts).
My program is a log in window, and details window. But I have to create the details window in a different class than Main class. How do I make the button call the 'details window'from Main when it's a class?
(See createAccountBtn.setOnAction)
Main Class
Detail window Class
So Here's the deal: I created an interface where I put this method:
void displayLayout(VBox layout);
And then It triggers this method in Main:
public void displayLayout(VBox layout){
Scene scene = new Scene(layout, 200, 200);
window.setScene(scene);
I also have in Main this:
Button createAccountBtn = new Button("Create new account");
createAccountBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
displayLayout(new DetailScreen(listener));
}
});
layout = new VBox();
layout.getChildren().addAll(accountNumberTF, loginBtn, createAccountBtn);
layout.setAlignment(Pos.CENTER);
displayLayout(layout);
window.show();
Which is the layout options and the button. the setOnAction button points to a new Class that inherits VBox and acts as a layout:
private ChangeScreenListener listener;
public DetailScreen(ChangeScreenListener listener) {
this.listener = listener;
Button exitBtn = new Button("EXIT");
TextField input = new TextField();
VBox layout = new VBox();
layout.getChildren().addAll(input, exitBtn);
However after I click the button, the window becomes empty and does not change layout.
Ideas?
public static variables can pass variables between classes. This is the easiest way i could think off to pull off the results you desire.
In the main class define the following variables:
public static Stage stage;
public static Scene sceneMain;
Assuming you have already set a scene and using a Stage in your main constructor write the following code after setting your Stage and Scene.
stage = primaryStage;
sceneMain = scene;
When you have done this you can easily call on to the stage in another class by and going back to your main scene like this: (this is what you can put under the event of your buttonclick())
Main.stage.setScene(sceneMain);
Or you can set another scene by simply putting the scene you want in the .setscene();
I created a swing GUI, and a graph using JavaFX. I would like to open the graph by clicking a button on the swing GUI. My Code is below. Thank you for your help.
This is what I have to open the FX GUI from the swing GUI.
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Test Graph = new Test();
Graph.GrpahScreen();
}
});
FX class
public class Test extends Application {
#Override
public void start(Stage stage) {
// TODO
}
public static void main(String args []) {
launch(args);
}
}
The Application class represents a JavaFX Application. Since you are writing a Swing application (with JavaFX content embedded in it), it makes no sense to create an Application subclass.
Additionally, it is highly recommended not to use both JavaFX Stages and Swing JFrames in the same application. You should place the JavaFX content in a JFXPanel and display the JFXPanel in a JFrame. You need to be careful to obey the threading restrictions of both toolkits: as usual, Swing components must be created on the AWT event dispatch thread, and JavaFX components must be created on the JavaFX application Thread. This is all covered in detail in the JFXPanel documentation, but in essence:
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JFrame frame = new JFrame();
JFXPanel jfxPanel = new JFXPanel();
Platform.runLater(() -> {
Parent root = ... ; // create JavaFX content, can be in a separate class
Scene scene = new Scene(root);
jfxPanel.setScene(scene);
});
frame.add(jfxPanel);
frame.setSize(...);
frame.setVisible(true);
}
});
My title is badly worded because my problem is very hard to describe, so I drew an image for it:
I have an ImageView object which represents a pile of cards (not poker cards but just used them as an example). When this image view is clicked, I need a window to popup that features a ScrollPane and shows them all the card objects that are in the linked list. When the user clicks anywhere outside of the window (and later on, any right mouse button click) the scrollpane window needs to close.
Ways that I have already tried:
Scene with APPLICATION_MODAL. Then did Scene.showAndWait(). I didn't like this method because it made another application on the user's taskbar. It also felt clunky and just bad.
Changed my root pane to a StackPane, then added this Scrollpane to the stackpane when the user clicked on the deck. This for some reason was really slow and seemed really obtrusive. It was also annoying because my alternate class needed to have access to the root pane (since when it closes, it needs to go to the root StackPane and call .remove() on itself).
Are there any other better ways to accomplish this? My application is going to have many of these piles and so this framework needs to work very well.
I would still propose to open a new Stage with some restrictions to solve your issues with this approach.
You can use the initOwner property of the Stage to have another Stage as owner, therefore no other icon will appear on the taskbar.
You can use the initStyle property with TRANSPARENT or UNDECORATED StageStlye, this will ensure that only the content is visible.
And in the end you can use the focusedProperty to check whether the Stage lost focus to close it.
Example
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
Button b = new Button("Open deck");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Stage popupStage = new Stage();
popupStage.initOwner(primaryStage);
popupStage.initStyle(StageStyle.UNDECORATED);
Scene sc = new Scene(new ScrollPane(), 300, 300);
popupStage.setScene(sc);
popupStage.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
Boolean newValue) {
if(!newValue)
popupStage.close();
}
});
popupStage.show();
}
});
root.setCenter(b);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
It is also possible of course to not open it in a new Stage, but draw a ScrollPane inside the current Stage which overlaps the content of the Stage using for example an AnchorPane or Group as root, but the first solution has the advantage that you are not bound to the dimensions of main Stage (the popup can have any size that you want).
You can achieve this with a low level system hook that catches the mouse events.
http://kra.lc/blog/2016/02/java-global-system-hook/ or https://github.com/kwhat/jnativehook/releases
I hope that is what you needed, otherwise i got your question wrong.
I have a JPanel component containing a JFXPanel with a browser that embedds a YouTube video. I'm using the videos embed URL from YouTube (i.e. https://www.youtube.com/embed/W-J2OYN9fF8?autoplay=true&controls=0).
I can add the JPanel (VideoPlayer) to a surrounding component without any problem. However - when I remove the VideoPlayer I would also like to stop the YouTube-player. As of now - it keeps playing in the background (with annoying sound). I'm guessing I have to get inside the JFX thread somehow... So, if someone could please help me with code to put in the stopTrailer() method - I'd be very grateful!
Here's my current code. For those who are looking for a simple way to embed a JFX YouTube player in a normal JPanel - this works great - appart from the limitations above...
public class VideoPlayer extends JPanel {
private Stage stage;
private WebView browser;
private JFXPanel jfxPanel;
private WebEngine webEngine;
private String videoUrl;
public VideoPlayer(String url){
this.videoUrl = url;
jfxPanel = new JFXPanel();
createScene();
setLayout(new BorderLayout());
setPreferredSize(new Dimension(800, 560));
add(jfxPanel, BorderLayout.CENTER);
}
private void createScene() {
PlatformImpl.startup(new Runnable() {
#Override
public void run() {
stage = new Stage();
stage.setTitle("Video");
stage.setResizable(true);
Group root = new Group();
Scene scene = new Scene(root,80,20);
stage.setScene(scene);
//Set up the embedded browser:
browser = new WebView();
webEngine = browser.getEngine();
webEngine.load(videoUrl);
ObservableList<Node> children = root.getChildren();
children.add(browser);
jfxPanel.setScene(scene);
}
});
}
public void stopTrailer() {
}
}
The answer to this was actually really simple. I put the following in the stopTrailer method.
public void stopTrailer() {
PlatformImpl.startup(new Runnable() {
#Override
public void run() {
remove(jfxPanel);
webEngine.load(null);
}
});
}
What it actually does is to reload the browser webEngine to the "address" null. And removes the panel. It seems a bit weird to me cause I would interpret this code as the start of a second thread - without access to the first one. Perhaps someone could explain just how this works?