简体   繁体   English

无法从 JavaFX 中的 MenuItem 获取场景

[英]Unable to get Scene from MenuItem in JavaFX

I am trying to change the scene in a javafx stage based on menuItem click.我正在尝试根据 menuItem 单击更改 javafx 阶段中的场景。 Here is my sample.fxml:这是我的 sample.fxml:

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

<?import java.lang.*?>
<?import java.net.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.GridPane?>

<AnchorPane prefHeight="-1.0" prefWidth="560.0" styleClass="background" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.Controller">
  <children>
    <MenuBar layoutY="0.0" maxWidth="1.7976931348623157E308" prefWidth="300.0" useSystemMenuBar="false" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="2.0">
      <menus>
        <Menu id="manageAccountsMenu" mnemonicParsing="false" onAction="#onManageAccountsMenuActionPerformed" text="Accounts" fx:id="manageAccountsMenu">
          <items>
            <MenuItem mnemonicParsing="false" onAction="#onTweetsMenuActionPerformed" text="Manage" fx:id="manageAccountsSubmenuItem" />
          </items>
        </Menu>
        <Menu mnemonicParsing="false" onAction="#onTweetsMenuActionPerformed" text="Tweets" fx:id="tweetsMenuItem" />
        <Menu mnemonicParsing="false" text="Retweets" />
      </menus>
    </MenuBar>
    <VBox id="VBox" alignment="CENTER" layoutY="24.0" spacing="5.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
      <children>
        <ScrollPane id="ScrollPane" fitToHeight="true" fitToWidth="true" prefViewportHeight="400.0" prefViewportWidth="300.0">
          <content>
            <TableView prefHeight="-1.0" prefWidth="-1.0" tableMenuButtonVisible="true">
              <columns>
                <TableColumn editable="false" prefWidth="75.0" text="SNO" />
                <TableColumn prefWidth="200.0" text="Account" />
                <TableColumn prefWidth="200.0" text="Status" />
                <TableColumn prefWidth="75.0" text="Actions" />
              </columns>
            </TableView>
          </content>
        </ScrollPane>
        <Button mnemonicParsing="false" text="Add Account" textAlignment="CENTER">
          <graphic>
            <ImageView fitHeight="150.0" fitWidth="200.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true">
              <image>
                <Image url="@addAccount.png" />
              </image>
            </ImageView>
          </graphic>
        </Button>
      </children>
    </VBox>
  </children>
  <stylesheets>
    <URL value="@darkTheme.css" />
  </stylesheets>
</AnchorPane>

Here is my Controller.java:这是我的 Controller.java:

package sample;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.MenuItem;
import javafx.stage.Stage;

import java.net.URL;
import java.util.ResourceBundle;

public class Controller implements Initializable {
    @FXML
    protected void onManageAccountsMenuActionPerformed(ActionEvent event) {
        System.out.println("Manage Accbtnclick");
//        Node node=(Node) event.getSource();
//        Stage stage=(Stage) node.getScene().getWindow();
//
//        Scene scene = new Scene(root);
//        stage.setScene(scene);
//        stage.show();
    }
    @FXML
    protected void onTweetsMenuActionPerformed(ActionEvent event) {
        System.out.println("Manage Accbtnclick");
    Node node= (Node)event.getSource();
    Stage stage=(Stage) node.getScene().getWindow();
    Scene scene = Main.screens.get("tweet");
    stage.setScene(scene);
    stage.show();
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        //To change body of implemented methods use File | Settings | File Templates.
    }
}

Here is my Main.java:这是我的 Main.java:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;
import java.util.HashMap;

public class Main extends Application {

    public static HashMap<String,Scene> screens=new HashMap<String,Scene>();

