前提・実現したいこと
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
1package application; 2import java.net.URL; 3import javafx.application.Application; 4import javafx.fxml.FXMLLoader; 5import javafx.scene.Scene; 6import javafx.scene.layout.Pane; 7import javafx.stage.Stage; 8import javafx.scene.shape.*; 9import javafx.scene.paint.*; 10 11 12public class Main extends Application { 13 14 // @FXML 15 // private Circle circle1; 16 17 public static void main(String[] args) { 18 launch(args); 19 } 20 21 @Override 22 public void start(Stage primaryStage) throws Exception { 23 //Pane setup 24 URL location = getClass().getResource( "Main.fxml" ); 25 FXMLLoader fxmlLoader = new FXMLLoader( location ); 26 Pane root = (Pane) fxmlLoader.load(); 27 final int width = 600; 28 final int height = 400; 29 30 //path setup 31 final int startRedX = 100; 32 final int startRedY = 100; 33 final int moveWidth = 350; 34 final int moveHeight = 200; 35 36 Path pathRB = new Path(); 37 pathRB.getElements().add(new MoveTo(startRedX, startRedY)); 38 pathRB.getElements().add(new LineTo(startRedX + moveWidth, startRedY)); 39 Path pathBG = new Path(); 40 pathBG.getElements().add(new MoveTo(startRedX + moveWidth, startRedY)); 41 pathBG.getElements().add(new LineTo(startRedX + moveWidth, startRedY + moveHeight)); 42 Path pathGY = new Path(); 43 pathGY.getElements().add(new MoveTo(startRedX + moveWidth, startRedY + moveHeight)); 44 pathGY.getElements().add(new LineTo(startRedX, startRedY + moveHeight)); 45 Path pathYR = new Path(); 46 pathYR.getElements().add(new MoveTo(startRedX, startRedY + moveHeight)); 47 pathYR.getElements().add(new LineTo(startRedX, startRedY)); 48 49 //machine setup 50 Machine machineRed = new Machine(); 51 Machine machineBlue = new Machine(); 52 Machine machineGreen = new Machine(); 53 Machine machineYellow = new Machine(); 54 55 machineRed.set(root,Color.RED,80,70); 56 machineBlue.set(root,Color.BLUE,440,70); 57 machineGreen.set(root,Color.GREEN,440,280); 58 machineYellow.set(root,Color.YELLOW,80,280); 59 60 machineRed.view(); 61 machineBlue.view(); 62 machineGreen.view(); 63 machineYellow.view(); 64 65 //material setup 66 //threads 67 Material matR = new Material(); 68 Material matB = new Material(); 69 Material matG = new Material(); 70 Material matY = new Material(); 71 72 matR.set(root, pathRB); matR.colorSet(Color.RED); 73 matB.set(root, pathBG); matB.colorSet(Color.BLUE); 74 matG.set(root, pathGY); matG.colorSet(Color.GREEN); 75 matY.set(root, pathYR); matY.colorSet(Color.YELLOW); 76 77 // MaterialThread matRthread = new MaterialThread(matR); 78 // MaterialThread matBthread = new MaterialThread(matB); 79 80 81 matR.start();//エラー発生? 82 matB.start(); 83 // matG.start(); 84 // matY.start(); 85 // matR.move(); 86 // matB.move(); 87 88 // matRthread.start(); 89 // matBthread.start(); 90 91 primaryStage.setScene( new Scene( root , width , height ) ); 92 primaryStage.show(); 93 } 94} 95
Java
1package application; 2import javafx.scene.shape.*; 3import javafx.animation.*; 4import javafx.util.Duration; 5import javafx.scene.layout.Pane; 6import javafx.animation.PathTransition; 7import javafx.scene.paint.*; 8 9 10 11public class Material extends Thread{ 12 protected Circle circle = new Circle(10); 13 protected Pane root; 14 protected Path path; 15 protected PathTransition animation; 16 protected Color color; 17 18 public void set(Pane root, Path path /*,double x, double y*/){ 19 this.root = root; 20 this.path = path; 21 } 22 23 public void colorSet(Color color){ 24 this.color = color; 25 circle.setFill(color); 26 } 27 28 public void move(){ 29 animation = new PathTransition(); 30 animation.setNode(circle); 31 animation.setDuration( Duration.seconds( 4 ) ); 32 animation.setPath( path ); 33 animation.setInterpolator( Interpolator.LINEAR ); 34 animation.setAutoReverse(false); 35 animation.setCycleCount(0); 36 37 root.getChildren().add(circle); 38 animation.play(); 39 } 40 41 @Override 42 public void run(){ 43 this.animation = new PathTransition(); 44 this.animation.setNode(this.circle); 45 this.animation.setDuration( Duration.seconds( 4 ) ); 46 this.animation.setPath( this.path ); 47 this.animation.setInterpolator( Interpolator.LINEAR ); 48 this.animation.setAutoReverse(false); 49 this.animation.setCycleCount(0); 50 this.root.getChildren().add(circle); 51 this.animation.play(); 52 } 53} 54// class MaterialThread extends Thread{ 55// private Material material; 56// public MaterialThread(Material material){ 57// this.material = material; 58// } 59// @Override 60// public void run(){ 61// this.material.move(); 62// } 63// }
Java
1package application; 2import javafx.scene.shape.*; 3import javafx.scene.layout.Pane; 4import javafx.scene.paint.*; 5public class Machine{ 6 7 protected int width = 80; 8 protected int height = 60; 9 protected int x,y; 10 protected Rectangle machine = new Rectangle(x,y,width,height); 11 protected Pane root; 12 protected Color color; 13 14 protected Thread t; 15 16 public void set(Pane root, Color color, int x, int y){ 17 this.x = x; 18 this.y = y; 19 this.machine = new Rectangle(x,y,width,height); 20 this.root = root; 21 this.color = color; 22 } 23 24 public void view(){ 25 machine.setFill(color); 26 root.getChildren().add(machine); 27 } 28}
追記
FXML
1<?xml version="1.0" encoding="UTF-8"?> 2 3<?import javafx.scene.control.Button?> 4<?import javafx.scene.control.Slider?> 5<?import javafx.scene.layout.AnchorPane?> 6<?import javafx.scene.layout.BorderPane?> 7<?import javafx.scene.layout.HBox?> 8 9<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"> 10 <top> 11 <HBox prefHeight="15.0" prefWidth="600.0" BorderPane.alignment="CENTER"> 12 <children> 13 <Button mnemonicParsing="false" text="Start" /> 14 <Button mnemonicParsing="false" text="Stop" /> 15 </children> 16 </HBox> 17 </top> 18 <bottom> 19 <Slider BorderPane.alignment="CENTER" /> 20 </bottom> 21 <center> 22 <AnchorPane prefHeight="331.0" prefWidth="600.0" BorderPane.alignment="CENTER" /> 23 </center> 24</BorderPane> 25
試したこと
・マルチスレッド機構を用いずにMaterialクラスにrun()と同じ内容のメソッドを作成したうえでそのメソッドを同じように複数呼び出すと同時にアニメーション描画することが出来ました。
・調べている中でGroupを用いると直りそうな気がしましたが、まだよくわかっていなのできちんとしたアドバイスをいただきたいです。
追記
先程、Mainクラス内で複数のstart()の間にthread.sleep(..)を挟んで実行するとうまくいきました。これがなぜなのかもわからず、、皆様の知恵を貸してくだされば大変助かります。よろしくお願い致します。
補足情報(FW/ツールのバージョンなど)
JavaFX Scene Builder 17.0.0
解決後の追記
皆さんのご協力で何とか解決いたしました!
ベストアンサーですが、追記依頼のコメント内でご指摘頂いた皆さんにも宛てるつもりでshiketaさんの回答につけさせていただきます。
本当にありがとうございました!
回答3件
あなたの回答
tips
プレビュー