JavaFX:将从根视图触发的FileChooser中提取的图像传递到嵌套视图中的ImageView对象

问题描述:

我是JavaFX的新手,请原谅Noobishness。JavaFX:将从根视图触发的FileChooser中提取的图像传递到嵌套视图中的ImageView对象

所以我正在制作图片浏览器。这是我第一次用MVC模式设计GUI,所以我不太确定自己在做什么。

我正在试图完成:

这只是一个正常的图片浏览器。就像您想要查看图片时打开的标准Windows程序一样。

我所做的:

所以我现在有2次:

  1. 的Root.fxml观点,这是只是一个BorderPane一个菜单栏顶部 与菜单项“打开”这触发FileChooser并将MainApp.java currentImage变量设置为用户选择的任何值。

  2. 嵌套在(root.setCenter)里面,我有ImageViewer.fxml视图, 这是一个AnchorPane,它只包含一个ImageView。

我100%肯定我的FXML没有问题,并且所有控制器和变量都被正确绑定。这在MainApp或控制器中必定是个问题。

我不知道如何将图像从RootViewController传递到ImageViewerController。当我尝试在ImageViewerController初始化程序中初始化mainApp.currentImage的侦听器时,我得到一个NullPointerException。如果我把它放在setMainApp方法中,它什么也不做。这里是我的代码:

验证码:

MainApp.java

import sample.model.MyImage; 
import samlple.view.ImageViewerController; 
import sample.view.RootController; 
import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Scene; 
import javafx.scene.layout.AnchorPane; 
import javafx.scene.layout.BorderPane; 
import javafx.stage.FileChooser; 
import javafx.stage.Stage; 
import java.io.File; 
import java.io.IOException; 

public class MainApp extends Application { 
    final static File img = new File("C:\\test\\1.png"); // test initial image loading 
    private Stage primaryStage; 
    private BorderPane rootLayout; 
    private MyImage currentImage = new MyImage(img); 

    public MainApp() { 
    } 

    public MyImage getCurrentImage(){ 
     return this.currentImage; 
    } 
    public void setCurrentImage(MyImage myImage){ 
     currentImage = myImage; 
    } 

    public void start(Stage primaryStage) throws Exception{ 
     this.primaryStage = primaryStage; 
     this.primaryStage.setTitle("Hi"); 

     initRootLayout(); 
     showImageViewer(); 
    } 



    /** 
    * Initializes the root layout. 
    */ 
    public void initRootLayout() { 
     try { 
      // Load root layout from fxml file. 
      FXMLLoader loader = new FXMLLoader(); 
      loader.setLocation(MainApp.class.getResource("view/Root.fxml")); 
      rootLayout = loader.load(); 

      // Show the scene containing the root layout. 
      Scene scene = new Scene(rootLayout); 
      primaryStage.setScene(scene); 

      // Giving the controller access to the main app. 
      RootController controller = loader.getController(); 
      controller.setMainApp(this); 

      primaryStage.show(); 

     } catch (IOException e) { 
      e.printStackTrace(); 
      System.err.println("Root layout loading error."); 
     } 
    } 

    /** 
    * Shows the ImageViewer inside the root layout. 
    */ 
    public void showImageViewer() { 
     try { 
      // Load ImageViewer. 
      FXMLLoader loader = new FXMLLoader(); 
      loader.setLocation(MainApp.class.getResource("view/ImageViewer.fxml")); 
      AnchorPane imageViewer = loader.load(); 

      // Set the ImageViewer into the center of root layout. 
      rootLayout.setCenter(imageViewer); 

      // Giving the controller access to the main app. 
      ImageViewerController controller = loader.getController(); 
      controller.setMainApp(this); 

     } catch (IOException e) { 
      e.printStackTrace(); 
      System.err.println("ImageViewer layout loading error."); 
     } 
    } 

    /** 
    * Called when user clicks the "Open" menu item 
    */ 

    public void menuOpenImageFile() { 
     FileChooser fileChooser = new FileChooser(); 

     //Set extension filter 
     FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("Image types (*.jpg; *.gif; *.bmp; *.png)", "*.jpg", "*.png", "*.bmp", "*.gif"); 
     fileChooser.getExtensionFilters().add(extFilter); 

     //Show save file dialog 
     File imageFile = fileChooser.showOpenDialog(primaryStage); 
     MyImage myImageToSetAsCurrent = new MyImage(imageFile); 
     setCurrentImage(myImageToSetAsCurrent); 
    } 

    /** 
    * Returns the main stage. 
    * @return 
    */ 
    public Stage getPrimaryStage() { 
     return primaryStage; 
    } 

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

MyImage.java(模型图像类来保存属性):

package sample.model; 


import javafx.beans.property.ObjectProperty; 
import javafx.beans.property.SimpleObjectProperty; 
import javafx.scene.image.Image; 

import java.io.File; 

public class MyImage { 
    private final ObjectProperty<File> imageFile; 
    private final ObjectProperty<Image> image; 

