質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

6653閲覧

JavaFXで値を計算後,Canvasへの再描画処理について

3_pyon

総合スコア12

JavaFX

JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2017/10/12 09:29

###前提・実現したいこと
Scene BuilderとJavaFXを使ってシミュレーターを作ろうとしています.

ここで実装したい処理は

・時間tに依存する関数を用いて計算した座標を用いて,AnchorPane上に配置したCanvasに物体を描画(初期配置).
・時間tを規定の量だけ増やし,再計算し,その座標でまた物体をCanvasに再描画.

なのですが,再描画処理をどのように行えばよいのか分からず,詰まってしまいました.
以下に初期配置のみを行い,単純化したソースコードを示します.物体はここでは正方形としています.

###該当のソースコード

Java

1//FXTest2.fxml 2 3<?xml version="1.0" encoding="UTF-8"?> 4 5<?import javafx.scene.canvas.Canvas?> 6<?import javafx.scene.layout.AnchorPane?> 7 8<AnchorPane xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sim.FXTest2Controller"> 9 <children> 10 <AnchorPane layoutX="-98.0" layoutY="-90.0" prefHeight="500.0" prefWidth="500.0"> 11 <children> 12 <Canvas fx:id="Canvas" height="348.0" layoutX="115.0" layoutY="109.0" width="270.0" AnchorPane.leftAnchor="115.0" AnchorPane.topAnchor="109.0" /> 13 </children> 14 </AnchorPane> 15 </children> 16</AnchorPane> 17

Java

1//FXTest2Controller.java 2 3package sim; 4 5 6import javafx.fxml.FXML; 7import javafx.fxml.Initializable; 8import java.net.URL; 9import java.util.ResourceBundle; 10import javafx.scene.paint.Color; 11import javafx.scene.canvas.GraphicsContext; 12import javafx.scene.canvas.Canvas; 13 14public class FXTest2Controller implements Initializable { 15 @FXML Canvas Canvas; 16 GraphicsContext gc; 17 double x=0; 18 double y=0; 19 20 public void initialize(URL location, ResourceBundle resources) { 21 gc = Canvas.getGraphicsContext2D(); 22 draw(x , y); 23 } 24 25 public void draw(double x , double y) { 26 27 //ここで初期配置を行っているものとして見てください. 28 gc.setFill(Color.RED); 29 gc.fillRect(x, y, 10, 10); 30 31 } 32} 33 34

Java

1//Main.java 2 3package sim; 4 5import javafx.application.Application; 6import javafx.stage.Stage; 7import javafx.scene.Scene; 8import javafx.scene.canvas.Canvas; 9import javafx.scene.canvas.GraphicsContext; 10import javafx.scene.layout.AnchorPane; 11import javafx.scene.paint.Color; 12import javafx.fxml.FXMLLoader; 13import javafx.fxml.FXML; 14 15public class Main extends Application { 16 17 @Override 18 public void start(Stage primaryStage) { 19 20 try { 21 AnchorPane root = (AnchorPane)FXMLLoader.load(getClass().getResource("FXTest2.fxml")); 22 Scene scene = new Scene(root,400,400); 23 scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); 24 primaryStage.setScene(scene); 25 primaryStage.show(); 26 } catch(Exception e) { 27 e.printStackTrace(); 28 } 29 } 30 31 public static void main(String[] args) { 32 launch(args); 33 } 34} 35 36

###試したこと
Mainメソッドの始めに

public class Main extends Application { @FXML Canvas Canvas; GraphicsContext gc; double x=0; double y=0; int t=0; @Override public void start(Stage primaryStage) { gc = Canvas.getGraphicsContext2D(); ・ ・ ・

と追加し,末尾に再描画を行おうと,

while(true) { /* * ここでxとy座標を再計算したとして見てください */ gc.setFill(Color.RED); gc.fillRect(x, y, 10, 10); //計算した座標に従い描画 t += 1; //空白時間を設ける try{ Thread.sleep(30); }catch(InterruptedException e){} }

とwhile文を追加したのですが
java.lang.reflect.InvocationTargetException
というエラーがでてしまいました.

知識が不十分で申し訳ないのですが,正しいCanvasへの再描画処理の方法をご教授頂ければ幸いです.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

対処についてはmasaya_ohashiさんの回答のとおりと思いますが、若干の指摘を含めコメントします

問題1: InvocationTargetException

アプリケーションクラス(Main)上に@FXML Canvas Canvas;と記述しておられますが、@FXMLアノテーションはコントロールクラスに書くべきものなので、それ以外のクラスに書いても無効(nullのまま)です。本件でいえばstartメソッドの中のgc = Canvas.getGraphicsContext2D();を実行するとNPEが発生します。NPEでなくInvocationTargetExceptionとなったのは、startメソッドがリフレクション(Method#invoke)によって呼び出されているからです。スタックトレースをよく見れば前述のgc = ...の行でNPEが発生していることが確認できますよ。

問題2: sleepや無限ループは禁止

JavaFX(やswingやその他多くのGUIシステム)ではランタイムの中に「イベントディスパッチループ」が実装されており、そこから「イベントハンドラー」が起動されます。startはイベントハンドラーではありませんが、守らなければならない原則は同様であり「sleepしたり、無限ループで何かを繰り返す」ことは禁止です。そういうことをすると、ユーザー操作によるイベントのハンドラーが即座に起動されなくなるため、「反応が悪い」あるいは「フリーズ」といった問題が起きます。自前で独自のスレッドを起動すればsleepや無限ループ的な実装も可能ですが、正しく実装することは難易度が高いため、普通は止した方がよいと思います。

対処

基本は「ある時間間隔で自動的に起動されるハンドラー」によって周期処理を実現することです。このためにAnimationクラスが提供されており、Timelineもその派生クラスの一つです。なおアプリケーションクラス(Main)ではなく、コントローラークラス(FXTest2Controller)でTimelineを用いるべきという点に注意してください。さもないと問題1と同様のことが起きかねません。

投稿2017/10/12 16:07

KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

3_pyon

2017/10/13 08:27

どこが間違いか,丁寧に説明して下さりありがとうございます! 納得できました!
guest

0

ベストアンサー

JavaFXでは固定の間隔ごとに呼ばれる処理を作るにはTimelineが向いています。

Java

1 public void initialize(URL location, ResourceBundle resources) { 2 Timeline timeline = new Timeline( 3 new KeyFrame( 4 Duration.millis(30)), // 間隔は30ミリ秒とする 5 (event) -> { // この処理が30ミリ秒ごとに呼ばれる 6 gc = Canvas.getGraphicsContext2D(); 7 draw(x, y); 8 }) 9 ); 10 timeline.setCycleCount(Timeline.INDEFINITE); // 無限に繰り返す 11 timeline.start(); // 開始 12 }

投稿2017/10/12 09:52

masaya_ohashi

総合スコア9206

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

3_pyon

2017/10/13 08:27

お早いコード付きの回答ありがとうございました! 無事実装できました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問