【问题标题】:Unable to get Scene from MenuItem in JavaFX无法从 JavaFX 中的 MenuItem 获取场景
【发布时间】:2013-12-15 12:17:19
【问题描述】:

我正在尝试根据 menuItem 单击更改 javafx 阶段中的场景。这是我的 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>

这是我的 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.
    }
}

这是我的 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);
    }
}

当我点击 Accounts Menu 下的 menuItem Manage 时,出现以下异常:

    "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

那么如何从菜单项单击事件处理程序中获取包含阶段/场景?

编辑 线

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

在 controller.java 中是第 29 行,这会产生问题。

【问题讨论】:

  • java.lang.NullPointerException at sample.Controller.onTweetsMenuActionPerformed(Controller.java:30) => Controller.java 类的第 30 行在哪里?
  • @assylias Stage stage=(Stage) node.getScene().getWindow();
  • 我的猜测是node 不可见,因此没有窗口。您可以尝试调试代码以检查此时的变量。
  • @assylias 我已经修改了堆栈跟踪和控制器。这应该会给你更好的主意。
  • @assylias 再次看到编辑中的第 29 行...

标签: java event-handling javafx fxml


【解决方案1】:

您真正的错误显示在堆栈跟踪的倒数第二行:

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

此错误指的是控制器中的以下行:

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

查看JavaFX API Docs,MenuItem 和Menu 都不是Node 的子类。 http://docs.oracle.com/javafx/2/api/javafx/scene/control/MenuItem.html http://docs.oracle.com/javafx/2/api/javafx/scene/control/Menu.html

我建议将源作为对象获取,然后在继续之前检查其类型。另外,我在使用 getSource() 方法时遇到了问题; getTarget() 方法对我来说效果更好。不管怎样,你仍然需要一种方法来进入舞台。

为此,您可能希望在 FXML 中使用 fx:id 标签,而不是 id 标签。这将允许您将 FXML 元素直接注入到您的控制器中。例如,您可以通过将 MenuBar 元素注入控制器来从 MenuBar(它是 Node 的子类)中获取舞台。

在 FXML 中:

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

在控制器中:

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();
    }
    ...
}

您可能需要在此处进行一些调整,但希望对您有所帮助。

【讨论】:

  • 听起来不错。 +10 给你。但试一试后确认为答案。
【解决方案2】:

这是一种获取SceneWindow 的方法,该方法基于单击的菜单项而不是注入的FXML 元素,或者如果您确实使用FXML 创建了它,则无需引用它。换句话说,通过使用Event 的目标。

在我的问题中,我有一个带有下拉菜单的MenuButton(我发现是一个ContextMenu,因为我用FXML创建了我的菜单,所以我不知道)包含MenuItems,我想打开一个FileChooser,当点击“保存”MenuItem 时,它需要Window 作为参数。

通常我会沿着获取事件目标的路线走,然后是父级,然后是下一个父级等,最后是场景,然后是窗口。由于MenuMenuItem 不是Nodes,因此没有Parents 在这种情况下,我做了以下操作:

FileChooser fileChooser = new FileChooser();

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

fileChooser.showSaveDialog(window);

或者,将批量转换为单行参数:

FileChooser fileChooser = new FileChooser();

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

只需根据您自己的场景图(即在子菜单等情况下您需要通过多少父母)以及您拥有Window 后想要做的事情进行调整,但一次你到ContextMenuMenuItems的弹出列表),你可以得到Scene,然后从那里得到Window




顺便说一句,这是我用来创建 MenuButton 的 FXML,因此我没有意识到我必须通过调用 @ 来获取 ContextMenu 987654343@ 无需反复试验:

<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>

【讨论】:

    【解决方案3】:

    在 JavaFX 15.0.1 中,您可以使用 getOwnerWindow 来获取舞台,并从舞台中获取场景。

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 请为您的答案添加解释
      • 问题是关于菜单项,而不是菜单栏。
      猜你喜欢
      • 2022-01-09
      • 1970-01-01
      • 2014-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-13
      相关资源
      最近更新 更多