    @Override
    public void start(Stage stage) {
        try {
            Parent accountScreen= FXMLLoader.load(getClass().getResource("sample.fxml"));
            Parent tweetScreen=FXMLLoader.load(getClass().getResource("tweetform.fxml"));
            //Parent retweetScreen=FXMLLoader.load(getClass().getResource("retweetform.fxml"));
            screens.put("account",new Scene(accountScreen));
            screens.put("tweet",new Scene(tweetScreen));
            //screens.put("retweet",new Scene(retweetScreen));
            stage.setTitle("Manage Accounts");
            stage.setScene(screens.get("account"));
            stage.show();
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

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

When I click on the menuItem Manage under Accounts Menu, I get the following exception:当我单击“帐户”菜单下的“管理”菜单项时,出现以下异常:

    "C:\Program Files\Java\jdk1.7.0_17\bin\java" -Didea.launcher.port=7541 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA 12.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_17\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\rt.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_17\jre\lib\ext\zipfs.jar;C:\Users\rahulserver\IdeaProjects\DrawingText\out\production\DrawingText;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 12.1.4\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain sample.Main
Manage Accbtnclick
Manage Accbtnclick
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1440)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:28)
    at javafx.event.Event.fireEvent(Event.java:171)
    at javafx.scene.control.MenuItem.fire(MenuItem.java:456)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1188)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1139)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1137)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
    at javafx.event.Event.fireEvent(Event.java:171)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3328)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3168)
    at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3123)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1563)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2265)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:250)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:173)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:292)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:528)
    at com.sun.glass.ui.View.notifyMouse(View.java:922)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
    at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
    at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:55)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:269)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1435)
    ... 40 more
Caused by: java.lang.ClassCastException: javafx.scene.control.MenuItem cannot be cast to javafx.scene.Node
    at sample.Controller.onTweetsMenuActionPerformed(Controller.java:29)
    ... 50 more

So how do I get the containing stage/scene from the menu Item clicked event handler?那么如何从菜单项单击事件处理程序中获取包含的舞台/场景?

EDIT The line编辑线

Node node= (Node)event.getSource();

in controller.java is line number 29 which is giving problems.在 controller.java 中是第 29 行,它给出了问题。

Your real error is shown in the second to last line of the stack trace:您真正的错误显示在堆栈跟踪的倒数第二行:

Caused by: java.lang.ClassCastException: javafx.scene.control.MenuItem cannot be cast to javafx.scene.Node
    at sample.Controller.onTweetsMenuActionPerformed(Controller.java:29)

This error is referring to the following line from your controller:此错误指的是控制器中的以下行:

Node node= (Node)event.getSource();

Looking at the JavaFX API Docs, neither MenuItem nor Menu are subclasses of Node.查看 JavaFX API 文档,MenuItem 和 Menu 都不是 Node.js 的子类。 http://docs.oracle.com/javafx/2/api/javafx/scene/control/MenuItem.html http://docs.oracle.com/javafx/2/api/javafx/scene/control/Menu.html http://docs.oracle.com/javafx/2/api/javafx/scene/control/MenuItem.html http://docs.oracle.com/javafx/2/api/javafx/scene/control/Menu.html

I would suggest grabbing the source as an Object, then checking its type before continuing.我建议将源作为对象获取,然后在继续之前检查其类型。 Also, I ran into problems using the getSource() method;另外,我在使用 getSource() 方法时遇到了问题; the getTarget() method worked better for me. getTarget() 方法对我来说效果更好。 Either way, you still need a way to get the to the Stage.无论哪种方式,您仍然需要一种方法来获得舞台。

To do this, you might want to use the fx:id tag in your FXML instead of the id tag.为此,您可能需要在 FXML 中使用fx:id标签而不是id标签。 This will allow you to inject the FXML elements directly into your controller.这将允许您将 FXML 元素直接注入控制器。 For example, you could grab the Stage from the MenuBar (which is a subclass of Node) by injecting the MenuBar element into your controller.例如,您可以通过将 MenuBar 元素注入控制器来从 MenuBar(它是 Node 的子类)中获取舞台。

In the FXML:在 FXML 中:

<MenuBar fx:id="myMenuBar" layoutY="0.0" maxWidth="1.7976931348623157E308" prefWidth="300.0" useSystemMenuBar="false" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="2.0">

In the controller:在控制器中:

public class Controller implements Initializable {
    @FXML MenuBar myMenuBar;
    ...
    @FXML
    protected void onTweetsMenuActionPerformed(ActionEvent event) {
        System.out.println("Manage Accbtnclick");
        Stage stage = (Stage) myMenuBar.getScene().getWindow();
        Scene scene = Main.screens.get("tweet");
        stage.setScene(scene);
        stage.show();
    }
    ...
}

You may need to do a bit of tweaking here, but hopefully it helps.您可能需要在这里做一些调整,但希望它有所帮助。

