Java FXグラフィックスとアニメーション入門(JJUG CCC 2015 Spring G-7)

Post on 17-Jul-2015

1083 Views

Category:

Software

8 Downloads

Preview:

Click to see full reader

Transcript

JavaFXグラフィックスとアニメーション入門

デスクトップにアナログ時計を表示しよう

高橋 徹(Java読書会BOF)

アジェンダ

目的

自己紹介

JavaFX駆け足紹介

アナログ時計の作り方

時計を描画するグラフィックス

3種類(画像、Java API、SVG)

針の動きをアニメーション

本セッションの目的

JavaFXに興味あるが「敷居が高い」と感じている人

JavaFXを使っているがグラフィックスやアニメーションを使ってない人

JavaFXプログラムの超基本構造

簡単な例としてアナログ時計を作成

を対象に

を紹介します

自己紹介

高橋 徹

Java読書会BOF代表http://www.javareading.com/bof/

日本JavaFXユーザーグループ参加https://groups.google.com/forum/#!forum/javafx-ja

ブログ等

ブログ http://d.hatena.ne.jp/torutk/

Twitter @boochnich

JavaFX駆け足紹介

Java標準搭載GUIライブラリ

種類 搭載年 搭載版(Oracle JDK)

備考

AWT 1995年 JDK Alpha/Beta

Swing 1998年 JDK 1.2

JavaFX2011年 JDK 7u2 同梱形態

2014年 JDK 8

JavaFX駆け足紹介 JavaFXの提供機能

ボタン、ラベル、グラフ等のUI部品

2Dグラフィックス(ベクトル、画像)

3Dグラフィックス(ポリゴン)

エフェクト(特殊効果)

アニメーション

サウンド

動画

Web表示

「Java SE Development Kit 8u40 Demos and Samples」に含まれるEnsemble を動かしてみると一通り確認できます。

JavaFX駆け足紹介

JavaFXの構成要素

1. Javaソースコード

JavaFX APIを呼ぶコードを記述

2. FXML(XML)

レイアウト・属性を記述

3. CSS

見栄えの記述

JavaFX駆け足紹介 JavaFXプログラムの配布形態

1. Javaデスクトップアプリケーション javaコマンド

JARファイル

2. ブラウザーから起動(Web Start)リンクからダウンロードおよび実行

3. Webページ内で実行Webページを開くと実行(Applet)

4. ネイティブバンドル EXE、MSI、RPM、DMG

JavaFX駆け足紹介

Hello worldを作成

ステップ1

空のウィンドウを表示

ステップ2

ウィンドウ内に文字列を表示

JavaFX駆け足紹介

Hello worldを作成(ステップ1)

package hello;

import javafx.application.Application;import javafx.stage.Stage;

public class HelloJavaFx extends Application {

@Overridepublic void start(Stage stage) {

stage.show();}

}

JavaFX駆け足紹介

Hello worldを作成(ステップ1)

package hello;

import javafx.application.Application;import javafx.stage.Stage;

public class HelloJavaFx extends Application {

@Overridepublic void start(Stage stage) {

stage.show();}

}

使うクラスは・Application・Stageの2つ

JavaFX駆け足紹介

Hello worldを作成(ステップ1)

package hello;

import javafx.application.Application;import javafx.stage.Stage;

public class HelloJavaFx extends Application {

@Overridepublic void start(Stage stage) {

stage.show();}

}

Applicationクラスは、JavaFXアプリケーションの制御を司る。

Application抽象クラスをサブクラス化し、抽象メソッドstartをオーバーライドする

JavaFX駆け足紹介

Hello worldを作成(ステップ1)

package hello;

import javafx.application.Application;import javafx.stage.Stage;

public class HelloJavaFx extends Application {

@Overridepublic void start(Stage stage) {

stage.show();}

}

Stageクラスは、トップレベルウィンドウを表す。

startメソッドで受け取るStageインスタンスはデフォルト非表示であり、showメソッドを呼んで可視とする。

JavaFX駆け足紹介

Hello worldを作成(ステップ1)

package hello;

import javafx.application.Application;import javafx.stage.Stage;

public class HelloJavaFx extends Application {

@Overridepublic void start(Stage stage) {

stage.show();}

}

mainメソッドがなくてもOK

JavaFX駆け足紹介

Hello world(ステップ1)の実演

JavaFX駆け足紹介

Hello worldを作成(ステップ2)

シーングラフの構築

Stage Scene

Node Node…

Node Node…

シーングラフと呼ぶツリー構造

Parent

Node

JavaFX駆け足紹介

Stage Scene Parent

Node

Hello worldを作成(ステップ2)

シーングラフの構築

今回構築するシーングラフ

JavaFX駆け足紹介

Stage Scene Group

Label

Hello worldを作成(ステップ2)

シーングラフの構築

今回構築するシーングラフ

JavaFX駆け足紹介

Stage Scene Group

Label

Hello worldを作成(ステップ2)

シーングラフの構築

Group root = new Group();Label hello = new Label(...);root.getChildren().add(hello);

Scene scene = new Scene(root);stage.setScene(scene);

JavaFX駆け足紹介

Stage Scene Group

Label

Hello worldを作成(ステップ2)

シーングラフの構築

Group root = new Group();Label hello = new Label(...);root.getChildren().add(hello);

Scene scene = new Scene(root);stage.setScene(scene);

JavaFX駆け足紹介

Stage Scene Group

Label

Hello worldを作成(ステップ2)

シーングラフの構築

Group root = new Group();Label hello = new Label(...);root.getChildren().add(hello);

Scene scene = new Scene(root);stage.setScene(scene);

JavaFX駆け足紹介

Stage Group

Label

Hello worldを作成(ステップ2)

シーングラフの構築

Group root = new Group();Label hello = new Label(...);root.getChildren().add(hello);

Scene scene = new Scene(root);stage.setScene(scene);

Scene

JavaFX駆け足紹介

Hello worldを作成(ステップ2)

ソースコード(1/2)package com.torutk.hellofx;

import javafx.application.Application;import javafx.stage.Stage;import javafx.scene.Group;import javafx.scene.Scene;import javafx.scene.control.Label;

JavaFX駆け足紹介

Hello worldを作成(ステップ2)

ソースコード(2/2)

public class HelloJavaFx extends Application {@Overridepublic void start(Stage stage) {

Group root = new Group();Label helloLabel = new Label("Hello, JavaFX world");root.getChildren().add(helloLabel);

Scene scene = new Scene(root);stage.setScene(scene);stage.show();

}}

JavaFX駆け足紹介

Hello world(ステップ2)の実演

アナログ時計の作り方

きっかけ

Windows 7のガジェット機能でアナログ時計を表示

Windows 8でガジェットが廃止

ならば、自分で作ってみよう!

アナログ時計の作り方

アナログ時計の要素

時計を描画する

針を時間の経過に応じて動かす

アナログ時計の作り方 アナログ時計の要素

時計を描画する

時計盤

短針 長針

秒針

中心

アナログ時計の作り方

アナログ時計の要素

針を時間の経過に応じて動かす

短針: 12時間で1回転

長針: 60分間で1回転

秒針: 1分間(60秒間)で1回転

アナログ時計の作り方 時計の描画

画像を重ねて描画

JavaのAPI(2Dグラフィックス)で描画

SVGデータで描画

アナログ時計の作り方

画像を重ねて描画

時計盤: clockDial.png

短針 : clockHourHand.png

長針 : clockMinuteHand.png

秒針 : clockSecondsHand.png

中心 : clockCenterPoint.png

アナログ時計の作り方

画像を重ねて描画

シーングラフの構築

Stage Scene

Node Node…

Node Node…

シーングラフ

Parent

Node

