空指针异常:尝试使用Platform.runlater()从另一个类更新JavaFX。

huangapple 未分类评论43阅读模式
英文:

Null Pointer Exception when trying to update JavaFX from anouther class using Platform.runlater()

问题

以下是您提供的内容的翻译部分:


因此,我有三个类:GUIBuilder、ConwayLife 和 Updater。GUIBuilder 类构建一个 GUI,为用户提供一个网格,其维度由用户设置,并且能够选择每个网格元素以确定它是“存活”还是“死亡”。ConwayLife 类是一个算法,用于运行康威生命游戏,运行的代数由用户设置。Updater 类是我放置 Platform.runlater 代码的地方,尽管这是在尝试了几种不同的想法后产生的,以使其工作,所以可能是问题的一部分。

目标是使 Conway 类更新网格以反映当前代的状态

起始状态:

[![GUI 起始状态][2]][2]

结束状态:

[![GUI 结束状态][1]][1]

我一直在阅读 JavaFX 应用程序线程应该基本上不要动它,所以在尝试遵循那种逻辑的同时,当按下“开始模拟”按钮时,创建了一个新线程中的 Conway 类。(也忽略 X 和 Y 文本字段中的内容)

startSimulationButton.setOnAction(new EventHandler<ActionEvent>()
{
    public void handle(ActionEvent event)
    {
        if(validateTextBox(genTextField))
        {
            int[][] universe = convertTo2DArray(simulationPane);
            int generations = Integer.parseInt(genTextField.getText());
            Runnable simStart = new ConwayLife(universe, generations, window);
            new Thread(simStart).start();
        }
        else
        {
            instructionLabel.setText("Didn't put numbers in ya silly billy");
        }
    }
});

以下是将从 GUIbuiler 对象内部或从 updater 对象中调用以更新网格的类。

public void updateSimulationPane(int[][] universe, int xUpdate, int yUpdate) throws InterruptedException
{
    clearSimulationGrid(simulationPane);
    int[] flatUniverse = Stream.of(universe).flatMapToInt(IntStream::of).toArray();
    for(int counter = 0; counter < (xUpdate*yUpdate); counter++)
    {
        Rectangle rect = new Rectangle(0, 0, squareDimensions, squareDimensions);
        if(flatUniverse[counter] == 1)
        {
            rect.setFill(Color.DARKGREEN);
        }
        else
        {
            rect.setFill(Color.GREY);
        }
        simulationPane.add(rect, (counter) % xInput, (counter) / xInput);
    }
    
    double leftAnchor = (xWindow / 2) - ((squareDimensions * xInput) / 2);            
    gridAnchorPane.setTopAnchor(simulationPane, 100d);
    gridAnchorPane.setLeftAnchor(simulationPane, leftAnchor);
    //Thread.sleep(5000);
}

以下是 Conway 类和线程的开头部分,我对使用线程不太自信,所以在这里包含这一点,以防我在这里搞砸了什么。

private final int[][] universe;
private final int generations;
private final GUIBuilder window;

public ConwayLife(int[][] universePassed, int generationsPassed, GUIBuilder windowPassed)
{
    universe = universePassed;
    generations = generationsPassed;
    window = windowPassed;
}

public void run()
{
    try
    {
        getGeneration(universe, generations, window);
    }
    catch(Exception e)
    {
        System.out.println("Exception caught");
    }
}