Here is a way to get the Scene and Window based on a menu item being clicked without it being an injected FXML element, or without referencing it if you did create it with FXML.这是一种基于单击的菜单项获取SceneWindow方法,而不是注入的 FXML 元素,或者如果您确实使用 FXML 创建了它,则无需引用它。 In other words by using the Event 's target.换句话说,通过使用Event的目标。

In my problem I had a MenuButton with a dropdown menu (a ContextMenu as I found out, which I didn't know as I had created my menu in FXML) containing MenuItems and I wanted to open a FileChooser , which needs the Window as an argument, when the "Save" MenuItem was clicked.在我的问题中,我有一个带有下拉菜单的MenuButton (我发现是一个ContextMenu ,我不知道,因为我在 FXML 中创建了我的菜单)包含MenuItems ,我想打开一个FileChooser ,它需要Window作为参数,当单击“保存” MenuItem时。

Normally I would have gone down the route of getting the event's target, then the Parent, then the next Parent etc. and finally the Scene and then Window.通常我会沿着获取事件目标的路线走下去,然后是父级,然后是下一个父级等等,最后是场景,然后是窗口。 As Menu and MenuItem are not Node s and therefore do not have Parent s In this case however I did the following:由于MenuMenuItem不是Node ,因此没有Parent在这种情况下,我做了以下事情:

FileChooser fileChooser = new FileChooser();

MenuItem menuItem = (MenuItem)event.getTarget();
ContextMenu cm = menuItem.getParentPopup();
Scene scene = cm.getScene();
Window window = scene.getWindow();

fileChooser.showSaveDialog(window);

Or, converting the bulk into a one line argument:或者,将批量转换为一行参数:

FileChooser fileChooser = new FileChooser();

fileChooser.showSaveDialog(((MenuItem)event.getTarget()).getParentPopup().getScene().getWindow());

Just adapt this as necessary based on your own scene-graph (ie how many parents you need to get through in cases of sub-menus etc) and what you are wanting to do once you have the Window , but once you get to the ContextMenu (the popup list of MenuItems ), you can get the Scene and then from that get the Window .只需根据您自己的场景图(即在子菜单等情况下您需要通过多少个父母)以及您在拥有Window想要做什么,但是一旦您到达ContextMenuMenuItems的弹出列表),您可以获取Scene ,然后从中获取Window




As an aside, here is the FXML I used to create my MenuButton , and hence why I didn't realise I had to get the ContextMenu through a call to getParentPopup() without some trial and error:顺便说一句,这是我用来创建MenuButton的 FXML,因此我没有意识到我必须通过调用getParentPopup()来获取ContextMenu而不经过一些试验和错误:

<MenuButton fx:id="menu" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" prefHeight="34.0" prefWidth="35.0" style="-fx-background-color: #6600ff; -fx-border-width: 0; -fx-mark-color: white; -fx-padding: 5;" textAlignment="CENTER" textFill="WHITE" underline="true">
    <items>
        <MenuItem fx:id="newSearch" mnemonicParsing="false" text="New Search" onAction="#clearSearch" />
        <SeparatorMenuItem  />
        <MenuItem fx:id="saveSearch" mnemonicParsing="false" text="Save Search" onAction="#saveSearch" />
        <MenuItem fx:id="loadSearch" mnemonicParsing="false" text="Load Search" />
        <SeparatorMenuItem  />
        <MenuItem fx:id="saveResults" mnemonicParsing="false" text="Save Results" />
        <MenuItem fx:id="loadResults" mnemonicParsing="false" text="Load Results" />
        <SeparatorMenuItem />
        <MenuItem fx:id="exportResults" mnemonicParsing="false" text="Export Results" />
    </items>
    <font>
        <Font name="Arial" size="12.0" />
    </font>
</MenuButton>

In JavaFX 15.0.1, you can use getOwnerWindow to get the Stage and get the Scene from the stage.在 JavaFX 15.0.1 中,您可以使用 getOwnerWindow 获取舞台并从舞台中获取场景。

Stage owner = (Stage)menuItem.getParentPopup().getOwnerWindow();
Scene scene = owner.getScene();

Stage stage = (Stage) ((Node) myMenuBar).getScene().getWindow();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM