1

I have the following setup

enter image description here

The three tic tac toe boards are gridpanes in JavaFx.

I am trying a very simple approach of adding an ImageView to the grid node in order to indicate if it is an x or o.

The code I am doing that with is.

private void handleNewGameAction(ActionEvent event) throws IOException {

        FXMLLoader fxmlloader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));

        GridPane pane = (GridPane)fxmlloader.getNamespace().get("topGrid");

        Image image = new Image("x.png");
        pane.add(new ImageView(image), 0, 0);

    }

The error I am getting is

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1774)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.control.MenuItem.fire(MenuItem.java:462)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$353(GlassViewEventHandler.java:432)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1769)
    ... 43 more
Caused by: java.lang.IllegalArgumentException: Invalid URL: Invalid URL or resource not found
    at javafx.scene.image.Image.validateUrl(Image.java:1118)
    at javafx.scene.image.Image.<init>(Image.java:620)
    at pkg3dtictactoe.FXMLDocumentController.handleNewGameAction(FXMLDocumentController.java:40)
    ... 53 more
Caused by: java.lang.IllegalArgumentException: Invalid URL or resource not found
    at javafx.scene.image.Image.validateUrl(Image.java:1110)
    ... 55 more

I am really at a loss and I have been looking at Stack overflow and seeing answers that look exactly like I have written, but I still get the error above.

DaveK
  • 544
  • 1
  • 6
  • 16
  • 1
    The ultimate cause of the error is `Caused by: java.lang.IllegalArgumentException: Invalid URL or resource not found`. This means the path you give to the `Image` is not correct. Double check to make sure it points to the resource you want (and that the resources exists). – Slaw Sep 05 '18 at 21:27
  • upon further debugging I also found that pane is null. Also the image exists but I may have it the wrong location. Currently it is in the same folder my .java files reside in the netbeans project. – DaveK Sep 05 '18 at 21:31
  • Okay! You fixed the first problem. I just needed to call fxmlloader.load(); and now pane is not null. Thanks for that, but I now need to figure out where the images need to go so they are loaded. – DaveK Sep 05 '18 at 21:41
  • Okay... image loads and pane is returned, but nothing is showing up. Is there a refresh method I need to call to redraw the pane? – DaveK Sep 05 '18 at 21:57
  • A "refresh" happens automatically, if the scene is modified. But you simply modify a scene you've just loaded and that is immediately dropped after the `handleNewGameAction` exits. The result of loading the fxml in this method is never shown on screen. – fabian Sep 05 '18 at 22:10
  • That would explain it. So I need to come up with a new method to "update the scene". – DaveK Sep 05 '18 at 23:32

1 Answers1

2

First, to answer the immediate problem, the ultimate cause of the error is given by the stack trace:

Caused by: java.lang.IllegalArgumentException: Invalid URL or resource not found
    at javafx.scene.image.Image.validateUrl(Image.java:1110)
    ... 55 more

This means that the URL ("x.png") you give to the constructor of Image is invalid. Either you have used the wrong path or the resource is not where you think it is. You mention in the comments that you have your image file in the same directory as your .java files. If you're using Maven or Gradle the default location for resources is src/main/resources (and src/main/java for *.java files). If this is a pure NetBeans JavaFX project (which may use Ant?) I don't know where the resources are supposed to go. See if there is a directory designated for resources and, if there is, move all resource files there.

There are a couple other things wrong with your code:

private void handleNewGameAction(ActionEvent event) throws IOException {

    FXMLLoader fxmlloader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));

    GridPane pane = (GridPane)fxmlloader.getNamespace().get("topGrid");

    Image image = new Image("x.png");
    pane.add(new ImageView(image), 0, 0);

}

The first issue deals with fxmlloader.getNamespace().get("topGrid"). You are calling this on a newly created FXMLLoader and you haven't called load() yet. The result of this call will always be null because the FXMLLoader hasn't actually instantiated anything yet.

The second, and in my opinion larger, issue deals with the fact you are creating a new GridPane every time this method is invoked. This new GridPane is never added to any Scene and therefore the GUI will never be updated. And to be clear, the GridPane you are retrieving in the method is not the same one that is already being displayed. And when the method exits the GridPane falls out of scope and is garbage collected.

Since you are creating a game board you should only ever need to load the board once (or maybe once per game). After that you use the loaded board to add images to. You should be using a controller class that's linked with the FXML file.

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;

public class Controller {

  @FXML 
  private GridPane topGrid;

  @FXML
  private void handleAction(ActionEvent event) {
    topGrid.add(new ImageView(/*image*/), 0, 0);
    event.consume();
  }

}

To link a controller to an FXML file you would use either the fx:controller attribute in the file or you would use FXMLLoader.setController(Object) before calling the instance load() method. To retrieve a controller you can use FXMLLoader.getController() after calling load.

To learn more about FXML in JavaFX, see Introduction to FXML.

Slaw
  • 37,820
  • 8
  • 53
  • 80