    public MyImage(){ 
     this(null); 
    } 

    public MyImage(File imageFile) { 
     this.imageFile = new SimpleObjectProperty<>(imageFile); 
     this.image = new SimpleObjectProperty<>(new Image("file:" + imageFile.toString())); 
    } 

    public File getImageFile() { 
     return imageFile.get(); 
    } 

    public ObjectProperty<File> imageFileProperty() { 
     return imageFile; 
    } 

    public void setImageFile(File myImageFile) { 
     this.imageFile.set(myImageFile); 
    } 

    public Image getImage() { 
     return image.get(); 
    } 

    public ObjectProperty<Image> imageProperty() { 
     return image; 
    } 

    public void setImage(Image myImage) { 
     this.image.set(myImage); 
    } 
} 

RootController.java

import samlple.MainApp; 
import javafx.fxml.FXML; 
import javafx.scene.control.MenuItem; 


public class RootController { 
    @FXML 
    private MenuItem open; 

    // Reference to the main application. 
    private MainApp mainApp; 

    /** 
    * The constructor. 
    * The constructor is called before the initialize() method. 
    */ 
    public RootController() { 
    } 

    /** 
    * Initializes the controller class. This method is automatically called 
    * after the fxml file has been loaded. 
    */ 
    @FXML 
    private void initialize() { 
    } 

    @FXML 
    private void openImageFile() { 
     mainApp.menuOpenImageFile(); 
    } 

    /** 
    * Is called by the main application to give a reference back to itself. 
    * 
    */ 
    public void setMainApp(MainApp mainApp) { 
     this.mainApp = mainApp; 

    } 
} 

ImageViewerController.java

import sample.MainApp; 
import javafx.fxml.FXML; 
import javafx.scene.image.Image; 
import javafx.scene.image.ImageView; 


public class ImageViewerController { 
    @FXML 
    private ImageView imageView; 

    // Reference to the main application. 
    private MainApp mainApp; 

    /** 
    * The constructor. 
    * The constructor is called before the initialize() method. 
    */ 
    public ImageViewerController() { 
    } 

    /** 
    * Initializes the controller class. This method is automatically called 
    * after the fxml file has been loaded. 
    */ 
    @FXML 
    private void initialize() { 
     mainApp.getCurrentImage().imageProperty().addListener(((observable, oldValue, newValue) -> imageView.setImage(newValue))); //This keeps throwing NullPointerException! 
    } 

    /** 
    * Is called by the main application to give a reference back to itself. 
    * 
    */ 
    public void setMainApp(MainApp mainApp) { 
     this.mainApp = mainApp; 

     imageView.setImage(mainApp.getCurrentImage().imageProperty().get()); 
    } 
} 

当初始化应有尽有,ImageViewController是观察的主要应用程序的当前MyImage对象的imageProperty。当从菜单中选择新图像时,在主应用程序中设置了一个新的MyImage对象:但ImageViewController仍在观察原始MyImage对象中的imagePropertyimageProperty永不改变。

你可能想使currentImage最后,只是更改属性:

public class MainApp extends Application { 
    final static File img = new File("C:\\test\\1.png"); // test initial image loading 
    private Stage primaryStage; 
    private BorderPane rootLayout; 
    private final MyImage currentImage = new MyImage(img); 

    public MainApp() { 
    } 

    public MyImage getCurrentImage(){ 
     return this.currentImage; 
    } 
    // public void setCurrentImage(MyImage myImage){ 
    //  currentImage = myImage; 
    // } 

    // ... 

    public void menuOpenImageFile() { 
     FileChooser fileChooser = new FileChooser(); 

     //Set extension filter 
     FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("Image types (*.jpg; *.gif; *.bmp; *.png)", "*.jpg", "*.png", "*.bmp", "*.gif"); 
     fileChooser.getExtensionFilters().add(extFilter); 

     //Show save file dialog 
     File imageFile = fileChooser.showOpenDialog(primaryStage); 
     currentImage.setImageFile(imageFile); 
     currentImage.setImage(new Image(imageFile.toURI().toString())); 
    } 

} 

注意您有另一个错误:你在你的控制器,才把它必然叫的initialize()方法调用mainApp.getCurrentImage()一个机会来设置主应用程序。您应该在setMainApp(...)方法中注册听众:

import sample.MainApp; 
import javafx.fxml.FXML; 
import javafx.scene.image.Image; 
import javafx.scene.image.ImageView; 


public class ImageViewerController { 
    @FXML 
    private ImageView imageView; 

    // Reference to the main application. 
    private MainApp mainApp; 


    /** 
    * Is called by the main application to give a reference back to itself. 
    * 
    */ 
    public void setMainApp(MainApp mainApp) { 
     this.mainApp = mainApp; 

     imageView.setImage(mainApp.getCurrentImage().imageProperty().get()); 

     mainApp.getCurrentImage().imageProperty().addListener(((observable, oldValue, newValue) -> imageView.setImage(newValue))); 
    } 
}