2015-05-29 152 views
1

我正在使用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

回答

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屬性)