Java FX Scaled Canvas

时间:2015-05-29 15:03:23

标签: canvas javafx scale

我正在使用JavaFX,并希望使用固定像素大小的Canvas,可以缩放以填充窗口,并在调整窗口大小时增长。

我正在使用SceneBuilder作为我的FXML。

2015年5月30日更新。我意识到我已经很好地指出了这个问题。我所追求的是Canvas组件没有调整大小;也就是说,内部栅格尺寸保持不变,而是对其应用缩放和变换,以便填充可用空间。

我的出发点如下:

FXML:

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

<?import javafx.geometry.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.canvas.*?>
<?import javafx.scene.shape.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="scalingcanvas.FXMLDocumentController">
   <center>
      <AnchorPane BorderPane.alignment="CENTER">
         <children>
            <Canvas fx:id="canvas" height="200.0" width="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
         </children>
      </AnchorPane>
   </center>
   <top>
      <Label text="top" BorderPane.alignment="CENTER" />
   </top>
   <bottom>
      <Label text="bottom" BorderPane.alignment="CENTER" />
   </bottom>
   <left>
      <Label text="left" BorderPane.alignment="CENTER" />
   </left>
   <right>
      <Label text="right" BorderPane.alignment="CENTER" />
   </right>
</BorderPane>

Java FX控制器:

package scalingcanvas;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

public class FXMLDocumentController implements Initializable {

    @FXML
    private Canvas canvas;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        System.out.printf("hi\n");
        GraphicsContext g2d = canvas.getGraphicsContext2D();
        double w = canvas.getWidth();
        double h = canvas.getHeight();
        g2d.setFill(Color.ALICEBLUE);
        g2d.fillRect(0, 0, w, h);
        g2d.setStroke(Color.BLUE);
        g2d.strokeOval(0, 0, w, h);
        g2d.strokeLine(0, 0, w, h);
        g2d.strokeLine(0, h, w, 0);
    }    
}

Java主要应用程序:

package scalingcanvas;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class ScalePanel extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

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

我理解为什么现有代码在窗口增长时不会扩展Canvas,但我需要添加什么才能实现这一目标?

此外,我需要缩放的画布来维持其基础宽高比(由像素宽度和高度指定),并且在封闭节点的纵横比与画布的宽高比不同时也保持在封闭节点的中心。 / p>

感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

根据我发现here的代码,我终于找到了这个解决方案:

/*
 * Based on http://gillius.org/blog/2013/02/javafx-window-scaling-on-resize.html
 */
package dsfxcontrols;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;

/**
 * A StackPane that <i>scales</i> its contents to fit (preserving aspect ratio),
 * or fill (scaling independently in X and Y) the available area.
 * <p>
 * Note <code>AutoScalingStackPane</code> applies to the contents a scaling
 * transformation rather than attempting to resize the contents.
 * <p>
 * If the contents is a Canvas with pixel dimension 50 by 50, after scaling the
 * Canvas still will have 50 by 50 pixels and the appearance may be pixelated
 * (this might be desired if the application is interfacing a camera and the
 * Canvas needs to match in size the camera's CCD size).
 * <p>
 * If the content contains FX Controls then these get magnified rather than
 * resized, that is all text and graphics are scaled (this might be desired for
 * Point of Sale full screen applications)
 * <p>
 * <h3>Known Limitations</h3>
 * Rescaling occurs only when the AutoScalingStackPane is resized, it does not
 * occur automatically if and when the content changes size.
 *
 *
 * @author michaelellis
 */
public class AutoScalingStackPane extends StackPane {

    /**
     * Force scale transformation to be recomputed based on the size of this
     * <code>AutoScalingStackPane</code> and the size of the contents.
     */
    public void rescale() {
        if (!getChildren().isEmpty()) {
            getChildren().forEach((c) -> {
                double xScale = getWidth() / c.getBoundsInLocal().getWidth();
                double yScale = getHeight() / c.getBoundsInLocal().getHeight();
                if (autoScale.get() == AutoScale.FILL) {
                    c.setScaleX(xScale);
                    c.setScaleY(yScale);
                } else if (autoScale.get() == AutoScale.FIT) {
                    double scale = Math.min(xScale, yScale);
                    c.setScaleX(scale);
                    c.setScaleY(scale);
                } else {
                    c.setScaleX(1d);
                    c.setScaleY(1d);
                }
            });
        }
    }

