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();
}
Related
I'm making an application with JavaFX and Scene Builder.
I have two controllers:Controller and FontController
I have Main class that launch my program and open Stage with first fontroller (Controller)
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("/card/card.fxml"));
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("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);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
There is a label and a button in Controller. When I click on the button a method is called and new window with second controller appears(FontController):
#FXML private Button btnFont;
#FXML private Label category1
#FXML
void changeFont(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new
FXMLLoader(getClass().getResource("font.fxml"));
Parent rootFont = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("Select Font");
stage.setScene(new Scene(rootFont));
stage.show();
} catch (Exception e) {
System.out.println("can't load new window");
}
}
There is the button "OK" and label in FontCOntroller:
#FXML private Label fontLabel;
#FXML private Button btnFontOk;
Please tell me, what should I do to send and apply text from label in FontController when I click on the burtton "OK" to label in Controller?
SOLUTION FOUND:
I created class "Context" in my project directory to make all controllers communicate each other. You can add as many controllers as you want there.
Here it looks like:
package card;
public class Context {
private final static Context instance = new Context();
public static Context getInstance() {
return instance;
}
private Controller controller;
public void setController(Controller controller) {
this.controller=controller;
}
public Controller getController() {
return controller;
}
private FontController fontController;
public void setFontController(FontController fontController) {
this.fontController=fontController;
}
public FontController getFontController() {
return fontController;
}
}
Controller:
I created getters and setters (ALT + Insert in IDEA) for Label that I wanna change
public Label getCategory1() {
return category1;
}
public void setCategory1(Label category1) {
this.category1 = category1;
}
To get FontController variables and methods through Context class I placed line of code
//getting FontController through Context Class
FontController fontCont = Context.getInstance().getFontController();
I registered Controller in Context class through my initialize method (my class implements Initializable)
#FXML
public void initialize(URL location, ResourceBundle resources) {
//register Controller in Context Class
Context.getInstance().setController(this);
}
FontController:
to get Controller variables and methods I placed this code:
//getting Controller variables and methods through Context class
Controller cont = Context.getInstance().getController();
I also registered FontController in Context class through initialize method:
#Override
public void initialize(URL location, ResourceBundle resources) {
//register FontController in Context Class
Context.getInstance().setFontController(this);
}
Method that send text and text color from label in this FontController to label in Controller when I click on button:
#FXML
void applyFont(ActionEvent event) {
cont.getCategory1().setText(fontLabel.getText());
cont.getCategory1().setTextFill(fontLabel.getTextFill());
}
*By creating Context class you can make controllers communicate each other and create as many controllers as you want there. Controllers see variables and methods of each other
I am trying to work around this bug in the jdk: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8088624
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Button btn = new Button("Click");
btn.setTooltip(new Tooltip("Blubb"));
Scene scene = new Scene(new BorderPane(btn), 320, 240);
primaryStage.setScene(scene);
primaryStage.show();
Stage secondStage = new Stage();
secondStage.setScene(new Scene(new BorderPane(new Button("Click")), 320, 240));
//secondStage.initOwner(primaryStage);
secondStage.show();
}
}
If the button on the primary stage is hovered, it will come in front of the second stage. I found that calling initOwner() on a Stage will eliminate this behavior.
Now my problem is following: I have multiple "popups" that have a common owner (the primary stage). Hovering over controls on the primary stage doesn't cause any unexpected behavior after the initOwner() workaround. If you however hover over controls in a popup while another popup was in focus, the hovered popup will steal focus.
Is there a way I can work around this bug for not only the primary stage but also the popups?
UPDATE: turns out my workaround has undesired side-effects. Javadocs for Stage state following:
A stage will always be on top of its parent window.
So additionally, what would be a workaround that makes the popup not "always on top" and minimizable?
There is a way to get around it by overlaying StackPanes. Create your Scene with a StackPane so that you can add another StackPane when the stage has lost its focus. The overlayed pane will prevent Tooltips or anything else happening on mouse-over while the pane is not in focus. You may also minimize any of your stages and they won't be always-on-top.
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Button button_1 = new Button("Button #1");
button_1.setTooltip(new Tooltip("Blubb #1"));
StackPane primary = new StackPane(new BorderPane(button_1));
primaryStage.setScene(new Scene(primary, 320, 240));
addStageFocusListener(primaryStage, primary);
primaryStage.show();
Button button_2 = new Button("Button #2");
button_2.setTooltip(new Tooltip("Blubb #2"));
StackPane second = new StackPane(new BorderPane(button_2));
Stage secondStage = new Stage();
addStageFocusListener(secondStage, second);
secondStage.setScene(new Scene(second, 320, 240));
secondStage.show();
}
public void addStageFocusListener(Stage stage, StackPane stackPane) {
stage.focusedProperty().addListener(new ChangeListener<Boolean>(){
public final StackPane preventTooltip = new StackPane();
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(stage.isFocused()) {
if(stackPane.getChildren().contains(preventTooltip)) {
stackPane.getChildren().remove(preventTooltip);
}
} else {
stackPane.getChildren().add(preventTooltip);
}
}
});
}
}
You can try this:
public static final disableMouseEventOnUnfocus(final Stage stage)
{
if (stage == null
|| stage.getScene() == null
|| stage.getScene().getRoot() == null)
return;
else
{
stage.getScene().getRoot().mouseTransparentProperty().bind(stage.focusedProperty().not());
}
}
I didn't try it though, but if it works, this should be a good alternative. There is no need to restructure your layout, and you can leave all your layout in FXML, without specifying fx:id for the tooltips.
I've come up with this alternative solution, as I've found it easier in my case to subclass Tooltip and apply a fix there. I just overload the show() method to only show if the owning window is focused. It's working like a charm for me...
public class FixedTooltip extends Tooltip {
public FixedTooltip(String string) {
super(string);
}
#Override
protected void show() {
Window owner = getOwnerWindow();
if (owner.isFocused())
super.show();
}
}
You could try to unset the tooltip whenever the node's window loses focus. Such as below:
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
public static void installTooltip(Node n, Tooltip tp)
{
Window w = n.getScene().getWindow();
w.focusedProperty().addListener((val, before, after) -> {
if (after)
Tooltip.install(n, tp);
else
Tooltip.uninstall(n, tp);
});
if (w.isFocused())
Tooltip.install(n, tp);
else
Tooltip.uninstall(n, tp);
}
#Override
public void start(Stage primaryStage) throws Exception {
Tooltip tp = new Tooltip("Blubb");
Button btn = new Button("Click");
Scene scene = new Scene(new BorderPane(btn), 320, 240);
primaryStage.setScene(scene);
//primaryStage.show();
Stage secondStage = new Stage();
secondStage.setScene(new Scene(new BorderPane(new Button("Click")), 320, 240));
//secondStage.initOwner(primaryStage);
secondStage.show();
primaryStage.show();
installTooltip(btn, tp);
}
}
Of course, you would have to call installTooltip after the node is added to the component.
I've got problem with may e(fx)clipse application. I want to show a splash screen upon application startup. I successfully created class implementing StartupProgressTrackerService, and got my stateReached method invoked. However I've got problems with javafx itself. I want to create Stage with StageStyle.UNDECORATED. However when i invoke stage.show() method stage isn't rendered immediately and appears just after main window is created. It works fine e.g. with StageStyle.UTILITY. It also renders correctly when i use showAndWait() method, but it stops my app from loading until i close the stage.
Here is my code:
public class MyStartupProgressTrackerService implements StartupProgressTrackerService {
private Stage stage;
public MyStartupProgressTrackerService() {
}
#Override
public OSGiRV osgiApplicationLaunched(IApplicationContext applicationContext) {
applicationContext.applicationRunning();
return StartupProgressTrackerService.OSGiRV.CONTINUE;
}
#Override
public void stateReached(ProgressState state) {
if (DefaultProgressState.JAVAFX_INITIALIZED.equals(state)) {
stage = new Stage(StageStyle.UNDECORATED);
stage.initModality(Modality.WINDOW_MODAL);
stage.setAlwaysOnTop(true);
ImageView view = null;
try {
view = new ImageView(SPLASH_IMAGE);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BorderPane bp = new BorderPane();
bp.getChildren().add(view);
Scene scene = new Scene(bp, 400, 300);
stage.setScene(scene);
stage.show();
}
}
}
I found an ugly solution, but, at least, it works. I noticed that method stage.showAndWait() as a side effect finishes building all controls which haven't been rendered yet. So the trick is to initialize splash screen, and then create dummy stage, showAndWait() it and close() immediately. I know that this solution is far from ideal, so i would appreciate it if someone could show me alternate way to make it work :)
My code:
public void showSplash() {
splashScreen = createSplashScreen();
Stage stage2 = new Stage(StageStyle.TRANSPARENT);
splashScreen.show();
Platform.runLater(new Runnable() {
#Override
public void run() {
stage2.close();
}
});
stage2.showAndWait();
}
private Stage createSplashScreen() {
Stage stage = new Stage(StageStyle.UNDECORATED);
stage.setAlwaysOnTop(true);
VBox vbox = new VBox();
vbox.getChildren().add(new ImageView(splashImage));
Scene scene = new Scene(vbox, 400, 300);
stage.setScene(scene);
return stage;
}
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);
}
}
So I've been trying to do this for atleast 4 hours now and with no succes.
The case: I need to open a new FXML Window after pressing a button on my Controller class.
I'm using JavaFX for this.
We are working in packages like this.
lobby.gui, login.gui etc...
It doesn't give an error message but the Scene will not start.
This is the LoginFXController where the button is pressed.
public class LoginFXController implements Initializable
{
DatabaseMediator mediator;
//INLOGGUI FXML
#FXML TextField tf_inlogusername;
#FXML PasswordField pf_inlogpassword;
#FXML Button btn_inlog;
#FXML Button btn_register;
#Override
public void initialize(URL url, ResourceBundle rb)
{
mediator = new DatabaseMediator();
}
public void loginPersoon(Event event)
{
String gebruikersnaam = tf_inlogusername.getText();
String wachtwoord = pf_inlogpassword.getText();
Persoon p = new Persoon(gebruikersnaam, wachtwoord);
Boolean check = mediator.controleerPersoonsGegevens(gebruikersnaam, wachtwoord);
if(check == false)
{
showDialog("Error", "Gegevens komen al voor in het Systeem!");
}
else
{
showDialog("Succes", "Welkom: " + gebruikersnaam);
try
{
FXMLLoader loader = new FXMLLoader(getClass().getResource("lobby.gui/LobbyGUI.fxml"));
LobbyGUI controller = new LobbyGUI();
loader.setController(controller);
loader.setRoot(controller);
Parent root = (Parent)loader.load();
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.show();
}
catch(IOException e)
{
showDialog("Error", e.getMessage());
}
}
}
And I'm trying to fire of this new class
public class LobbyGUI extends Application
{
#Override
public void start(Stage stage) throws IOException
{
Parent root = FXMLLoader.load(getClass().getResource("LobbyGUI.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}