前提・実現したいこと
SceneBuilderとJavaFXで、4つの四角から円が流れていくアニメーションを作成しようとしています。
マルチスレッド機構を必ず使い、制作したいです。
発生している問題・エラーメッセージ
スレッドを2つ以上同時にstart()させると以下のようなエラーメッセージが出てきます。
また、表示されるウインドウにはアニメーションをつけている円以外(各GUI要素・四角)は表示されます。円は一つも表示されません。
duplicate children added: から、複数の図形を描画する際のgetChildren().add(machine)で追加できないような問題が起きていると思うのですが、解決方法がわかりません。
Exception in thread "Thread-4" Exception in thread "Thread-3" java.lang.IllegalArgumentException: Children: duplicate children added: parent = BorderPane@61af5816 at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:561) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:205) at application.Material.run(Material.java:55) java.lang.IllegalArgumentException: Children: duplicate children added: parent = BorderPane@61af5816 at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:561) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:205) at application.Material.run(Material.java:55)
該当のソースコード
Mainクラス、Materialクラス(アニメーションを持つ円)、Machineクラス(通過点の四角)でできています。
MaterialクラスにThreadクラスを継承させ、run()のオーバーライドでアニメーションを設定しPlay()させています。
エラー発生部分はMainクラス内のmatR.start();matB.start();の辺りと考えられます。
Java
package application; import java.net.URL; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.stage.Stage; import javafx.scene.shape.*; import javafx.scene.paint.*; public class Main extends Application { // @FXML // private Circle circle1; public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) throws Exception { //Pane setup URL location = getClass().getResource( "Main.fxml" ); FXMLLoader fxmlLoader = new FXMLLoader( location ); Pane root = (Pane) fxmlLoader.load(); final int width = 600; final int height = 400; //path setup final int startRedX = 100; final int startRedY = 100; final int moveWidth = 350; final int moveHeight = 200; Path pathRB = new Path(); pathRB.getElements().add(new MoveTo(startRedX, startRedY)); pathRB.getElements().add(new LineTo(startRedX + moveWidth, startRedY)); Path pathBG = new Path(); pathBG.getElements().add(new MoveTo(startRedX + moveWidth, startRedY)); pathBG.getElements().add(new LineTo(startRedX + moveWidth, startRedY + moveHeight)); Path pathGY = new Path(); pathGY.getElements().add(new MoveTo(startRedX + moveWidth, startRedY + moveHeight)); pathGY.getElements().add(new LineTo(startRedX, startRedY + moveHeight)); Path pathYR = new Path(); pathYR.getElements().add(new MoveTo(startRedX, startRedY + moveHeight)); pathYR.getElements().add(new LineTo(startRedX, startRedY)); //machine setup Machine machineRed = new Machine(); Machine machineBlue = new Machine(); Machine machineGreen = new Machine(); Machine machineYellow = new Machine(); machineRed.set(root,Color.RED,80,70); machineBlue.set(root,Color.BLUE,440,70); machineGreen.set(root,Color.GREEN,440,280); machineYellow.set(root,Color.YELLOW,80,280); machineRed.view(); machineBlue.view(); machineGreen.view(); machineYellow.view(); //material setup //threads Material matR = new Material(); Material matB = new Material(); Material matG = new Material(); Material matY = new Material(); matR.set(root, pathRB); matR.colorSet(Color.RED); matB.set(root, pathBG); matB.colorSet(Color.BLUE); matG.set(root, pathGY); matG.colorSet(Color.GREEN); matY.set(root, pathYR); matY.colorSet(Color.YELLOW); // MaterialThread matRthread = new MaterialThread(matR); // MaterialThread matBthread = new MaterialThread(matB); matR.start();//エラー発生? matB.start(); // matG.start(); // matY.start(); // matR.move(); // matB.move(); // matRthread.start(); // matBthread.start(); primaryStage.setScene( new Scene( root , width , height ) ); primaryStage.show(); } }
Java
package application; import javafx.scene.shape.*; import javafx.animation.*; import javafx.util.Duration; import javafx.scene.layout.Pane; import javafx.animation.PathTransition; import javafx.scene.paint.*; public class Material extends Thread{ protected Circle circle = new Circle(10); protected Pane root; protected Path path; protected PathTransition animation; protected Color color; public void set(Pane root, Path path /*,double x, double y*/){ this.root = root; this.path = path; } public void colorSet(Color color){ this.color = color; circle.setFill(color); } public void move(){ animation = new PathTransition(); animation.setNode(circle); animation.setDuration( Duration.seconds( 4 ) ); animation.setPath( path ); animation.setInterpolator( Interpolator.LINEAR ); animation.setAutoReverse(false); animation.setCycleCount(0); root.getChildren().add(circle); animation.play(); } @Override public void run(){ this.animation = new PathTransition(); this.animation.setNode(this.circle); this.animation.setDuration( Duration.seconds( 4 ) ); this.animation.setPath( this.path ); this.animation.setInterpolator( Interpolator.LINEAR ); this.animation.setAutoReverse(false); this.animation.setCycleCount(0); this.root.getChildren().add(circle); this.animation.play(); } } // class MaterialThread extends Thread{ // private Material material; // public MaterialThread(Material material){ // this.material = material; // } // @Override // public void run(){ // this.material.move(); // } // }
Java
package application; import javafx.scene.shape.*; import javafx.scene.layout.Pane; import javafx.scene.paint.*; public class Machine{ protected int width = 80; protected int height = 60; protected int x,y; protected Rectangle machine = new Rectangle(x,y,width,height); protected Pane root; protected Color color; protected Thread t; public void set(Pane root, Color color, int x, int y){ this.x = x; this.y = y; this.machine = new Rectangle(x,y,width,height); this.root = root; this.color = color; } public void view(){ machine.setFill(color); root.getChildren().add(machine); } }
追記
FXML
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Slider?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.HBox?> <BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Main"> <top> <HBox prefHeight="15.0" prefWidth="600.0" BorderPane.alignment="CENTER"> <children> <Button mnemonicParsing="false" text="Start" /> <Button mnemonicParsing="false" text="Stop" /> </children> </HBox> </top> <bottom> <Slider BorderPane.alignment="CENTER" /> </bottom> <center> <AnchorPane prefHeight="331.0" prefWidth="600.0" BorderPane.alignment="CENTER" /> </center> </BorderPane>
試したこと
・マルチスレッド機構を用いずにMaterialクラスにrun()と同じ内容のメソッドを作成したうえでそのメソッドを同じように複数呼び出すと同時にアニメーション描画することが出来ました。
・調べている中でGroupを用いると直りそうな気がしましたが、まだよくわかっていなのできちんとしたアドバイスをいただきたいです。
追記
先程、Mainクラス内で複数のstart()の間にthread.sleep(..)を挟んで実行するとうまくいきました。これがなぜなのかもわからず、、皆様の知恵を貸してくだされば大変助かります。よろしくお願い致します。
補足情報(FW/ツールのバージョンなど)
JavaFX Scene Builder 17.0.0
解決後の追記
皆さんのご協力で何とか解決いたしました!
ベストアンサーですが、追記依頼のコメント内でご指摘頂いた皆さんにも宛てるつもりでshiketaさんの回答につけさせていただきます。
本当にありがとうございました!
まだ回答がついていません
会員登録して回答してみよう