アナログ時計の作り方

画像を重ねて描画

シーングラフの構築

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

ソースコード

public void start(Stage stage) {StackPane root = new StackPane();ImageView clockDial = new ImageView(new Image("file:clockDial.png"));ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);stage.setScene(scene);stage.show();

}

アナログ時計の作り方

画像を重ねて描画

ソースコード

public void start(Stage stage) {

StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

アナログ時計の作り方

画像を重ねて描画

public void start(Stage stage) {StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

public void start(Stage stage) {StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

public void start(Stage stage) {

StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

public void start(Stage stage) {StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

public void start(Stage stage) {StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

public void start(Stage stage) {StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

public void start(Stage stage) {StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

public void start(Stage stage) {StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

ImageView(時計盤)

StackPane

ImageView(短針)

ImageView(長針)

ImageView(秒針)

ImageView(中心)

アナログ時計の作り方

画像を重ねて描画

ソースコード

public void start(Stage stage) {

StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);stage.setScene(scene);stage.show();

}

アナログ時計の作り方

画像を重ねて描画

ソースコード

public void start(Stage stage) {

StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);stage.setScene(scene);stage.show();

}

Scene StackPane

アナログ時計の作り方

画像を重ねて描画

ソースコード

public void start(Stage stage) {

StackPane root = new StackPane();

ImageView clockDial = new ImageView(new Image("file:clockDial.png"));

ImageView hourHand = new ImageView(new Image("file:clockHourHand.png"));

ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png"));

ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png"));

ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png"));

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondsHand, centerPoint

);

Scene scene = new Scene(root);

stage.setScene(scene);stage.show();

}

Stage Scene

アナログ時計の作り方

画像を重ねて描画の実演

アナログ時計の作り方 針を時間の経過に応じて動かす

短針: 12時間で1回転

長針: 60分間で1回転

秒針: 1分間(60秒間)で1回転

アニメーション

javafx.animation.RotateTransition

Nodeをお手軽にアニメーションする便利クラスTransitionのひとつで、回転を扱う

Transitionでは物足りないアニメーションを作成するには、Timelineを使う

アナログ時計の作り方

針を動かすアニメーション

針の回転を表すRotateTransitionを生成

private RotateTransition createRotateTransition(Duration duration, Node node, double startAngle

) {RotateTransition rt =

new RotateTransition(duration, node);rt.setFromAngle(startAngle);rt.setByAngle(360);rt.setCycleCount(Animation.INDEFINITE);rt.setInterpolator(Interpolator.LINEAR);return rt;

}

アナログ時計の作り方

針を動かすアニメーション

針の回転を表すRotateTransitionを生成

private RotateTransition createRotateTransition(Duration duration, Node node, double startAngle

) {RotateTransition rt =

new RotateTransition(duration, node);rt.setFromAngle(startAngle);rt.setByAngle(360);rt.setCycleCount(Animation.INDEFINITE);rt.setInterpolator(Interpolator.LINEAR);return rt;

}

・1回転に要する時間(duration)、・回転するNode・回転開始角度(startAngle)を指定し、RotateTransitionを生成するcreateRotateTransitionメソッド

アナログ時計の作り方

針を動かすアニメーション

針の回転を表すRotateTransitionを生成

private RotateTransition createRotateTransition(Duration duration, Node node, double startAngle

) {RotateTransition rt =

new RotateTransition(duration, node);rt.setFromAngle(startAngle);rt.setByAngle(360);rt.setCycleCount(Animation.INDEFINITE);rt.setInterpolator(Interpolator.LINEAR);return rt;

}

1回転に要する時間と回転するNodeを指定し、RotateTransitionをインスタンス化

アナログ時計の作り方

針を動かすアニメーション

針の回転を表すRotateTransitionを生成

private RotateTransition createRotateTransition(Duration duration, Node node, double startAngle

) {RotateTransition rt =

new RotateTransition(duration, node);rt.setFromAngle(startAngle);rt.setByAngle(360);rt.setCycleCount(Animation.INDEFINITE);rt.setInterpolator(Interpolator.LINEAR);return rt;

}

RotateTransitionインスタンスに回転開始角度を設定

アナログ時計の作り方

針を動かすアニメーション

針の回転を表すRotateTransitionを生成

private RotateTransition createRotateTransition(Duration duration, Node node, double startAngle

) {RotateTransition rt =

new RotateTransition(duration, node);rt.setFromAngle(startAngle);rt.setByAngle(360);rt.setCycleCount(Animation.INDEFINITE);rt.setInterpolator(Interpolator.LINEAR);return rt;

}

RotateTransitionインスタンスに回転角度を設定(開始角度からの増分)

アナログ時計の作り方

針を動かすアニメーション

private RotateTransition createRotateTransition(Duration duration, Node node, double startAngle

) {RotateTransition rt =

new RotateTransition(duration, node);rt.setFromAngle(startAngle);rt.setByAngle(360);rt.setCycleCount(Animation.INDEFINITE);rt.setInterpolator(Interpolator.LINEAR);return rt;

}

開始角度(fromAngle)

回転角度(byAngle)

アナログ時計の作り方

針を動かすアニメーション

針の回転を表すRotateTransitionを生成

private RotateTransition createRotateTransition(Duration duration, Node node, double startAngle

) {RotateTransition rt =

new RotateTransition(duration, node);rt.setFromAngle(startAngle);rt.setByAngle(360);rt.setCycleCount(Animation.INDEFINITE);rt.setInterpolator(Interpolator.LINEAR);return rt;

}

RotateTransitionインスタンスに繰り返し回数を設定(INDEFINITE は無限)

アナログ時計の作り方

針を動かすアニメーション

針の回転を表すRotateTransitionを生成

private RotateTransition createRotateTransition(Duration duration, Node node, double startAngle

) {RotateTransition rt =

new RotateTransition(duration, node);rt.setFromAngle(startAngle);rt.setByAngle(360);rt.setCycleCount(Animation.INDEFINITE);rt.setInterpolator(Interpolator.LINEAR);return rt;

}

RotateTransitionインスタンスにアニメーション加減速を設定(LINEAR は等速)

アナログ時計の作り方

針を動かすアニメーション

秒針:1分間(60秒間)で1回転

createRotateTransitionメソッドを呼ぶ

RotateTransition secondsHandTransition = createRotateTransition(

Duration.seconds(60),secondsHand,getAngleOfSeconds(LocalTime.now())

);secondsHandTransition.play();

private static double getAngleOfSeconds(LocalTime time) {return time.getSecond() * 360 / 60;

}

アナログ時計の作り方

針を動かすアニメーション

秒針:1分間(60秒間)で1回転

createRotateTransitionメソッドを呼ぶ

RotateTransition secondsHandTransition = createRotateTransition(

Duration.seconds(60),secondsHand,getAngleOfSeconds(LocalTime.now())

);secondsHandTransition.play();

private static double getAngleOfSeconds(LocalTime time) {return time.getSecond() * 360 / 60;

}

1回転の期間をjavafx.util.Duration で指定

アナログ時計の作り方

針を動かすアニメーション

秒針:1分間(60秒間)で1回転

createRotateTransitionメソッドを呼ぶ

RotateTransition secondsHandTransition = createRotateTransition(

Duration.seconds(60),secondsHand,getAngleOfSeconds(LocalTime.now())

);secondsHandTransition.play();

private static double getAngleOfSeconds(LocalTime time) {return time.getSecond() * 360 / 60;

}

回転するNodeを指定(秒針のImageView)

アナログ時計の作り方

針を動かすアニメーション

秒針:1分間(60秒間)で1回転

createRotateTransitionメソッドを呼ぶ

RotateTransition secondsHandTransition = createRotateTransition(

Duration.seconds(60),secondsHand,getAngleOfSeconds(LocalTime.now())

);secondsHandTransition.play();

private static double getAngleOfSeconds(LocalTime time) {return time.getSecond() * 360 / 60;

}

現在時刻から秒針の回転開始角度を計算

アナログ時計の作り方

針を動かすアニメーション

秒針:1分間(60秒間)で1回転

createRotateTransitionメソッドを呼ぶ

RotateTransition secondsHandTransition = createRotateTransition(

Duration.seconds(60),secondsHand,getAngleOfSeconds(LocalTime.now())

);secondsHandTransition.play();

private static double getAngleOfSeconds(LocalTime time) {return time.getSecond() * 360 / 60;

}

アニメーションの開始

アナログ時計の作り方

針を動かすアニメーション

長針:60分間で1回転

createRotateTransitionメソッドを呼ぶ

RotateTransition minuteTransition = createRotateTransition(

Duration.minutes(60),minuteHand,getAngleOfMinute(LocalTime.now())

);minuteTransition.play();

private static double getAngleOfMinute(LocalTime time) {return (time.getMinute() + time.getSecond() / 60d) * 360 / 60;

}

1回転は60分間

現在時刻から短針の回転開始角度を計算

短針のImageView

アナログ時計の作り方

針を動かすアニメーション

短針:12時間で1回転

createRotateTransitionメソッドを呼ぶ

RotateTransition hourTranslation = createRotateTransition(

Duration.hours(12),hourHand,getAngleOfHour(LocalTime.now())

);hourTranslation.play();

private static double getAngleOfHour(LocalTime time) {return (time.getHour() % 12 + time.getMinute() / 60d +

time.getSecond() / (60d * 60d)) * 360 / 12;

}

1回転は12時間

現在時刻から長針の回転開始角度を計算

長針のImageView

アナログ時計の作り方

画像を重ねて描画+針の回転の実演

アナログ時計の作り方 アナログ時計の描画方法

画像を重ねて描画

JavaのAPI(2Dグラフィックス)で描画

SVGデータで描画

アナログ時計の作り方

JavaのAPIで描画

時計盤: Circle, Line, RadialGradient, Rotate

短針 :

長針 :

秒針 : Line

中心 : Circle

Path, MoveTo, LineTo

アナログ時計の作り方

JavaのAPIで描画

シーングラフの構築

Stage Scene

Node Node…

Node Node…

シーングラフ

Parent

Node

アナログ時計の作り方

JavaのAPIで描画

シーングラフの構築

Stage Scene

Pane(時計盤)

StackPane

Pane(短針)

Pane(長針)

Pane(秒針)

Pane(中心)

アナログ時計の作り方

JavaのAPIで描画

public void start(Stage stage) {StackPane root = new StackPane();Node clockDial = createClockDial();Node hourHand = createHourHand();Node minuteHand = createMinuteHand();Node secondHand = createSecondHand();Node centerPoint = createCenter();

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondHand, centerPoint

);

Scene scene = new Scene(root);stage.setScene(scene);stage.show();

}

アナログ時計の作り方

JavaのAPIで描画

シーングラフの構築

Stage Scene

Pane(時計盤)

StackPane

Pane(短針)

Pane(長針)

Pane(秒針)

Pane(中心)

アナログ時計の作り方

JavaのAPIで描画

シーングラフの構築

Circle

Pane(時計盤)

Group

Line Line Line・・・

60個のライン

アナログ時計の作り方

JavaのAPIで描画

時計盤(Circle)

Node createCircle() {RadialGradient gradient = new RadialGradient(

0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE,new Stop(0.8, Color.WHITE),new Stop(0.9, Color.BLACK),new Stop(0.95, Color.WHITE),new Stop(1.0, Color.BLACK)

);Circle circle = new Circle(

100, 100, 100, gradient);return circle;

}

(0,0)

(200,200)

(100,100)

アナログ時計の作り方

JavaのAPIで描画

時計盤(Line)

Node createTickMark(int n) {Line line;if (n % 5 == 0) {

line = new Line(100, 100 * 0.12, 100, 100 * 0.2);} else {

line = new Line(100, 100 * 0.15, 100, 100 * 0.16);}line.getTransforms().add(new Rotate(360 / 60 * n, 100, 100));line.setStrokeWidth(2);return line;

}60個で1周する回転

アナログ時計の作り方

JavaのAPIで描画

時計盤(Group)

Node createTickMarks() {Group tickMarkGroup = new Group();List<Node> tickMarks = IntStream.range(0, 60)

.mapToObj(this::createTickMark)

.collect(toList());tickMarkGroup.getChildren().addAll(tickMarks);return tickMarkGroup;

}

アナログ時計の作り方

JavaのAPIで描画

短針/長針共通の描画Node createHourOrMinuteHand(

double stretchRelativeToRim, Color color) {

Path path = new Path(new MoveTo(100, 100),new LineTo(100 * 0.9, 100 * 0.9),new LineTo(100, stretchRelativeToRim),new LineTo(100 * 1.1, 100 * 0.9),new LineTo(100, 100)

);path.setFill(color);path.setStroke(Color.TRANSPARENT);return path;

}

① ②②

④①

塗りつぶし

アナログ時計の作り方

JavaのAPIで描画

秒針

Node createSecondHand() {Pane pane = new Pane();pane.setPrefSize(100 * 2, 100 * 2);Line line = new Line(100, 100 * 1.1, 100, 100 * 0.2);pane.getChildren().add(line);return pane;

}

アナログ時計の作り方

JavaのAPIで描画の実演

アナログ時計の作り方

針を動かすアニメーション

画像を重ねて描画におけるアニメーションと実装は同一

アナログ時計の作り方 アナログ時計の描画方法

画像を重ねて描画

JavaのAPI(2Dグラフィックス)で描画

SVGデータで描画

アナログ時計の作り方

SVGデータについてScalable Vector Graphics

javafx.scene.shape.SVGPath

SVG 1.1仕様書(2版)の8章 Paths

日本語訳http://www.hcn.zaq.ne.jp/___/SVG11-2nd/paths.html

アナログ時計の作り方

SVGパスデータについて

MoveTo, LineTo

“M 100 100 L 90 90”

円弧

“A 100 100 0 1 1 99 0”

MoveTo(100, 100); LineTo(90, 90)

Circle(100, 100, 100) に近似

アナログ時計の作り方

SVGパスデータについて

円を定義するコマンドがない

“A 100 100 0 1 1 99 0”

Circle(100, 100, 100) に近似

アナログ時計の作り方

SVGデータで描画

public void start(Stage stage) {StackPane root = new StackPane();Node clockDial = createClockDial();Node hourHand = createHourHand();Node minuteHand = createMinuteHand();Node secondHand = createSecondHand();Node centerPoint = createCenter();

root.getChildren().addAll(clockDial, hourHand, minuteHand, secondHand, centerPoint

);

Scene scene = new Scene(root);stage.setScene(scene);stage.show();

}

アナログ時計の作り方

SVGデータで描画

時計盤(枠)

Node createCircle() {RadialGradient gradient = new RadialGradient(

0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE,new Stop(0.8, Color.WHITE),new Stop(0.9, Color.BLACK),new Stop(0.95, Color.WHITE),new Stop(1.0, Color.BLACK)

);SVGPath svg = new SVGPath();svg.setContent(

“M 100 0 A 100 100 0 1 1 99 0”);svg.setFill(gradient);return circle;

}

(0,0)

(200,200)

(100,100)

JavaのAPIで描画から変更した部分

アナログ時計の作り方

SVGデータで描画

時計盤(分刻み)

Node createTickMarks() {SVGPath svg = new SVG();svg.setContent(

“M 100,12 L 100,20 M 108.9,15.5 ...”);svg.setStroke(Color.BLACK);svg.setStrokeWidth(2);return svg;

} 刻み1つ毎に M nn,nn L nn,nn を記述

アナログ時計の作り方

SVGデータで描画

短針の描画

Node createHourHand() {SVGPath svg = new SVGPath();svg.setContent(“M 100,100 L 90,90, L 100,40 L 110,90 Z”);Pane pane = new Pane(svg);pane.setPrefSize(100*2, 100*2);return pane;

}

アナログ時計の作り方

SVGデータで描画

長針の描画

Node createMinuteHand() {SVGPath svg = new SVGPath();svg.setContent(“M 100,100 L 90,90, L 100,20 L 110,90 Z”);Pane pane = new Pane(svg);pane.setPrefSize(100*2, 100*2);return pane;

}

アナログ時計の作り方

SVGデータで描画

秒針の描画

Node createSecondHand() {SVGPath svg = new SVGPath();svg.setContent(“M 100,110 L 100,20”);svg.setStroke(Color.GLAY);Pane pane = new Pane(svg);pane.setPrefSize(100 * 2, 100 * 2);return pane;

}

アナログ時計の作り方

SVGデータで描画の実演

アナログ時計の作り方

針を動かすアニメーション

画像を重ねて描画におけるアニメーションと実装は同一

アナログ時計の作り方

ウィンドウ枠の非表示

public void start(Stage stage) {:Scene scene = new Scene(root, Color.TRANSPARENT);stage.initStyle(StageStyle.TRANSPARENT);:

}

最後に(1/2) JavaFXの最小限のプログラムとして

Hello worldを紹介

アナログ時計を題材に時計のグラフィックス表示

3種類(画像を重ねる、Java API、SVG)

時計の針の回転をアニメーション

デモのコードは次からダウンロードhttps://github.com/torutk/jjugccc2015spring-javafx

最後に(2/2)

本セッションの元ネタhttp://www.torutk.com/projects/swe/wiki/JavaFX

から、「JavaFXとアナログ時計」のリンクへ

Timelineを使った回転のアニメーション

SceneBuilderでSVGPathやRadialGradientの取り扱い

マウスドラッグによる時計の移動

マウスホイール、ピンチ操作での時計の拡大縮小

ポップアップメニュー

Q&A

JavaFXアニメーション(補足) JavaFX関連のスレッド

JavaFX Applicationスレッド 入力、pulse、ユーザータスクに応じて

シーングラフを更新 renderコマンド作成

QuantumRenderスレッド renderコマンドを実行し描画

JavaFX pulse アニメーション中は、16msタイマーで発生(最大

60FPS)

(参考)JavaOne SF 2014 CON2262 “Be in Control of Your JavaFX Mission”

JavaFXアニメーション(補足) JavaFX pulse

JavaFX

Render

Timer

アニメーション処理、シーングラフ更新、renderコマンド作成

render実行、描画

16msタイマー

JavaFXアニメーション(補足)

JavaFX pulseをFlight Recorderで調査

JavaFXにmain不要

javafx.application.Applicationを継承したクラスを指定してjavaコマンドで起動するとき、mainメソッドがなくてもよい

「JavaFXアプリケーションクラスにmainメソッドがなくてもよい訳」

http://d.hatena.ne.jp/torutk/20150402/p1

ラスター v.s. ベクター

拡大縮小への対応

拡大時の表示具合

画像(ラスター)で実現 SVG(ベクター)で実現

JavaFXの主な特徴 Java API FXMLとGUIデザインツール(Scene Builder) WebView(WebKitベース) Swing相互互換性 組込みUI部品とCSS テーマ(Modena) 3Dグラフィックス Canvas API 印刷API リッチテキスト マルチタッチ Hi-DPI ハードウェアアクセラレートグラフィックス メディアエンジン JRE内蔵アプリケーション配布

top related