web-dev-qa-db-ja.com

javaFXでキャンバスをサイズ変更可能にするにはどうすればよいですか?

キャンバスのサイズを変更するjavaFXには、そのような方法はありません。唯一の解決策は、Canvasから拡張することです。

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);

    }

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

キャンバスから拡張されたものは、キャンバスをサイズ変更可能にする唯一のソリューションですか?このソリューションはFXMLを使用したくない場合にのみ機能するため、fxmlでキャンバスを宣言する場合、どのようにサイズ変更可能にすることができますか?

これは私のコードです:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.AnchorPane;
import javafx.scene.Paint.Color;
import javafx.stage.Stage;

public class Main extends Application {

    Controller controller;

    @Override
    public void start(Stage primaryStage) throws Exception{
        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
        AnchorPane root = loader.load(); // controller initialized
        controller = loader.getController();
        GraphicsContext gc = controller.canvas.getGraphicsContext2D();
        gc.setFill(Color.AQUA);
        gc.fillRect(0, 0, root.getPrefWidth(), root.getPrefHeight());
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(controller.Pane, controller.Pane.getPrefWidth(), controller.Pane.getPrefHeight()));
        primaryStage.show();
    }

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

サイズ変更可能なキャンバスを設定するのに役立つと思うガイドがあります。

JavaFxのヒント-サイズ変更可能なキャンバス

ガイドからのコードの一部:

/**
 * 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();
        }
    }
6
Maslor

http://werner.yellowcouch.org/log/resizable-javafx-canvas/ から取得:JavaFxキャンバスのサイズを変更できるようにするには、min/pref/maxメソッドをオーバーライドするだけです。サイズを変更可能にし、サイズ変更メソッドを実装します

この方法では、再描画をトリガーするために幅/高さのリスナーは必要ありません。また、幅と高さのサイズをコンテナにバインドする必要もなくなりました。

public class ResizableCanvas extends Canvas {

@Override
public double minHeight(double width)
{
    return 64;
}

@Override
public double maxHeight(double width)
{
    return 1000;
}

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

@Override
public double minWidth(double height)
{
    return 0;
}

@Override
public double maxWidth(double height)
{
    return 10000;
}

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

@Override
public void resize(double width, double height)
{
    super.setWidth(width);
    super.setHeight(height);
    <Paint>
}
5
user458577

与えられたすべての答えの中で、キャンバスを親と一緒に自動的にサイズ変更するという点で、実際にはどれもうまくいきませんでした。私はこれにひびを入れることに決めました、そしてこれは私が思いついたものです:

public class ResizableCanvas extends Canvas {

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

    @Override
    public double maxHeight(double width) {
        return Double.POSITIVE_INFINITY;
    }

    @Override
    public double maxWidth(double height) {
        return Double.POSITIVE_INFINITY;
    }

    @Override
    public double minWidth(double height) {
        return 1D;
    }

    @Override
    public double minHeight(double width) {
        return 1D;
    }

    @Override
    public void resize(double width, double height) {
        this.setWidth(width);
        this.setHeight(height)
    }
}

これは実際にキャンバスを本当にサイズ変更可能にした唯一のものでした。


このアプローチを採用する理由は次のとおりです。

  • 親コンポーネントにコンストラクターでwidthheightを送信するように強制することでカプセル化を破りたくありませんでした。これは、キャンバスをFXMLで使用できないことも意味します。 。
  • また、親のwidthプロパティとheightプロパティに依存したくなかったため、すべてのスペースを占有することで、キャンバスを親の唯一の子にしました。
  • 最後に、キャンバスは別のクラスで描画を行う必要がありました。つまり、ここに投稿された回答の1つを使用できませんでした。これには、drawメソッドによるキャンバスへの描画も含まれていました。

このキャンバスでは、キャンバスのサイズを変更するために、親のwidth/heightプロパティにバインドする必要はありません。親が選択したサイズに合わせてサイズを変更するだけです。さらに、キャンバスを使用している人は誰でも、幅/高さのプロパティにバインドして、これらのプロパティが変更されたときに自分の描画を管理できます。

3
smac89

上記の解決策は、キャンバスがHBoxに含まれている場合は機能しないことがわかりました。これは、ウィンドウのサイズが変更されてもHBoxが縮小せず、キャンバスがクリップされるためです。したがって、HBoxは拡張されますが、これ以上小さくなることはありません。次のコードを使用して、キャンバスをコンテナに合わせました。

public class ResizableCanvas extends Canvas {
    @Override
    public double prefWidth(double height) {
        return 0;
    }

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

そして私のコントローラークラスでは:

@FXML
private HBox canvasContainer;
private Canvas canvas = new ResizableCanvas();
...
@Override
public void start(Stage primaryStage) throws Exception {
    ...
    canvas.widthProperty().bind(canvasContainer.widthProperty());
    canvas.heightProperty().bind(canvasContainer.
    pane.getChildren().add(canvas);
    ...
}
2
aalleexxx5

キャンバスクラスはisResizable()をオーバーライドする必要があります(他の例で提案されている他のすべては実際には必要ありません):

public class ResizableCanvas extends Canvas
{
  public boolean isResizable()
  {
      return true;
  }
}

また、アプリケーションでは、キャンバスの幅と高さのプロパティをキャンバスの親にバインドする必要があります。

@Override
public void start(Stage primaryStage) throws Exception
{
    ...
    StackPane pane = new StackPane();
    ResizableCanvas canvas = new ResizableCanvas(width, height);

    canvas.widthProperty().bind(pane.widthProperty());
    canvas.heightProperty().bind(pane.heightProperty());

    pane.getChildren().add(_canvas);
    ...
}

サイズ変更時にキャンバスを再描画するために、heightプロパティのwidthにリスナーを追加できます(ただし、必要な場合と配置する場所は、アプリケーションによって異なります)。

widthProperty().addListener(this::Paint);
heightProperty().addListener(this::Paint);
1
sys64738

このソリューションは、FXMLを使用したくない場合にのみ機能します

FXMLの有無にかかわらず、サイズ変更可能なキャンバス自体のメリットについてはよくわかりません。通常、何かを再描画したいのですが、キャンバス(それ自体にはコンテンツがありません)はありませんが、質問iselfのように、アプリケーション固有のコードに戻り、周りのほとんどの回答にいくつかが含まれていますre/draw()メソッド。
次に、別のクラスを破棄し、FXMLで4つのバインディングを実行できます。

_<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="test.TestController">
  <children>
    <Pane fx:id="pane" VBox.vgrow="ALWAYS">
      <children>
        <Canvas fx:id="canvas" height="${pane.height}" width="${pane.width}"
                onWidthChange="#redraw" onHeightChange="#redraw" />
      </children>
    </Pane>
  </children>
</VBox>
_

javaではredraw()のみを実装します。

_package test;

import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;

public class TestController {
  @FXML
  private Canvas canvas;

  @FXML
  private void redraw() {
    double w=canvas.getWidth();
    double h=canvas.getHeight();
    GraphicsContext gc=canvas.getGraphicsContext2D();
    gc.clearRect(0, 0, w, h);
    gc.beginPath();
    gc.rect(10, 10, w-20, h-20);
    gc.stroke();
  }
}
_

(必要に応じて、適切なメインクラスとモジュール情報を https://stackoverflow.com/a/58915071/7916438 で見つけてください)

0
tevemadar