public static void getGeneration(int[][] cells, int generations, GUIBuilder window) throws InterruptedException
{
    int [][] oldGeneration = cells; //new array to not alter original dataset
    int [][] newGeneration = new int[oldGeneration.length + 2][oldGeneration[0].length + 2]; //next generation will always have more columns and rows to simulate infinite universe
    int deadCells = 0; //counting the deadcells in the dataset to stop redundant generations
    int i = 0;
    int j = 0;
    int bounds = 2;
    boolean boundsChange = false;

下面是代数完成后将更新 GUI 的位置,然后循环应用算法到新的代数。

Updater changer = new Updater(window, trim(oldGeneration), oldGeneration.length, oldGeneration[0].length);
changer.run();
oldGeneration = newGeneration; //make the new generation the old generation to show passing of a generation
newGeneration = new int[oldGeneration.length + bounds][oldGeneration[0].length + bounds]; //em

最后是 Updater 类,其中包含回调到 GUIBuilder 对象的方法调用。

public class Updater implements Runnable
{
    private final GUIBuilder window;
    private final int[][] unvierse;
    private final int xAxis;
    private final int yAxis;
    
    public Updater(GUIBuilder passedWindow, int[][] passed2DArray,int passedXAxis,int passedYAxis)
    {
        this.window = passedWindow;
        this.unvierse = passed2DArray;
        this.xAxis = passedXAxis;
        this.yAxis = passedYAxis;
    }
    
    public void run() 
    {
        Platform.runLater(new Runnable() 
        {
            @Override
            public void run() 
            {
                try 
                {
                    window.updateSimulationPane(unvierse, xAxis, yAxis);
                } 
                catch (InterruptedException e) 
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
    }
}

这是您遇到的错误消息:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at moduleInfo/ConwayGUIOfLife.GUIBuilder.clearSimulationGrid(GUIBuilder.java:174)
at moduleInfo/ConwayGUIOfLife.GUIBuilder.updateSimulationPane(GUIBuilder.java:216)
at moduleInfo/ConwayGUIOfLife.Updater$1.run(Updater.java:29)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:389)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:835)

编辑 1:这是 clearSimulationPane 方法。很抱歉,只是一行代码,所以以为可能是其他原因造成的问题:

private void clearSimulationGrid(GridPane simulationPane)
{
    simulationPane

<details>
<summary>英文:</summary>

So, I have three classes: GUIBuilder, ConwayLife, and Updater. The GUI Builder builds a GUI that gives you a grid with dimensions that the user sets and the ability to select each grid element to pick if it is &quot;alive&quot; or &quot;dead&quot;. The ConwayLife class is an algorithm to run Conway&#39;s game of life for a user-set number of generations. The Updater class is where I put the Platform.runlater code, although this came about after trying several different ideas to get it to work so may be a part of the problem.

The objective is for the Conway class to update the grid to reflect the current generation&#39;s state

Start State:

[![GUI Start State][2]][2]

End State:

[![GUI End State][1]][1]

I&#39;ve been reading that the JavaFX application thread should be pretty much left alone and so in trying to follow that logic created the Conway class in a new thread when the start simulation button is pressed. (Also ignore what is in the X and Y Textfields)

    startSimulationButton.setOnAction(new EventHandler&lt;ActionEvent&gt;()
		{
			public void handle(ActionEvent event)
			{
				if(validateTextBox(genTextField))
				{
					int[][] universe = convertTo2DArray(simulationPane);
					int generations = Integer.parseInt(genTextField.getText());
					Runnable simStart = new ConwayLife(universe, generations, window);
					new Thread(simStart).start();
				}
				else
				{
					instructionLabel.setText(&quot;Didn&#39;t put numbers in ya silly billy&quot;);
				}
			}
		});

Here is the class that will be called to update the grid either from within the GUIbuiler object or from an updater object.

    public void updateSimulationPane(int[][] universe, int xUpdate, int yUpdate) throws InterruptedException
	{
		clearSimulationGrid(simulationPane);
		int[] flatUniverse = Stream.of(universe).flatMapToInt(IntStream::of).toArray();
		for(int counter = 0; counter &lt; (xUpdate*yUpdate); counter++)
		{
			Rectangle rect = new Rectangle(0, 0, squareDimensions, squareDimensions);
			if(flatUniverse[counter] == 1)
			{
				rect.setFill(Color.DARKGREEN);
			}
			else
			{
				rect.setFill(Color.GREY);
			}
			simulationPane.add(rect, (counter) % xInput, (counter) / xInput);
		}
		
		double leftAnchor = (xWindow / 2) - ((squareDimensions * xInput) / 2);			
		gridAnchorPane.setTopAnchor(simulationPane, 100d);
		gridAnchorPane.setLeftAnchor(simulationPane, leftAnchor);
		//Thread.sleep(5000);
	}

Here is the start of the Conway class and thread, I&#39;m not too confident using threads so I&#39;m including this in case I&#39;ve messed something up here.

    private final int[][] universe;
	private final int generations;
	private final GUIBuilder window;
	
	public ConwayLife(int[][] universePassed, int generationsPassed, GUIBuilder windowPassed)
	{
		universe = universePassed;
		generations = generationsPassed;
		window = windowPassed;
	}
	
	public void run()
	{
		try
		{
			getGeneration(universe, generations, window);
		}
		catch(Exception e)
		{
			System.out.println(&quot;Exception caught&quot;);
		}
	}
	
	public static void getGeneration(int[][] cells, int generations, GUIBuilder window) throws InterruptedException
	{
		int [][] oldGeneration = cells; //new array to not alter original dataset
		int [][] newGeneration = new int[oldGeneration.length + 2][oldGeneration[0].length + 2]; //next generation will always have more columns and rows to simulate infinite universe
		int deadCells = 0; //counting the deadcells in the dataset to stop redundant generations
		int i = 0;
		int j = 0;
		int bounds = 2;
		boolean boundsChange = false;

Next here is where is where the generation has finished, is going to update the GUI then loop to apply the algorithim to the new generation.

			Updater changer = new Updater(window, trim(oldGeneration), oldGeneration.length, oldGeneration[0].length);
			changer.run();
			oldGeneration = newGeneration; //make the new generation the old generation to show passing of a generation
			newGeneration = new int[oldGeneration.length + bounds][oldGeneration[0].length + bounds]; //em
		}

Finally here is the updater class where the method call back to the GUIBuilder object is located.

    public class Updater implements Runnable
    {
    	private final GUIBuilder window;
    	private final int[][] unvierse;
    	private final int xAxis;
    	private final int yAxis;
    	
    	public Updater(GUIBuilder passedWindow, int[][] passed2DArray,int passedXAxis,int passedYAxis)
    	{
    		this.window = passedWindow;
    		this.unvierse = passed2DArray;
    		this.xAxis = passedXAxis;
    		this.yAxis = passedYAxis;
    	}
    	
    	public void run() 
    	{
    		Platform.runLater(new Runnable() 
    		{
    			@Override
    			public void run() 
    			{
    				try 
    				{
    					window.updateSimulationPane(unvierse, xAxis, yAxis);
    				} 
    				catch (InterruptedException e) 
    				{
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    		});
    	}
    
    }

This is the error message I get: 

    Exception in thread &quot;JavaFX Application Thread&quot; java.lang.NullPointerException
	at moduleInfo/ConwayGUIOfLife.GUIBuilder.clearSimulationGrid(GUIBuilder.java:174)
	at moduleInfo/ConwayGUIOfLife.GUIBuilder.updateSimulationPane(GUIBuilder.java:216)
	at moduleInfo/ConwayGUIOfLife.Updater$1.run(Updater.java:29)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:389)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
	at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
	at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
	at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
	at java.base/java.lang.Thread.run(Thread.java:835)

EDIT 1: Here is the clearSimulationPane method. Sorry, it was just a single line so thought it must be something else causing it:

    private void clearSimulationGrid(GridPane simulationPane)
	{
		simulationPane.getChildren().clear();
	}

Edit 2: Okay, I see the problem where the GUIBuilder window object has been getting passed between classes by value rather than by reference as I&#39;d been assuming I&#39;m pretty sure but I still don&#39;t know how to fix it at this point.
  [1]: https://i.stack.imgur.com/v9KkO.png
  [2]: https://i.stack.imgur.com/ah5NS.png

</details>


huangapple
  • 本文由 发表于 2020年4月10日 02:12:39
  • 转载请务必保留本文链接:https://java.coder-hub.com/61127570.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定