ハイパーニートプログラマーへの道

頑張ったり頑張らなかったり

JavaFXでサイズ変更可能なcanvasを作る

resizableなcanvasJavaFXで作ろうよ、というお話。
JavaFX Tip 1: Resizable Canvas | Java Code Geeks

上記記事を参考に
Javaは8です。やることは以下。

  • Canvasのサブクラスを作る
  • isResizable()メソッドをオーバーライド
  • prefWidth(),prefHeight()メソッドをオーバーライド
  • 画面サイズが変わったときに再描画する(線のサイズも変わる)listenerをCanvasのwidth,heightプロパティに追加
  • 親ペインのwidth,heightプロパティにCanvasのwidth,heightプロパティをバインド

Tip1ResizableCanvas.java

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 * Tip 1: A canvas resizing itself to the size of
 *        the parent pane.
 */
public class Tip1ResizableCanvas extends Application {

	class ResizableCanvas extends Canvas {

		public ResizableCanvas() {
			// Redraw canvas when size changes.
			widthProperty().addListener(evt -> draw());
			heightProperty().addListener(evt -> draw());
		}

		private void draw() {
			double width = getWidth();
			double height = getHeight();

			GraphicsContext gc = getGraphicsContext2D();
			gc.clearRect(0, 0, width, height);

			gc.setStroke(Color.RED);
			gc.strokeLine(0, 0, width, height);
			gc.strokeLine(0, height, width, 0);
		}

		@Override
		public boolean isResizable() {
			return true;
		}

		@Override
		public double prefWidth(double height) {
			return getWidth();
		}

		@Override
		public double prefHeight(double width) {
			return getHeight();
		}
	}

	@Override
	public void start(Stage stage) throws Exception {
		ResizableCanvas canvas = new ResizableCanvas();

		StackPane stackPane = new StackPane();
		stackPane.getChildren().add(canvas);

		// Bind canvas size to stack pane size.
		canvas.widthProperty().bind(
		               stackPane.widthProperty());
		canvas.heightProperty().bind(
		               stackPane.heightProperty());

		stage.setScene(new Scene(stackPane));
		stage.setTitle("Tip 1: Resizable Canvas");
		stage.show();
	}

	public static void main(String[] args) {
		launch(args);
	}
}

f:id:noriyo_tcp:20140427191621p:plain
対角線上に線が引かれていますが

f:id:noriyo_tcp:20140427191628p:plain
ウィンドウサイズを横にのばすと、それに合わせて線もビヨーン。

f:id:noriyo_tcp:20140427191634p:plain
縦にのばしても同様に。


以下、メモしたことをそのまま。
何書いてるのかよくわからんですが・・・。まだまだ調べものしなきゃなんない。


何やらjavafxをいろいろインポートしていく
application.Application
scene.Scene
scene.canvas.Canvas
scene.canvas.GraphicsContext
scene.layout.StackPane
scene.paint.Color
stage.Stage

Applicationを拡張してTip1ResizableCanvasを作る。
その中でCanvasを拡張してResizableCanvasを作る。
そのコンストラクタの内容は
サイズが変わったら再描画する
widthProperty(),heightProperty()に(の?)addListener methodを使う。これで再描画のトリガーセット。
evt -> draw()がわからん。draw()をevtが指しているの?(*要はJava8がわかっとらん)

んで、draw()の中身は
getWidth(),getHeight()にそれぞれwidth,heightをアサイン。
GraphicsContextクラス型のgcをgetGraphicsContext2D()にアサイン。インスタンス化。
.clearRect()で四角を作る?(0, 0, width, height) width,heightにgetWidth,getHeightで取得した値が入る。これで画面のサイズを変更できるのか。
で線を描く。
setStroke(色はレッド)、strokeLineで(0,0)から(width, height)まで。つまり対角線ですな。
そしてそれとは逆の対角線を書く。(0, height)左下の角から、(width,0)右上の角まで

isResizableをオーバーライド trueを返す。これでサイズ変更おkにしている?

prefWidth,prefHeightをオーバーライド。ここで引数はそれぞれheight,widthになっている。つまり反対だ。戻り値がgetWidth,getHeightと、引数とは逆?になっている。それぞれのメソッドでwidth,heightをゲットしている、というのは一目見ればわかるが、引数が逆とはこれいかに。

startをオーバーライド(Application classの抽象メソッド) 例外を投げるようにしてある。
Resizable classのオブジェクト作成(canvas)
StackPaneクラス型でstackPaneというオブジェクト作成
.getChildren()のさらに?.addで引数canvas つまりStackPaneにcanvasを追加している

canvasのサイズをstack paneのサイズにバインド、合わせている。
canvas.widthProperty().bind(stackPane.widthProperty());
heightも同様に
これによってstack paneのサイズが変われば、canvasのサイズも変わる、ということ?

stage.setStage(new Scene(stackPane));
stackPaneを引数としてSceneオブジェクト作成し、それをさらにsetStageの引数とする。これがラップというやつなのか?要確認
あとはsetTitileと.show()

mainでやっていることはargsを引数としてlaunchさせることだけ。これはJavaFXを扱う際には必須のことなのか、よくわからんがそういうことだ。

円で書いてみるとどうなるんだろうねっと。真ん中に円を作ってみるのも良いかもしらん。
これ、デフォの画面サイズはどこで決定されているのだろう?
draw()のgetWidth(),getHeight()の値にwidth,heightがアサインされているので決まっていると思うが。