    private void init() {
        widthProperty().addListener((b, o, n) -> rescale());
        heightProperty().addListener((b, o, n) -> rescale());
    }

    /**
     * No argument constructor required for Externalizable (need this to work
     * with SceneBuilder).
     */
    public AutoScalingStackPane() {
        super();
        init();
    }

    /**
     * Convenience constructor that takes a content Node.
     *
     * @param content
     */
    public AutoScalingStackPane(Node content) {
        super(content);
        init();
    }

    /**
     * AutoScale scaling options:
     * {@link AutoScale#NONE}, {@link AutoScale#FILL}, {@link AutoScale#FIT}
     */
    public enum AutoScale {

        /**
         * No scaling - revert to behaviour of <code>StackPane</code>.
         */
        NONE,
        /**
         * Independently scaling in x and y so content fills whole region.
         */
        FILL,
        /**
         * Scale preserving content aspect ratio and center in available space.
         */
        FIT
    }

    // AutoScale Property
    private ObjectProperty<AutoScale> autoScale = new SimpleObjectProperty<AutoScale>(this, "autoScale",
            AutoScale.FIT);

    /**
     * AutoScalingStackPane scaling property
     *
     * @return AutoScalingStackPane scaling property
     * @see AutoScale
     */
    public ObjectProperty<AutoScale> autoScaleProperty() {
        return autoScale;
    }

    /**
     * Get AutoScale option
     *
     * @return the AutoScale option
     * @see AutoScale
     */
    public AutoScale getAutoScale() {
        return autoScale.getValue();
    }

    /**
     * Set the AutoScale option
     *
     * @param newAutoScale
     * @see AutoScale
     *
     */
    public void setAutoScale(AutoScale newAutoScale) {
        autoScale.setValue(newAutoScale);
    }
}

AutoScalingStackPane应用缩放变换来缩放其内容以填充或拟合(保留的宽高比)到StackPane大小。我添加了一个AutoScale属性,允许您选择缩放选项(NONE,FIT,SCALE)。

如果你编译成Jar,它可以与SceneBuilder一起使用(和测试)。这是我使用它的fxml:

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

<?import javafx.scene.web.*?>
<?import java.lang.*?>
<?import dsfxcontrols.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.shape.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import scalingcanvas.*?>
<?import javafx.scene.text.*?>
<?language javascript?>

<BorderPane fx:id="root" prefHeight="324.0" prefWidth="318.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">

    <top>
        <Label text="top" BorderPane.alignment="CENTER" />
    </top>
    <bottom>
        <Label fx:id="bottom" text="bottom" BorderPane.alignment="CENTER" />
    </bottom>
    <left>
        <Label text="left" BorderPane.alignment="CENTER" />
    </left>
    <right>
        <Label text="right" BorderPane.alignment="CENTER" />
    </right>
    <center>
        <AutoScalingStackPane minHeight="20.0" minWidth="20.0" style="-fx-border-color: blue; -fx-background-color: aliceblue;" BorderPane.alignment="CENTER">

            <children>
                <Group>
                    <children>
                        <Circle fill="#c891c9b2" radius="100.0" stroke="BLACK" strokeType="INSIDE" />
                        <Circle fill="#ff781f62" layoutX="73.0" layoutY="111.0" radius="79.0" stroke="BLACK" strokeType="INSIDE" />
                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="129.0" layoutX="-22.0" layoutY="-19.0" opacity="0.24" stroke="#f80000" strokeType="INSIDE" strokeWidth="3.0" width="141.0" />
                        <Button layoutX="-86.0" layoutY="150.0" mnemonicParsing="false" onAction="bottom.setText(Date());timeLabel.setText(Date());" text="Hello" />
                  <Label fx:id="timeLabel" alignment="CENTER" layoutX="-86.0" layoutY="-70.0" text="Label" textAlignment="CENTER">
                     <font>
                        <Font name="Brush Script MT Italic" size="12.0" />
                     </font>
                  </Label>
                    </children>
                </Group>
            </children>
        </AutoScalingStackPane>
    </center>
</BorderPane>

由于这只需要很少的代码,我想知道StackPane是否可以直接包含这种缩放功能。它似乎很有用,几乎没有任何API更改(仅包含额外的AutoScale属性)