将ImageView大小绑定到AnchorPane的大小始终为0

时间:2016-02-17 17:35:35

标签: javafx fxml

我在ImageView内有AnchorPane,使用FXML构建。

<fx:root prefHeight="600.0" prefWidth="800.0" type="AnchorPane" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.LifeMandelbrot">
  <children>
    <ImageView fx:id="view" fitHeight="600.0" fitWidth="800.0" onMouseClicked="#moveCenter" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
    <HBox alignment="CENTER" spacing="10.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
      <children>
        <Button fx:id="zoomOut" mnemonicParsing="false" onAction="#zoomOut" text="Zoom-" />
        <Button fx:id="zoomIn" mnemonicParsing="false" onAction="#zoomIn" text="Zoom+" />
        <Button fx:id="defaultView" mnemonicParsing="false" onAction="#defaultView" text="Vue par défaut" />
      </children>
    </HBox>
  </children>
</fx:root>

如您所见,ImageView符合AnchorPane使用锚点。

当我点击其中一个按钮时,ImageView会重新粉刷。

问题: view.getFitWidth()始终显示0,高度相同。

修改

控制器代码如下所示:

package application;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;

public class LifeMandelbrot extends AnchorPane implements Initializable {
    private static final double DEFAULT_ZOOM = 200.0;
    private static final Complex DEFAULT_CENTER = new Complex(0, 0);
    private static final double ZOOM_RATIO = 1.2;
    @FXML
    private Button zoomOut;

    @FXML
    private Button zoomIn;

    @FXML
    private Button defaultView;

    @FXML
    private Button julia;

    @FXML
    ImageView view;

    private double zoom;
    private Complex center;
    private List<Color> colors;
    private int colorStep;

    public LifeMandelbrot() {
    zoom = DEFAULT_ZOOM;
    center = DEFAULT_CENTER;
    colors = new ArrayList<Color>();
    colors.add(Color.RED);
    colors.add(Color.GREEN);
    colors.add(Color.BLUE);
    colorStep = 20;
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
    repaint();
    view.fitWidthProperty().bind(widthProperty());
    view.fitHeightProperty().bind(heightProperty());
    }

    @FXML
    void defaultView(ActionEvent event) {
    zoom = DEFAULT_ZOOM;
    center = DEFAULT_CENTER;
    repaint();
    }

    @FXML
    void julia(ActionEvent event) {
    }

    @FXML
    void zoomIn(ActionEvent event) {
    zoom *= ZOOM_RATIO;
    repaint();
    }

    @FXML
    void zoomOut(ActionEvent event) {
    zoom /= ZOOM_RATIO;
    repaint();
    }

    @FXML
    void moveCenter(MouseEvent event) {
    center = fractalFromView(event.getX(), event.getY());
    repaint();
    }

    private void repaint() {
    WritableImage image = new WritableImage((int) view.getFitWidth(), (int) view.getFitHeight());
    PixelWriter pw = image.getPixelWriter();
    for (int x = 0; x < image.getWidth(); x++) {
        for (int y = 0; y < image.getHeight(); y++) {
        Complex c = fractalFromView(x, y);
        int iterations = Fractal.mandelbrot(c);
        if (iterations == -1) {
            pw.setColor(x, y, new Color(0, 0, 0, 1));
        } else {
            int colorIndex = iterations / colorStep;
            int colorAdd = iterations % colorStep;
            Color color1 = colors.get(colorIndex % colors.size());
            Color color2 = colors.get((colorIndex + 1) % colors.size());
            Color color = color1.interpolate(color2, (double) colorAdd / colorStep);
            pw.setColor(x, y, color);
        }
        }
    }
    view.setImage(image);
    }

    private Complex fractalFromView(double x, double y) {
    return new Complex((x - view.getFitWidth() / 2) / zoom + center.getReal(),
        (y - view.getFitHeight() / 2) / zoom + center.getImaginary());
    }
}

从那里加载:

package application;

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

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
    try {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("../lifeMandelbrot.fxml"));
        loader.setRoot(new LifeMandelbrot());
        AnchorPane root = loader.load();
        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.setTitle("LIFE Is a Fractal Explorer");
        primaryStage.show();
    } catch (Exception e) {
        e.printStackTrace();
    }
    }

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

1 个答案:

答案 0 :(得分:1)

您有两个LifeMandelbrot实例:您通过调用构造函数创建的实例,并明确设置为FXML的动态根;另一个是由FXMLLoader为您创建的,用作控制器。您创建的在场景图中使用的那个(scene = new Scene(root))。创建为控制器的那个永远不会放在场景图中。因此它永远不会经历布局,并且宽度和高度始终为零。

当然,处理程序方法和initialize()方法在&#34;控制器实例&#34;上调用,而不是&#34;根实例&#34;,因此你绑定fitWidth并且fitHeight为零。

你需要

FXMLLoader loader = new FXMLLoader(getClass().getResource("../lifeMandelbrot.fxml"));
LifeMandelbrot root = new LifeMandelbrot();
loader.setRoot(root);
loader.setController(root);
loader.load();
Scene scene = new Scene(root);

然后您需要从FXML的根元素中删除fx:controller属性。这样控制器和根节点就是同一个实例。

由于您的FXML已经通过使用子节点上的锚窗格设置明确地将其自身绑定到AnchorPane,因此为此使用标准模式可能更清楚。即

<AnchorPane fx:id="root" fx:controller="application.LifeMandelbrot" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
  <children>
    <ImageView fx:id="view" onMouseClicked="#moveCenter" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
    <HBox alignment="CENTER" spacing="10.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
      <children>
        <Button fx:id="zoomOut" mnemonicParsing="false" onAction="#zoomOut" text="Zoom-" />
        <Button fx:id="zoomIn" mnemonicParsing="false" onAction="#zoomIn" text="Zoom+" />
        <Button fx:id="defaultView" mnemonicParsing="false" onAction="#defaultView" text="Vue par défaut" />
      </children>
    </HBox>
  </children>
</AnchorPane>

在控制器中:

public class LifeMandelbrot implements Initializable {

    @FXML
    private AnchorPane root ;

    // existing code...

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        repaint();
        view.fitWidthProperty().bind(root.widthProperty());
        view.fitHeightProperty().bind(root.heightProperty());
    }    

    // existing code...
}

然后只是

FXMLLoader loader = new FXMLLoader(getClass().getResource("../lifeMandelbrot.fxml"));
Scene scene = new Scene(loader.load());