Java FX缩放的画布

问题描述:

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

我为我的FXML使用了SceneBuilder。

更新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来保持其底层纵横比(如其像素宽度和高度所指定的),并且在包围节点的纵横比与Canvas的纵横比不相同的情况下也保持在封闭节点的中心位置。

感谢任何帮助。

+0

Oracle的Ensemble 8示例应用程序包含[可调整大小的画布示例](http:// hg。 openjdk.java.net/openjfx/8u40/rt/file/eb264cdc5828/apps/samples/Ensemble8/src/samples/java/ensemble/samples/graphics2d/canvas/SanFranciscoFireworks.java#l112)。 – jewelsea

基于代码我发现我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属性)